【问题标题】:Approximating the lograrithm of binomial coefficients for very large numbers逼近非常大数的二项式系数的对数
【发布时间】:2019-08-28 09:12:40
【问题描述】:

我目前正在努力计算非常大的数字的二项式系数,假设 n

到目前为止,我已经尝试了许多方法来处理这些计算产生的大量数字。但是,问题是我不需要计算这些二项式系数一次而是数十万次。这意味着计算阶乘的常规方法迄今为止过于昂贵,而像long long int 这样的标准数据类型太有限,无法容纳这些数字。

我已经尝试过来自Boost 库的多精度数据类型,但正如我所提到的,计算如此多次会导致性能极慢。我也尝试过使用OpenMP 的多线程,但性能优势仍然太低。结果,我改用计算二项式系数的对数来保持数字很小。虽然这解决了大数的问题,但这并没有加快进程。这就是为什么我尝试了对数二项式系数的斯特林近似。我当前的解决方案如下所示:

#include <math.h>

long double calc_hgeom(unsigned int k, unsigned int n, unsigned int K, unsigned int N)
{
    long double hprob = std::exp((log_C(K, k) + log_C(N-K, n-k)) - log_C(N, n));
    return hprob;
}

long double log_C(unsigned int u, unsigned int m)
{
    long double C = u * std::log(u) - m * std::log(m) - (u-m) * std::log(u-m)) + 0.5 * (std::log(u) - std::log(m) - std::log(u-m) - std::log(2*M_PI));
    return C;
}

但是,结果与实际值相差很大,最高可达 7 %。因此我的问题是:是否有一种有效的方法来计算二项式系数的对数,或者我的近似值是否可以改进以提高准确性?

任何帮助将不胜感激,因为这个计算是我整个算法的基础。

【问题讨论】:

  • 您是否尝试为 en.wikipedia.org/wiki/… 的第三个公式中给出的斯特林公式添加第一(两个)校正项?这与后面的近似部分中的其他公式相比如何?
  • @LutzL:感谢您的快速回复。我一定会测试您提到的其他更正条款并比较准确性。
  • 这很奇怪。查看 Wiki 文章中的相对错误图表,7% 应该通过n=100。所以对于n = 1e7,第一个近似值应该要好得多

标签: c++ c++11 math approximation binomial-coefficients


【解决方案1】:

考虑 R 的 lchoose 函数 ...

> choose(10000, 5000) 
[1] Inf
> lchoose(10000, 5000)
[1] 6926.641

基础 R 语言源代码库是解决此类问题的重要思路。

https://github.com/wch/r-source/blob/trunk/src/nmath/choose.c

这里的技巧是使用 ln 转换的输入来避免溢出。

请注意代码是在 GNU 许可下的。

【讨论】:

    【解决方案2】:

    您应该使用Sterling's approximation formula 表示n! ,它应用于二项式系数,为您提供:

    对于二项式系数本身,对于对数,只需取该等式右侧的对数即可;大多数这些东西很快就会变得简单得多。你仍然有k!但是,对于较大的 k,您将再次需要近似公式。最终你会得到更可行的东西(即在数值上更稳定)。

    如果这还不够好,即如果您仍然有几乎相互抵消的条款,请考虑在其中一个变量上应用 Taylor expansion

    【讨论】:

    • 非常感谢您的回复。事实上,泰勒展开是改进方法的好主意!我明天会试试这个。
    猜你喜欢
    • 2020-01-03
    • 1970-01-01
    • 1970-01-01
    • 2017-10-18
    • 2015-12-09
    • 2010-12-25
    • 2018-04-25
    • 1970-01-01
    • 2018-07-23
    相关资源
    最近更新 更多