Я внедряю Pythagorean means в PHP, арифметические и геометрические средства - это кусок торта, но у меня очень трудное время придумать надежную реализацию 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/н ясерии 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
.
В то время как обратный результат на CodePad является -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
)
Можно ли обойти эту проблему, не обращаясь к расширениям /bcmath
?
Очень хороший ответ и наблюдения Джеффри, используя произвольную библиотеку точности, выполняет трюк, также округляя до максимальной точности ('round (array_sum ($ arguments), ini_get ('precision'))) возвращает' -0', что также может быть хорошим способом избежать зависимости 'gmp' или' bcmath'. Что касается вашего мета-наблюдения, вы правы. Должен ли я просто фильтровать отрицательные значения или использовать их абсолютное значение? –
@AlixAxel Округление движется по воротам. Он может работать для значений, равных нулю, но в какой-то момент даст неправильный результат для значений, очень близких к 0. Возьмем, например, H (999999, -999998, -999997,999996) '. Результат равен ~ 1e + 18', но округляется до макс. двойная точность даст 0. –
@AlixAxel Как вы обрабатываете отрицательные входы, зависит от ваших требований. Если это чисто для информационных целей, я бы просто предупредил. –