2016-02-05 9 views
2

У меня есть две величины a & b, которые определяются рекурсией и посредством ссылки на другой список значений x = [x_1, x_2, ... x_N], который будет вводить в программу , Программа будет перебирать все значения х и обновления & б в соответствии с:Стабильное вычисление больших квантов через рекурсию

for n in range(1,N) 
    a[n] = a[n-1] * exp(+x[n]) + b[n-1] * exp(-x[n]) 
    b[n] = b[n-1] * exp(+x[n]) + a[n-1] * exp(-x[n]) 

и исходных значений

a[0] = exp(+x[0]) 
b[0] = exp(-x[0]) 

Значения в х не большие числа (всегда < 10), но N будет в сотнях, и из-за всех экспонент конечные значения & b будут очень большими. Меня волнует, что из-за формы рекурсии, где мы постоянно умножаем экспоненциально большие числа с экспоненциально малыми и добавляя их, эта схема станет довольно численно неустойчивой.

В идеале я бы подсчитал log (a) и log (b), чтобы остановить слишком большие значения. Но из-за схемы рекурсии, что это невозможно, если я не вычислять что-то гораздо грязнее, как

log_a[n] = x[n] + log_a[n-1] + log(1 + exp(-2*x[n] + log_b[n-1]-log_a[n-1])) 

ли численная устойчивость что-то я права быть обеспокоены здесь? И если бы это было похоже на схему, основанную на журналах, чтобы стабилизировать ее?

+0

Ваше выражение messier может быть улучшено с помощью 'log1p (x) == log (1 + x)', который более численно устойчив – Eric

+0

Я предполагаю, что ваши начальные значения используют 'x [0]', а не ' х [п] '? – Eric

+0

Да, это так. Оно должно быть x [0] в начальных условиях. Спасибо за отзыв о log1p too – RGWinston

ответ

1

Мы можем переписать это первое, как:

for n in range(1,N) 
    a[n] = exp(log(a[n-1]) + x[n]) + exp(log(b[n-1]) - x[n]) 
    b[n] = exp(log(b[n-1]) + x[n]) + exp(log(a[n-1]) - x[n])) 

Затем измените наши итерационные переменные:

for n in range(1,N) 
    log_a[n] = log(exp(log_a[n-1] + x[n]) + exp(log_b[n-1] - x[n])) 
    log_b[n] = log(exp(log_b[n-1] + x[n]) + exp(log_a[n-1] - x[n])) 

который может быть вычислен более стабильно, используя np.logaddexp:

for n in range(1,N) 
    log_a[n] = np.logaddexp(log_a[n-1] + x[n], log_b[n-1] - x[n]) 
    log_b[n] = np.logaddexp(log_b[n-1] + x[n], log_a[n-1] - x[n]) 

об осуществлении logaddexp можно увидеть here

+0

Это замечательно, спасибо! Есть ли вообще недостатки в использовании logaddexp, и как вы думаете, что числовая стабильность была бы проблемой с исходным кодом? – RGWinston

+0

@ RGWinston: Боюсь, я не знаю ответа на любой из этих вопросов. – Eric

-1

Насколько я знаю, все (?) Рекурсивные проблемы могут быть решены посредством динамического программирования. Например, последовательность Фибоначчи может быть выражена следующим образом:

def fibo(n): 
    if n == 0: 
     return 0 
    elif n == 1: 
     return 1 
    return fibo(n-1) + fibo(n-2) 

Или, итеративным:

n = 10 
fibo_nums = [0, 1] 
while len(fibo_nums) <= n: 
    fibo_nums.append(fibo_nums[-2] + fibo_nums[-1]) 

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

+0

OP уже выполнил такую ​​распаковку. Сама формула является «рекурсивной», но код в вопросе не является – Eric

+0

@ Эрик ах. Я не видел рекурсии в коде OP и не знаком с проблемным доменом. И поскольку название вопроса говорит о рекурсии, я предположил, что что-то происходит в другом месте в их коде. –