主题:[原创]关于求任意数阶乘的算法
if007
[专家分:650] 发布于 2006-03-24 21:08:00
//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个回复)
沙发
if007 [专家分:650] 发布于 2006-03-24 17:23:00
过奖了....
板凳
zqfsl2008 [专家分:110] 发布于 2006-03-24 18:50:00
三个字:太强了!!!!
3 楼
if007 [专家分:650] 发布于 2006-03-24 20:44:00
不好意思,我的程序有漏洞...刚才突然想到的...
已在原程序上做了一个补充说明了.
不过稍大一些的数还是可以解决的.
/* --------------------------------------------------------------------
补充:
1, CARRY与DIGIT的更新需同步,
2, CARRY与DIGIT值越大,效率越高风险越大
3, CARRY与DIGIT值越低,效率越差风险越小
4, 为合理利用空间,本程序采用动态分配的方案,故若阶乘数太大,请注意你的
内存问题。
(解释:由于进位的堆积,如某数据段一万乘一万,进位后又刚好与下一个本身
就很大数据段的数据堆积一起,于是“突围了”。。。,所以越后面越有数据
段范围崩溃危险,改变方法是每一个数据段“递归”或"类乘类",不过这就不是此文三言两语说得清的了。另外,即使数据段定位再小也有风险,另忘了数值小点的数乘大数
还是可成大数的。)
----------------------------------------------------------------------*/
4 楼
if007 [专家分:650] 发布于 2006-03-25 01:46:00
由于我的计算机的内存才320M,动态分配不够,所以超过一万的阶乘就算不了.
我把以上的程序改写成非动态分配的试了下,算两万的阶乘,花了二十九秒左右吧...
5 楼
if007 [专家分:650] 发布于 2006-03-25 02:44:00
//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 楼
if007 [专家分:650] 发布于 2006-03-25 02:45:00
//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 楼
if007 [专家分:650] 发布于 2006-03-25 02:46:00
//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 楼
liangbch [专家分:1270] 发布于 2006-06-06 20:21:00
这个程序代码虽然简单,但速度不能说是快,阶乘算法的速度的高低,主要在于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 楼
liangbch [专家分:1270] 发布于 2006-06-06 20:32:00
通过 http://down.csdn.net/ycx/C/11233.html 可下载我的阶乘计算器。这个压缩包提供了所有算法的可执行文件和源程序。刚才测试了以下,用1.2版本计算1万/2万的阶乘,需要0.37/1.58秒.
10 楼
boxertony [专家分:23030] 发布于 2006-06-06 22:46:00
[quote] 我曾深入研究过这个问题,并发布了一个程序阶乘计算器,可在http://down.csdn.net/ycx/C/11233.html(由于csdn数据丢失治保留了前3个版本) 下载,该程序分为1.0, 1.1 ,1.2, 2.0,3.0(大数乘法使用分治法) 等五个版本[/quote]
不会你自己的电脑上也没有了吧?我手头还有你的3.5, 4.2, 4.5版的可执行程序呢。
我来回复