我正在编写一个模型检查器,它依赖于算法的计算,该系统由以下算法密集使用:
![alt text] [1]
其中q
为double,t
为double,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等。我很好地使用了它们。
答案 0 :(得分:3)
qt^k/k! = e^[log[qt^k/k!]]
log[qt^k/k!] = log[qt^k] - log[k!] // log[k!] ~ klnk - k by stirling
~ k ln(qt) - (k lnk - k)
~ k ln(qt/k) - k
对于小的k值,斯特林近似不准确。
但是,由于您似乎在进行有限的已知范围,因此您可以计算log[k!]
并将其放入数组中,从而避免任何错误。
当然,您可以进一步做出多种变化。
答案 1 :(得分:1)
这不是一个答案(我相信),但这只是一个澄清。如果我误解了某些内容,我会在你发表评论后将其删除。
据我了解,您正在尝试计算n,例如以下总和等于1.
正如你可能看到渐近地接近1,它永远不会等于1。 如果我误解了你的问题,请纠正我。