最近公司里面的项目一直用c++来进行,基础太薄弱,于是自己完成了公司的任务后,加紧练习。
本来那天是想写一个递归阶乘算法的,发现有不少问题啊:
第一: 递归有一个弊端,就是栈空间不够的问题
第二:遇到大数的时候,在cpp中没有办法找到合适的数据类型来进行计算,用long,double都是不可能够的。因为我一开始的目标至少是要计算到1000的阶乘的(现在想想实在太小)。
下面简单说说实现的思路:
n!,对于任意的0<m<n, 假设计算结果为result,因为result的位数>=n的位数。所以就对每一个result对n进行相乘计算,然后再相加。
1)初始分配空间
假设定义 long* result = (long*)malloc(sizeof(long*n*Q);其中的Q我为了考虑足够的空间,暂时取的是10(这个q的计算我尚未进行,所以在空间上是有浪费的)。用malloc来定义result,是考虑到大数的结果位数比较大,需要存放的空间会比较大,所以放在堆中(题外,对于堆栈存储的理解也不知道对不对,最近公司里面要我写的代码中有一段用了递归,于是公司的同事告诉我,递归可能会导致栈空间不够,最好不要用递归,于是自己上网查了查,大概就这么理解。不知道堆中最多能存放多少的空间呢?是不是内存有多少就能存多少?)。
2)核心算法
for i: 1-n //从1*2*3*4...n
{
int upvalue = 0;
for j: 0-result.size // 假设result是712,那么result[0]=2,result[1]=1,result[2]=7.然后计算下一个result的时候,分别
// 每一位对i相乘,如果>10,再进位
{
int tmpvalue = resultM[j] * i + upvalue; //将result当前位和i相乘后+进位,计算结果放在临时变量tmpvalue中
resultM[j] = tmpvalue % 10; //当前位存放tmpvalue的个位数
upvalue = tmpvalue / 10; // 将tmpvalue/10后的数用于存放进位,供下次计算使用
}
//当result计算完成结束循环后,如果最后一位有进位,那么result的位数就要增加,所以这里就要对这个情况进行处理
while(upvalue != 0)
{
resultM[size] = upvalue % 10;
upvalue /= 10;
size++;
}
}
如此就算完成了。
3)改进
查看结果可以发现,后面有若干个0.如果在for j: 0-result.size 这层循环里面进行数个0*i,就很浪费时间了。
所以在for j: 0-result.size 循环前,判断0的个数,初始zeroNumber=-1:
while(result[zeroNumber + 1] == 0)
{
zeroNumber++;
}
然后将for j: 0-result.size 中,j的初始计算改为zeroNumber+1,这样效率就会提高不少。
4)源码
5)效果
我在公司的机器稍微好些,10000基本秒杀,100000基本在2分钟左右计算完成。
我在家的机器配置稍差,我在虚拟机中运行,1.96G cpu,256M内存,计算10000在5秒内,计算100000需要14分钟以内
6)不足
CPU占用率高,不知道编程的时候,有哪些会使得cpu占用率高?要怎么使cpu占用率降低呢?
内存空间还存在有浪费情况。
效率还不够高。
7)小结
对大数阶乘的学习暂时告一段落了,等以后学的知识再多些,才能再进行改进了。