【问题标题】:Optimization of C code for hypergeometric distribution超几何分布的 C 代码优化
【发布时间】:2019-10-11 06:15:43
【问题描述】:

我必须优化给定的 C 代码:-

#include<sys/time.h>
#include<stdio.h>
#define GAP(start, end) ((end.tv_usec-start.tv_usec)+(end.tv_sec-start.tv_sec)*1000000)
void main(){
    struct timeval start_time, end_time;
    gettimeofday(&start_time, NULL);
    /* START OF BLOCK */
    long int n, N, e, E, e_choose_i, E_e_choose_N_i, E_choose_N;
    float p_value = 0;
    long int i, var1, var2, var3, var4; // Temporary variables
    fscanf(stdin, "%ld%ld%ld%ld", &n, &N, &e, &E);
    for(i = n; i <= N; i++){
        for(var1 = 1, var4 = 2; var4 <= e; var4++)
            var1 = var1 * var4; // Computes e!
        for(var2 = 1, var4 = 2; var4 <= i; var4++)
            var2 = var2 * var4; // Computes i!
        for(var3 = 1, var4 = 2; var4 <= e-i; var4++)
            var3 = var3 * var4; // Computes (e-i)!
        e_choose_i = var1/(var2*var3); // Computes (e choose i)
        for(var1 = 1, var4 = 2; var4 <= (E-e); var4++)
            var1 = var1 * var4; // Computes (E-e)!
        for(var2 = 1, var4 = 2; var4 <= (N-i); var4++)
            var2 = var2 * var4; // Computes (N-i)!
        for(var3 = 1, var4 = 2; var4 <= ((E-e)-(N-i)); var4++)
            var3 = var3 * var4; // Computes ((E-e)-(N-i))!
        E_e_choose_N_i = var1/(var2*var3); // Computes ((E-e) choose (N-i))
        p_value += e_choose_i*E_e_choose_N_i;
    }
    for(var1 = 1, var4 = 2; var4 <= E; var4++)
        var1 = var1 * var4; // Computes E!
    for(var2 = 1, var4 = 2; var4 <= N; var4++)
        var2 = var2 * var4; // Computes N!
    for(var3 = 1, var4 = 2; var4 <= E-N; var4++)
        var3 = var3 * var4; // Computes (E-N)!
    E_choose_N = var1/(var2*var3); // Computes (E choose N)
    p_value /= E_choose_N;
    /* END OF BLOCK */
    gettimeofday(&end_time, NULL);
    printf("p-value = %f (%d microseconds)", p_value, (int) GAP(start_time, end_time));
}

我已经使用动态规划方法来找到二项式系数并将代码修改为:-

#include<sys/time.h>
#include<stdio.h>
#define GAP(start, end) ((end.tv_usec-start.tv_usec)+(end.tv_sec-start.tv_sec)*1000000)
void main(){
    struct timeval start_time, end_time;
    gettimeofday(&start_time, NULL);
    /* START OF BLOCK */
    long int n, N, e, E, e_choose_i, E_e_choose_N_i, E_choose_N;
    float p_value = 0;
    long int i; //var1, var2, var3, var4; // Temporary variables
    int p,q,r;
    fscanf(stdin, "%ld%ld%ld%ld", &n, &N, &e, &E);
    for(i = n; i <= N; i++){
        long int C[e+1][i+1];
        for(p=0;p<=e;p++)
        {
            r=(p<i)?p:i;
            for(q=0;q<=r;q++)
            {
                if(q==0 || q==p)
                    C[p][q]=1;
                else
                    C[p][q]=C[p-1][q-1]+C[p-1][q];
            }
        } 
        e_choose_i=C[e][i];
        long int C1[E-e+1][N-i+1];
        for(p=0;p<=E-e;p++)
        {
            r= (p<N-i)?p:N-i ;
            for(q=0;q<=r;q++)
            {
                if(q==0 || q==p)
                    C1[p][q]=1;
                else
                    C1[p][q]=C1[p-1][q-1]+C1[p-1][q];
            }
        } 
        E_e_choose_N_i = C1[E-e][N-i]; // Computes ((E-e) choose (N-i))
        p_value += e_choose_i*E_e_choose_N_i;
       }
     long int C2[E+1][N+1];
        for(p=0;p<=E;p++)
        {
            r= (p<N)?p:N ;
            for(q=0;q<=r;q++)
            {
                if(q==0 || q==p)
                    C2[p][q]=1;
                else
                    C2[p][q]=C2[p-1][q-1]+C2[p-1][q];
            }
        }   
    E_choose_N = C2[E][N]; // Computes (E choose N)
    p_value /= E_choose_N;
    /* END OF BLOCK */
    gettimeofday(&end_time, NULL);
    printf("p-value = %f (%d microseconds)", p_value, (int) GAP(start_time, end_time));
}

较小的值例如 n=2,N=3,e=3 和 E=7 花费的时间较少,但对于较大的值例如 n=3,N=8,e=10 和E=15。如何针对大值对其进行优化。请帮忙。

【问题讨论】:

  • 您将 fscanf 包含在您的定时序列中。这将花费大部分时间!对一个函数的多次调用而不是一次调用计时总是一个好主意。
  • 谢谢@dmuir。你能告诉我,有没有其他方法可以减少运行时间,比如为二项式系数或阶乘编写优化代码而没有任何循环或其他东西?为阶乘编写递归代码比其迭代版本花费更多时间。你能帮忙优化给定的代码吗?

标签: c optimization


【解决方案1】:

正如dmuir所指出的那样

您将 fscanf 包含在您的定时序列中。这将花费大部分时间!对一个函数的多次调用而不是一次调用计时总是一个好主意。

除此之外,您可以尝试不同的方法来评估二项式系数:

double binomial(long n, long k)
{
    double result = 1.0;
    if ( k > n/2 )
        k = n - k;
    ++n;
    for (int i = 1; i <= k; ++i)
        result *= (double)(n - i) / i;
    return result;
}

这样计算(如果我得到正确的话)减少到

for(int i = n; i <= N; i++)
{       
    p_value += binomial(e, i) * binomial(E - e, N - i);
}
p_value /= binomial(E, N);

可测试here

【讨论】:

  • 感谢@Bob__ 的回答。我正在使用 gcc 编译器。在终端中,您的代码给出的运行时间为 6379105 微秒,而原始代码(代码中没有任何修改)给出的运行时间为 4675572 微秒,每次我编译它时,它都会显示不同的运行时间。你能告诉我为什么它显示更多的运行时间和不同的运行时间吗?
  • 我的意思是,当我多次编译时,每次它显示不同的运行时间。能说说背后的原因吗?
  • @ankit 井分析不是一项简单的任务。每次执行程序时,可能有不同的进程同时运行,数据可能在同一执行的不同运行时在缓存中或不在缓存中,等等。 Here 我尝试使用一个众所周知的框架来比较不同的实现。你会注意到,如果没有记忆,我的建议与原来的建议相比也是一种悲观化。
  • @ankit 你还在计时中包括 fscanf 吗?在我的(普通)电脑上,计算你给出的值的函数需要几微秒,而不是几秒钟!
  • @ankit 将 gettimeofday 的第一次调用移动到最后一次 scanf 之后。
猜你喜欢
  • 2016-08-01
  • 2012-11-13
  • 1970-01-01
  • 2013-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
相关资源
最近更新 更多