我在PHP中实现了Pythagorean means,算术和几何平均值是小菜一碟,但我很难找到一个可靠的harmonic mean实现。谐波平均值计算和浮点精度
而且这是在PHP中相当于实施:
function harmonicMeanV1()
{
$result = 0;
$arguments = func_get_args();
foreach ($arguments as $argument)
{
$result += 1/$argument;
}
return func_num_args()/$result;
}
现在,如果任何参数是0
这将抛出一个除以0警告,但由于1/n
与相同ñ-1和pow(0, -1)
优雅地返回INF
常数不引发我可以重写的任何错误以下(它仍然会引发错误,如果没有参数,但让忽略现在):
function harmonicMeanV2()
{
$arguments = func_get_args();
$arguments = array_map('pow', $arguments, array_fill(0, count($arguments), -1));
return count($arguments)/array_sum($arguments);
}
两种实现很好地工作在大多数情况下(例如v1,v2和WolframAlpha),但他们失败了壮观如果满足的1/n 我总和系列是0,我应该警告0拿到另一个部门,但我不...
考虑以下设置:-2, 3, 6
(WolframAlpha说,这是一个复杂的无限):
1/-2 // -0.5
+ 1/3 // 0.33333333333333333333333333333333
+ 1/6 // 0.16666666666666666666666666666667
= 0
但是,我的两个实现都返回-2.7755575615629E-17
作为总和(v1,v2)而不是0
。
虽然键盘上的返回结果为-108086391056890000
我的dev的机器(32位)说,这是-1.0808639105689E+17
,它仍然是不一样的0
或INF
我所期待的。我甚至尝试在返回值上调用is_infinite()
,但它按预期返回为false
。
我还发现了stats_harmonic_mean()
功能那是stats
PECL扩展的一部分,但让我惊喜,我得到了完全相同的马车结果:-1.0808639105689E+17
,如果任何参数为0
,返回0
但没有检查做的该系列的总和,as you can see on line 3585:
3557 /* {{{ proto float stats_harmonic_mean(array a)
3558 Returns the harmonic mean of an array of values */
3559 PHP_FUNCTION(stats_harmonic_mean)
3560 {
3561 zval *arr;
3562 double sum = 0.0;
3563 zval **entry;
3564 HashPosition pos;
3565 int elements_num;
3566
3567 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
3568 return;
3569 }
3570 if ((elements_num = zend_hash_num_elements(Z_ARRVAL_P(arr))) == 0) {
3571 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The array has zero elements");
3572 RETURN_FALSE;
3573 }
3574
3575 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos);
3576 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **)&entry, &pos) == SUCCESS) {
3577 convert_to_double_ex(entry);
3578 if (Z_DVAL_PP(entry) == 0) {
3579 RETURN_LONG(0);
3580 }
3581 sum += 1/Z_DVAL_PP(entry);
3582 zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos);
3583 }
3584
3585 RETURN_DOUBLE(elements_num/sum);
3586 }
3587 /* }}} */
这看起来像一个典型的浮动精确的错误,但我真的不能明白为什么,因为个人计算是相当精确:
Array
(
[0] => -0.5
[1] => 0.33333333333333
[2] => 0.16666666666667
)
是否可以在不恢复到gmp
/bcmath
扩展的情况下解决此问题?
非常好的答案和观察杰弗里,使用任意精度库做的伎俩,也四舍五入到最大精度('圆(array_sum($参数),ini_get('精度'))')返回'-0'这也可能是避免'gmp'或'bcmath'的依赖的好方法。关于你的元观察,你是对的。我应该过滤负值还是使用它们的绝对值? –
@AlixAxel舍入是移动的目标职位。它可能适用于完全为零的值,但在某些时候,对于非常接近0的值会给出错误的结果。以'H(999999,-999998,-999997,999996)'为例。结果在'1e + 18'左右,但四舍五入到最大。双精度将给出0. –
@AlixAxel您如何处理负面投入取决于您的要求。如果纯粹是为了提供信息,那么我只会提出警告。 –