【问题标题】:different binomial coefficient algorithm's efficiency compare不同二项式系数算法的效率比较
【发布时间】:2014-10-03 02:45:15
【问题描述】:

我比较了两种计算二项式系数 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),并且它使用乘法,它应该也更慢。

那么为什么动态编程版本会这么慢呢?

【问题讨论】:

  • 我认为应该是O(k) 而不是O(k^2)。实际上,我计算了给定 N 和 K 的系数计算所需的步数,formula method 需要 30 步,Dynamic Programming 需要 1116 步。

标签: c algorithm time-complexity


【解决方案1】:

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;
}

您应该记住,动态编程只是一种技术,而不是灵丹妙药。它不会神奇地使所有算法更快。

【讨论】:

    【解决方案2】:

    第一个算法

    • 需要线性时间
    • 使用固定数量的空间

    第二种算法

    • 采用二次方时间
    • 使用二次方空间

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-27
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 2015-02-24
      • 1970-01-01
      相关资源
      最近更新 更多