不同二项式系数算法的效率比较

时间:2014-10-03 02:45:15

标签: c algorithm time-complexity

我比较了两种算法来计算二项式系数C(n,k),如下所示:#1来源于要计算的二项式系数的公式定义,#2使用动态规划。

#include <stdio.h>
#include <sys/time.h>

#define min(x, y) (x<y?x:y)
#define NMAX 150    

double binomial_formula(int n, int k) {
  double denominator=1, numerator=1, i;
  for (i = 0; i< k; i++)
    numerator *= (n-i), denominator *= (i+1);
  return numerator/denominator;
}

double binomial_dynamic_pro(int n, int k) {
  double c[NMAX][NMAX];
  int i,j;
  for (i = 0; i <= n; i++) {
    for (j = 0; j <= min(i, k); j++) {
      if (i == j || j == 0)
        c[i][j] = 1;
      else
        c[i][j] = c[i-1][j-1]+c[i-1][j];
    }
  }
  return c[n][k];
}

int main(void) {
  struct timeval s, e;
  int n = 50, k = 30;
  double re = 0;
  printf("now formula calc C(%d, %d)..\n", n, k);
  gettimeofday(&s, NULL);
  re = binomial_formula(n, k);
  gettimeofday(&e, NULL);
  printf("%.0f, use time: %ld'us\n", re,
         1000000*(e.tv_sec-s.tv_sec)+ (e.tv_usec-s.tv_usec));

  printf("now dynamic calc C(%d, %d)..\n", n, k);
  gettimeofday(&s, NULL);
  re = binomial_dynamic_pro(n, k);
  gettimeofday(&e, NULL);
  printf("%.0f, use time: %ld'us\n", re,
         1000000*(e.tv_sec-s.tv_sec)+ (e.tv_usec-s.tv_usec));
  return 0;
}

我使用gcc进行编译,它会像这样运行:

now formula calc C(50, 30)..
47129212243960, use time: 2'us
now dynamic calc C(50, 30)..
47129212243960, use time: 102'us

这些结果对我来说意外。我认为动态编程应该更快,因为它O(nk),但公式的方法应该是O(k^2)并且它使用乘法,它也应该更慢。

那么为什么动态编程版本要慢得多呢?

2 个答案:

答案 0 :(得分:2)

所写的

binomial_formula绝对不是O(k^2)。它只有一个大小为k的循环,使其成为O(k)。您还应该记住,在现代架构中,内存访问的成本使任何单个指令的成本相形见绌,而动态编程解决方案会在内存中读写更多地址。第一个版本可以完全在几个寄存器中计算。

请注意,您可以通过识别C(n,k)== C(n,n-k)来实际改进线性版本:

double binomial_formula(int n, int k) {
  double delominator=1, numerator=1, i;
  if (k > n/2)
      k = n - k;
  for (i = 0; i< k; i++)
    numerator *= (n-i), delominator *= (i+1);
  return numerator / delominator;
}

你应该记住,动态编程只是一种技术而不是银弹。它并没有神奇地使所有算法更快。

答案 1 :(得分:0)

第一个算法

  • 需要线性时间
  • 使用恒定的空间

第二种算法

  • 采取二次时间
  • 使用二次空格

就时间/空间而言,第一种算法更好,但第二种算法也具有为较小值计算答案的优势;它可以用作预处理步骤。

想象一下,您会收到一些n k形式的查询,并要求您为每个查询n choose k。此外,假设查询的数量很大(比如n*n左右)。使用第一个算法需要O(nq) = O(n*n*n),而使用第二个算法需要O(n*n)

所以这一切都取决于你想要做什么。