Каковы преимущества следующего кода C? - PullRequest
2 голосов
/ 21 июля 2010

Учитывая log (a) и log (b), вернуть log (a + b)

double log_sum(double log_a, double log_b){
    double v;
    if(log_a < log_b){
        v=log_b+log(1+exp(log_a-log_b));
    }
    else{
        v=log_a+log(1+exp(log_b-log_a));
    }
    return v;
}

Я хочу знать, каковы преимущества вышеуказанной функции?

Ответы [ 8 ]

7 голосов
/ 21 июля 2010

Основная (грубая) альтернатива выглядит следующим образом:

v = log(exp(log_a) + exp(log_b));

Имеет три оценки трансцендентных функций.

Показанное вычисление использует только две трансцендентные функции - и должно быть быстрее.

Может также быть более стабильным численно.

5 голосов
/ 21 июля 2010

Компьютеры и журналы не всегда ладят.Как уже упоминалось другими, точность становится реальной проблемой. Этот пост в блоге имеет большое значение для объяснения этого явления.Эта статья о, казалось бы, ненужных библиотечных функциях и о том, почему они на самом деле очень удобны.

Функция log1p вычисляет log (1 + x).Насколько сложно это реализовать?

Существуют всевозможные сумасшедшие правила и преобразования, которые вы можете использовать при работе с журналами / экспонентами.Я предполагаю, что автор использовал некоторые из этих правил, чтобы сделать расчет более точным, более эффективным, или и то, и другое.

3 голосов
/ 21 июля 2010

Другие уже упоминали о потенциальной потере точности, но в этом случае проблема действительно переполнения .Попробуйте это:

double log_a = 100;
double log_b = 1000;
printf("%f\n", log_b+log(1+exp(log_a-log_b)));
printf("%f\n", log_a+log(1+exp(log_b-log_a)));

На типичной платформе первая будет печатать «inf», а вторая - «1000.000000».

2 голосов
/ 21 июля 2010

Не совсем ответ, но возможная подсказка.

Числа, хранящиеся в «форме журнала», можно умножать или делить, просто добавляя или вычитая числа. Например, exp(log(a) + log(b)) совпадает с a * b. Или, используя a = 41, b = 101, это будет exp(3.71357 + 4.61512), что составляет exp(8.32869) или 4140.98930. Очевидно, что точность играет роль, и я укоротил цифры до 5 цифр. 41 * 101 это 4141.

Я не проработал ваш пример кода, и для меня не сразу понятно, почему ваш код работает так, как он работает, но, надеюсь, вышеизложенное поможет вам собрать его воедино.

РЕДАКТИРОВАТЬ: Я набрал некоторые цифры в вашем примере кода. Если a = 41 и b = 101, а log_a = 3.71357 и log_b = 4.61512, то ваш пример кода вычисляет 4.95582, а exp(4.95582) равно 142.0. «Более простой» способ получить тот же результат - log(exp(log_a) + exp(log_b)), но, как уже отмечали другие, этот способ включает в себя три дорогих трансцендентных функции, тогда как ваш пример кода требует только две (плюс тривиальное сравнение).

2 голосов
/ 21 июля 2010

Если вы имеете в виду в отличие от log(exp(log_a) + exp(log_b)), то выгода довольно очевидна;способ, который вы упоминаете, должен рассчитать только один журнал и один опыт, тогда как этот способ должен рассчитать два опытаЭто намного дороже, чем дополнительное сложение / вычитание / если проверка.

0 голосов
/ 21 июля 2010

Если вы спрашиваете о if / else, это во избежание потери точности. Все арифметические операции над числами с плавающей запятой (за исключением умножения на степени 2 и некоторых случаях сложения / вычитания чисел с одним и тем же показателем) уничтожают информацию, и хороший код с плавающей запятой выберет метод с наименьшей потерей точности.

0 голосов
/ 21 июля 2010

Другие опубликовали хорошие ответы на вопрос, зачем вообще это делать. Я задаюсь вопросом о части if / else. Независимо от того, больше log_a или log_b, оба выражения для v должны быть эквивалентны log(a+b). В каждом случае 0 < exp( ... ) <= 1 и log(1+exp( ... )) - это небольшое положительное число. По какой-то причине я не знаю, это должно быть хорошо.

0 голосов
/ 21 июля 2010

Полагаю, вы спрашиваете, почему эта функция лучше, чем непосредственное вычисление log(a+b) путем восстановления a и b, суммирования их и вычисления log():

 log( exp( log_a ) + exp( log_b ) )

В этом случае вам нужно вычислить показатель степени дважды, а в функции, которую вы запрашиваете показатель степени, вычисляется только один раз. Поскольку вычисление показателя занимает довольно много времени, оно может быть быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...