【问题标题】:Calculate binomial coffeficient very reliably非常可靠地计算二项式系数
【发布时间】:2017-06-23 10:19:13
【问题描述】:

在 C++ 中计算二项式系数的最佳方法是什么?我见过一些代码片段,但在我看来,它总是只在某些特定区域可行。我需要一个非常、非常、非常可靠的计算。我用伽玛函数试了一下:

unsigned n=N;
unsigned k=2;
number = tgammal(n + 1) / (tgammal(k + 1) * tgammal(n - k + 1));

但它已经在 n=8, k=2 of 1 时有所不同(并且在 n=30, k=2 时它崩溃了).. 我“只”需要计算至少 n=3000,k=2。

【问题讨论】:

  • 它需要有多准确return 1 非常可靠,只是完全不准确。
  • 您只需要 k=2 的值吗?因为那肯定很容易
  • @MSalters 好问题。我的意思是输出必须是正确的,所以我想因此我的意思是准确的?
  • @alexeykuzmin0 1 +2 在二项式系数的情况下?
  • @HarshitSinghal 是的,仅适用于 k=2。到底有多容易?

标签: c++ math binomial-coefficients gamma-function


【解决方案1】:

这个

constexpr inline size_t binom(size_t n, size_t k) noexcept
{
    return
      (        k> n  )? 0 :          // out of range
      (k==0 || k==n  )? 1 :          // edge
      (k==1 || k==n-1)? n :          // first
      (     k+k < n  )?              // recursive:
      (binom(n-1,k-1) * n)/k :       //  path to k=1   is faster
      (binom(n-1,k) * n)/(n-k);      //  path to k=n-1 is faster
}

需要 O(min{k,n-k}) 操作,可靠且可以在编译时完成。

解释 二项式系数定义为B(n,k)=k!(n-k)!/n!,从中我们可以看到B(n,k)=B(n,n-k)。我们还可以得到递归关系n*B(n,k)=(n-k)*B(n-1,k)=k*B(n-1,k-1)。此外,k=0,1,n,n-1 的结果是微不足道的。

对于k=2,结果也是平凡的(n*(n-1))/2

当然,您也可以使用其他整数类型来做到这一点。如果你需要知道一个超过最大可表示整数类型的二项式系数,你必须使用近似方法:使用double。在这种情况下,最好使用 gamma 函数

#include <cmath>
inline double logbinom(double n, double k) noexcept
{
    return std::lgamma(n+1)-std::lgamma(n-k+1)-std::lgamma(k+1);
}
inline double binom(double n, double k) noexcept
{
    return std::exp(logbinom(n,k));
}

【讨论】:

  • 我知道这已经有一段时间了,但我又遇到了这个解决方案,我想我不明白它是如何工作的或者忘记了它。如何得到公式“(binom(n-1,k-1) * n)/k”?以及如何调用我实际操作的函数?
  • @Ben。我为代数添加了解释。调用是递归的:函数使用不同的参数类型调用自身。递归算法非常频繁并且允许一些优雅。
【解决方案2】:

您可以使用渐近更有效的循环公式:

constexpr inline size_t binom(size_t n, size_t k) noexcept
{
    return
      (        k> n  )? 0 :          // out of range
      (k==0 || k==n  )? 1 :          // edge
      (k==1 || k==n-1)? n :          // first
      binom(n - 1, k - 1) * n / k;   // recursive
}

这将仅使用 O(k) 运算来计算 C(n, k)。

【讨论】:

  • 这段代码看起来与我的答案几乎相同,这是一个奇怪的巧合吗?甚至是cmets?唯一的区别是最后一行使用整数除法,对于大 k 更快。
  • @Walter,唯一的区别是我使用另一个循环公式:C(n, k) = n/k * C(n - 1, k - 1) 而不是C(n, k) = C(n - 1, k - 1) + C(n - 1, k)。我的解决方案在 O(k) 操作中运行,而您的解决方案在 O(nk) 中运行。因此,我提出的解决方案比您的解决方案快约 3000 倍。
  • 确实,我的递归不适用于大型n。我采用了你的方法,但在k&gt;n/2的情况下有所改进。
猜你喜欢
  • 1970-01-01
  • 2021-12-27
  • 2018-11-21
  • 2018-07-05
  • 2019-08-28
  • 1970-01-01
  • 2014-05-22
  • 2018-02-22
  • 1970-01-01
相关资源
最近更新 更多