Я пишу средство проверки моделей, которое опирается на вычисление коэффициента, который интенсивно используется в следующих алгоритмах:
! [Alt text] [1]
где q
является двойным, t
тоже двойным и k
int.e
обозначает экспоненциальную функцию.Этот коэффициент используется в шагах, в которых q
и t
не изменяются, а k
всегда начинается с 0, пока сумма всех предыдущих коэффициентов (этого шага) не достигнет 1.
Мой первыйреализация была буквальной:
let rec fact k =
match k with
0 | 1 -> 1
| n -> n * (fact (k - 1))
let coeff q t k = exp(-. q *. t) *. ((q *. t) ** (float k)) /. float (fact k)
Конечно, это длилось не так долго, так как вычисление всего факториала было просто невозможно, когда k
перешагнул небольшой порог (15-20): очевидно, результаты началисьсходить с ума.Поэтому я переставил все это, выполнив инкрементные деления:
let rec div_by_fact v d =
match d with
1. | 0. -> v
| d -> div_by_fact (v /. d) (d -. 1.)
let coeff q t k = div_by_fact (exp(-. q *. t) *. ((q *. t) ** (float k))) (float k)
Эта версия работает довольно хорошо, когда q
и t
достаточно «нормальные», но когда все становится странным, например, q = 50.0
и t = 100.0
и я начинаю вычислять его с k = 0 to 100
, и я получаю серию из 0, за которыми следуют NaN от определенного числа до конца.
Конечно, это вызвано операциями с числами, которые начинают получатьслишком близко к 0 или аналогичным проблемам.
Есть ли у вас какие-либо идеи относительно того, как я могу оптимизировать формулу, чтобы иметь возможность давать достаточно точные результаты при широком разбросе входных данных?
Все должно бытьуже 64 бит (так как я использую OCaml, который по умолчанию использует удваивается).Может быть, есть способ использовать 128-битные двойные числа, но я не знаю, как.
Я использую OCaml, но вы можете предлагать идеи на любом языке, который вам нужен: C, C ++, Java и т. Д. Iвполне использовал их все.