从递归计算到线性计算:C(N)=(2/N)*∑(i: 0->N-1) C(i)+N

C(N)=(2/N)*∑(i: 0->N-1) C(i)+N

其中C(0)=1。

问题来源《数据结构与算法分析》P289  “Eval函数中的递归计算”。

对此递归程序的优化有了一点微小的心得。。

优化过程

原书中的递归函数 O(L^n)

double Eval(int N)
{
    int i;
    double Sum;

    if(N==0)
        return 1.0;
    else
    {
        Sum=0.0;
        for(i=0;i<N;i++)
            Sum+=Eval(i);
        return 2.0*Sum/N+N;
    }

可以看出,用了一个递归结构来计算这个方程。优点:易于描述和理解。缺点:时间消耗极大,呈指数增长。

输入10,20 还好,30就已经需要4秒以上的时间来计算了。。

对此递归结构的剪枝优化,粗略估计为O(nlogn)~O(n^2)

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
double Eval(int n);

int visit[100000];
double Sum[100000],Sam[100000];

int main()
{
    int n;
    while( ~scanf("%d",&n))
    {
        memset(visit,0,sizeof(visit));
        memset(Sam,0,sizeof(Sam));
        memset(Sum,0,sizeof(Sum));
        printf("C[n]=%lf\n",Eval(n));
    }
}

double Eval(int n)
{
    if(visit[n]) return Sam[n];
    if(!n) return 1.0;
    else
    {
        for(int i=0;i<n;i++)
            Sum[n]+=Eval(i);
        Sam[n]=(2.0/n)*Sum[n]+n;
        visit[n]=1;
        return Sam[n];
    }
}
对原程序附加了一点优化。因为原程序对Eval函数的子分支进行了大量的重复运算,所以我把每个运算完成后添加一个标记数组visit,并且对于每个n都用数组存储。
计算时间大大缩短了。输入10000需要1S,20000需要3S左右,30000需要6秒以上。。。
当然,还可以进一步优化,每次循环不清空Sum[]和C[],以加速下一个数的计算。

优化到线性计算 O(n)

#include<stdio.h>
#include<iostream>
using namespace std;

double c[1000000] = {1.0};
double sum[1000000] = {0.0};
double Eval(int n);

int main()
{
    int n;
    while( ~scanf("%d",&n))
    {
        printf("C[n]=%lf\n",Eval(n));
    }
}

//Final 优化后的线性结构 O(n)
double Eval(int n)
{
    for(int i=1;i<=n;i++)
    {
        sum[i] = sum[i-1] + c[i-1];
        c[i] = (2.0/i)*sum[i]+i;
    }
    return c[n];
}

O(n)时间复杂度果然棒棒哒,输入1000000和输入1,肉眼看不出时间差别。应该还能进一步优化,加个判断,计算过的值不用重复运算就好了。

书上给了个关于此的O(n^2)算法,让思考将其优化到O(n),应该达标了吧。。

没看那个O(n^2)算法,就不贴出来了,(在书P290页)。

 ( ̄▽ ̄)╭  ,数学不是很好,如果优化到O(1)的话估计需要推出通项公式吧。。应该能推出来吧。。反正我不能。。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值