我比较了两种算法来计算二项式系数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)
并且它使用乘法,它也应该更慢。
那么为什么动态编程版本要慢得多呢?
答案 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)
。
所以这一切都取决于你想要做什么。