回 帖 发 新 帖 刷新版面

主题:[原创]关于求任意数阶乘的算法

//factorial.h
/********************************************************************
*  文件名:   factorial.h
*  文件描述: 求阶乘
*  创建人:   陈泽丹 ,2006年3月23日       (QQ:82314038) 
*  版本号:   1.0
*  修改记录:
********************************************************************/
/*============================================================
    自定义类型(ElemType) :                        double
    类名:            Factorial
    -    x:            ElemType
    -    next:        Factorial* 
    //执行进位的操作    
    -    carry(Factorial* &c, ElemType xx):        ElemType 
    //显示结果的重载函数
    -    display(Factorial* t):                    void
    //格式化显示结果
    -    format(ElemType xx,int count):            void
    //构造函数防止next越界
    +    Factorial():x(0),next(NULL){}
    //设置类的初始值
    +    set(ElemType xx):                        void
    //执行乘法运算
    +    M(ElemType xx):                            void
    //显示结果
    +    display():                                void
    //显示结果的位数
    +    digit():                                double 
作  者:陈泽丹 2006/3/23
============================================================*/
/* --------------------------------------------------------------------
补充:
1,    以下CARRY与DIGIT的更新需同步,
2,    以下CARRY与DIGIT值越大,效率越高风险越大
3,    以下CARRY与DIGIT值越低,效率越差风险越小
4,    为合理利用空间,本程序采用动态分配的方案,故若阶乘数太大,请注意你的
    内存问题。
(解释:由于进位的堆积,如某数据段一万乘一万,进位后又刚好与下一个本身
就很大数据段的数据堆积一起,于是“突围了”。。。,所以越后面越有数据
段范围崩溃危险,改良方法是每一个数据段“递归”或改“类乘类(而非本例的'类乘
实数')”,不过这就不是此文三言两语说得清的了。另外,即使数据段定位再小也有风
险,另忘了数值小点的数乘大数还是可成大数的。)
----------------------------------------------------------------------*/

#ifndef HEADER_FACTORIAL
#define HEADER_FACTORIAL

#include <iostream.h>
#define        ElemType    double
#define        CARRY        1000        //进位的条件
#define        DIGIT        3            //数据段的位数

class Factorial
{
private:
    ElemType x;
    Factorial* next;        
    ElemType carry(Factorial* &c, ElemType xx);
    void display(Factorial* t);    
    void format(ElemType xx,int count);
public:
    Factorial():x(0),next(NULL){}
    void set(ElemType xx);
    void M(ElemType xx);
    void display();
    double digit();
};

#endif
/*======================================================================*/
//factorial.cpp
//factor类实现部份

#include "factorial.h"

void Factorial::set(ElemType xx)
{
    x=xx;
    Factorial *t=this;
    while (t != NULL)
    {
        if ( t->x > CARRY) 
        { 
            t->x=carry(t->next,t->x); 
        }
        t=t->next;
    }
}

void Factorial::M(ElemType xx)
{
    Factorial *t=this;
    while (t!=NULL)
    {
        t->x*=xx;
        t=t->next;
    }
    t=this;
    while (t != NULL)
    {
        if ( t->x > CARRY) 
        { 
            t->x=carry(t->next,t->x); 
        }
        t=t->next;
    }
}

ElemType Factorial::carry(Factorial* &c, ElemType xx)
{
    if ( c==NULL) 
    { 
        c= new Factorial;
        c->next=NULL;
    }
    int temp=xx/CARRY;
    c->x=c->x+temp;
    return xx-temp*CARRY;
}


void Factorial::display()
{
    display(this);
}
void Factorial::display(Factorial *t)
{
    if (t!=NULL)
    {
        display(t->next);
        if (t->next!=NULL)    { format(t->x,DIGIT); }
        else cout<<t->x;
        if (t != this) cout<<",";
    }
}
void Factorial::format(ElemType xx, int count)
{
    if (count>0)
    {
        int temp=xx;
        format(xx/10,count-1);
        cout<<temp%10;
    }
}
double Factorial::digit()
{
    Factorial *t=this;
    double i=0;
    while(t->next!=NULL)
    {
        i+=1;
        t=t->next;
    }
    int d=0;
    int temp=t->x;
    while(temp)
    {
        d++;
        temp=temp/10;
    }
    return i*DIGIT+d;
}

/*=================================================================*/
//Main.cpp
//主函数实现部份

#include "factorial.h"
#include "iostream.h"

void main()
{

    Factorial c;
    ElemType start, end;
    cout<<"本程序实现连乘效果(阶乘是连乘的特例)"<<endl;
    cout<<"请输入起始数:";
    cin>>start;
    c.set(start);
    cout<<"您输入的的起始数为";
    c.display();
    cout<<endl;
    cout<<"请输入终止数: ";
    cin>>end;
    for (int i=start+1;i<=end; i++) { c.M(i); }
    c.display();
    cout<<endl;
    cout<<"共有"<<c.digit()<<"位"<<endl;

}

回复列表 (共17个回复)

沙发

过奖了....

板凳

三个字:太强了!!!!

3 楼

不好意思,我的程序有漏洞...刚才突然想到的...
已在原程序上做了一个补充说明了.
不过稍大一些的数还是可以解决的.
/* --------------------------------------------------------------------
补充:
1,    CARRY与DIGIT的更新需同步,
2,    CARRY与DIGIT值越大,效率越高风险越大
3,    CARRY与DIGIT值越低,效率越差风险越小
4,    为合理利用空间,本程序采用动态分配的方案,故若阶乘数太大,请注意你的
    内存问题。
(解释:由于进位的堆积,如某数据段一万乘一万,进位后又刚好与下一个本身
就很大数据段的数据堆积一起,于是“突围了”。。。,所以越后面越有数据
段范围崩溃危险,改变方法是每一个数据段“递归”或"类乘类",不过这就不是此文三言两语说得清的了。另外,即使数据段定位再小也有风险,另忘了数值小点的数乘大数
还是可成大数的。)
----------------------------------------------------------------------*/

4 楼

由于我的计算机的内存才320M,动态分配不够,所以超过一万的阶乘就算不了.
我把以上的程序改写成非动态分配的试了下,算两万的阶乘,花了二十九秒左右吧...

5 楼

//factorial.h
/********************************************************************
*  文件名:        factorial.h
*  文件描述:        求阶乘 (静态分配空间方案)
*  创建人:        陈泽丹, 2006年3月25日
*  版本号:        2.0
*  修改记录:
********************************************************************/
/*============================================================
    自定义类型(ElemType) :                    double
    类名:            Factorial
    //        x数组存放实际数据
    -    x[MaxSize]:                            ElemType
    //        y数组标志某单元是否存放数据了
    //        (你可能会问为什么不让x数组包揽这工作?原因是乘运算较常用
    //        到,如果每次运算都检测该单元是否开辟了,效率降了.)
    -    y[MaxSize]:                            int
    //        next表示数据段个数
    -    next:                                int    
    //        执行进位操作
    -    carry(int i, ElemType xx):            ElemType
    //        格式化数据段
    -    format(double xx, int count):        void
    //        构造函数用初始化数值
    Factorial();
    //        输入初始值
    +    set(ElemType xx):                    void
    //        执行乘法
    +    M(ElemType xx):                        void
    //        显示结果
    +    display():                            void
    //        显示结果的位数
    +    digit():                            int

作  者:陈泽丹 2006/3/25
============================================================*/
/* --------------------------------------------------------------------------
补充:
1,    以下CARRY与DIGIT的更新需同步,
2,    以下CARRY与DIGIT值越大,效率越高风险越大
3,    以下CARRY与DIGIT值越低,效率越差风险越小
4,    为了提高效率,本程序采用静态分配的方案,故若阶乘数太大,请调节MaxSize

(解释:由于进位的堆积,如某数据段一万乘一万,进位后又刚好与下一个本身
就很大数据段的数据堆积一起,于是“突围了”。。。,所以越后面越有数据
段范围崩溃危险,改良方法是每一个数据段“递归”或改“类乘类(而非本例的数乘
实数”,不过这就不是此文三言两语说得清的了。另外,即使数据段定位再小也有风
险,别忘了数值再小的数乘一个极极大数还是可成大数的。)
(好了,坏家伙们,由于上面的原因,我不得不修改此程序的贴名:从“求任意数阶
乘的算法”改成“关于求任意数阶乘的算法"。哦,我用了我很少用的丑闻申明。呵呵。
愿大家编程技能提高。“不要说我们一无所有,我们程序员要做天下的主人!” :) 
---------------------------------------------------------------------------*/

#ifndef HEADER_FACTORIAL
#define HEADER_FACTORIAL
#include <stdlib.h>
#include <iostream.h>

#define        ElemType    double
#define        CARRY        1000        //进位的条件
#define        DIGIT        3            //数据段的位数
#define        MaxSize        40000        //数组的大小

class Factorial
{
private:
    ElemType x[MaxSize];
    int         y[MaxSize];
    int next;        
    ElemType carry(int i, ElemType xx);
    void format(double xx, int count);
public:
    Factorial();
    void set(ElemType xx);
    void M(ElemType xx);
    void display();
    int digit();
};

#endif

6 楼

//factor类实现部份
#include "factorial.h"

Factorial::Factorial():next(0)
{
    for (int i=0; i<MaxSize; i++)
    {
        x[i]=0;
        y[i]=0;
    }    
}
void Factorial::set(ElemType xx)
{
    x[0]=xx;
    while ( x[next]>CARRY )
    {
        x[next]=carry(next+1,x[next]);
        next+=1;
    }
}

void Factorial::M(ElemType xx)
{
    int i=0;
    while (i<=next)
    {
        x[i]=x[i]*xx;
        i+=1;
    }
    i=0;
    while (i<=next)
    {
        if ( x[i] > CARRY) 
        { 
            x[i]=carry(i+1,x[i]); 
        }
        i+=1;
    }
}

ElemType Factorial::carry(int i, ElemType xx)
{
    if ( i>=MaxSize)
    {
        cout<<"内存空间用完!"<<endl;
        exit(1);
    }
    if ( y[i]== 0)
    {
        y[i]=1;
        next++;
    }
    int temp=xx/CARRY;        //注意实数除10可未必会等于0哦
    x[i]=x[i]+temp;
    return xx-temp*CARRY;
}


void Factorial::display()
{
    int i=next;
    while( i>=0)
    {
        if ( i != next)    { format(x[i], DIGIT); }
        else cout<<x[i];
        if (i != 0) cout<<",";
        i--;
    }
}
void Factorial::format(double xx, int count)
{
    if (count>0)
    {
        int temp=xx;
        format(xx/10,count-1);
        cout<<temp%10;
    }
}

int Factorial::digit()
{
    int temp=x[next];
    int d=0;
    while(temp)
    {
        d++;
        temp/=10;
    }
    return next*DIGIT+d;
}

7 楼

//Main.cpp
#include "factorial.h"
#include "iostream.h"
void main()
{
    double start, end;
    Factorial c;

    cout<<"本程序实现连乘效果(阶乘是连乘的特例)"<<endl;
    cout<<"请输入连乘的起始数:";
    cin>>start;
    c.set(start);
    cout<<"您输入的连乘的起始数为";
    c.display();
    cout<<endl;
    cout<<"请输入连乘的终止数: ";
    cin>>end;
    for (int i=start+1 ; i<=end; i++)
    { 
        c.M(i);
    }
    c.display();
    cout<<endl;
    cout<<"共有"<<c.digit()<<"位"<<endl;
}

8 楼

这个程序代码虽然简单,但速度不能说是快,阶乘算法的速度的高低,主要在于2个方面。1。乘法运算的速度。2.根据阶乘自身的特点对乘法运算的优化。对于前者,但规模较小时可采取硬乘法(被乘数与乘数对对见面),规模稍大时(大于300位十进制数)采用分治法,再大时采用快速数论变换或者快速傅立叶变换算法。
  我曾深入研究过这个问题,并发布了一个程序阶乘计算器,可在http://down.csdn.net/ycx/C/11233.html(由于csdn数据丢失治保留了前3个版本) 下载,该程序分为1.0, 1.1 ,1.2, 2.0,3.0(大数乘法使用分治法) 等五个版本,其中2.0比1.1快10倍,在计算10万的阶乘时,3.0比2.0块10倍,在PIV2.6计算机,计算10万的阶乘不到3秒),另外,我还发布了用汇编语言计算阶乘的程序,他们的特点是执行文件特别小巧.dos程序仅仅161字节,win32程序仅仅896字节.详情请参阅:http://community.csdn.net/Expert/topic/4154/4154011.xml?temp=.7977869
擂台:用汇编语言计算大数阶乘,程序最小最快者1000分送上 

9 楼

通过 http://down.csdn.net/ycx/C/11233.html 可下载我的阶乘计算器。这个压缩包提供了所有算法的可执行文件和源程序。刚才测试了以下,用1.2版本计算1万/2万的阶乘,需要0.37/1.58秒.

10 楼

[quote]&nbsp;&nbsp;我曾深入研究过这个问题,并发布了一个程序阶乘计算器,可在http://down.csdn.net/ycx/C/11233.html(由于csdn数据丢失治保留了前3个版本)&nbsp;下载,该程序分为1.0,&nbsp;1.1&nbsp;,1.2,&nbsp;2.0,3.0(大数乘法使用分治法)&nbsp;等五个版本[/quote]

不会你自己的电脑上也没有了吧?我手头还有你的3.5, 4.2, 4.5版的可执行程序呢。

我来回复

您尚未登录,请登录后再回复。点此登录或注册