主题:大数阶乘末尾非零数字
birdfly324
[专家分:20] 发布于 2005-06-10 18:21:00
求教,谁有算法,算P!末尾非零数字,0<=p<=10^1000,acm.hit.edu.cn,1013
回复列表 (共10个回复)
沙发
compiler [专家分:80] 发布于 2005-06-10 19:34:00
不是很难的,主要在于计算
1*2*3*4*6*7*8*9*11*12*... (mod 5^18)
也就是去掉所有5的倍数后的乘积.
方法如下:
由于对于充分大的N,N!中因子2的数目要远远大于因子5的数目,我们可以认为对于充分大,2的因子数目
减去5的因子数目不小于18了,也就是,去掉N!末尾所有的0后面的数我们认为还是被2^18整除,
实际上,对于28!,
2的因子数目为:
[28/2]+[28/4]+[28/8]+[28/16]=25
5的因子数目为:
[28/5]+[28/25]=6
所以28!的2的因子数目同5的因子差值已经不小于18了,对于更大的N,这个差值也不会变小.
所以我们只需要对小于28的整数特殊处理(直接相乘就可以了),其余的,都认为N!去掉末尾的0后,
还是被2^18整除,所以我们只要计算出这个数对于5^18的余数,就可以计算出关于10^18的余数了.
对于N!,我们可以将5的倍数和不是5的倍数的数分开处理:
所有的5的倍数构成5^[N/5]*[N/5]!
如果我们已经得到所有不是5的倍数的乘积关于5^18的余数,记为g(N)
那么所求的结果(记为f(N))就有f(N)=g(N)*f([N/5]) (去掉了所有的因子5)
所以关键是求g(N),也就是1到N之间所有不是5的倍数的数的乘积关于5^18的余数
也就是最前面列出的.
现在讨论如何计算:
1*2*3*4*6*7*8*9*11*12*...*N (mod 5^18)
可以知道的是,
1*2*3*4*6*7*8*9*11*12*...*(5^17-1)=-1 (mod 5^18)
同样
(5^17*k+1)*(5^17*k+2)*...*(5^17*k+5^17-1)=-1 (mod 5^18) 所有5的倍数不包括
所以对于N>5^17,我们可以将前面的若干段去掉,全部用-1替换,最后变成一个长度不超过
5^17的连乘
(k*5^17+1)*(k*5^17+2)*...*(k*5^17+s) (mod 5^18) 其中s<5^17,5的倍数全部被去掉
然后剩下的数据我们再根据5^16的长度来分段,其中完整的段可以写成
(k*5^16+1)*(k*5^16+2)*....*(k*5^16+5^16-1) (mod 5^18)
这个可以看成关于k的函数,是个周期为5的函数,可以事先算好,保存起来.
同样剩下还有一段不完整的,我们再根据5^15长度来分段,完整段关于模5^18是周期为25的函数,
同样可以事先计算.
然后再划分余下不完整的段...
当最后长度为5^10时,是个周期为5^7的函数,也可以事先计算,不过一个70多K长的数据,还可以忍受的
最后长度不超过5^10的部分,直接计算它的值就可以了.
转贴
板凳
birdfly324 [专家分:20] 发布于 2005-06-11 12:31:00
没看明白,还是给了30
3 楼
birdfly324 [专家分:20] 发布于 2005-06-11 12:37:00
5^18从哪里出来的?
4 楼
compiler [专家分:80] 发布于 2005-06-12 22:36:00
呵呵,细心点嘛。
这个帖子中做了个约定,这里求的是末尾非零18位。
5 楼
rickone [专家分:15390] 发布于 2005-06-14 20:01:00
转自CSDN社区吧。
6 楼
zhouling90 [专家分:40] 发布于 2005-06-16 13:40:00
QB中高精度数最好是用字符型去做,数值型太小了啊!想一想计算机中内部是不是都是用加法运算的。1*2相当与1+1
2*3相当与3+3
5*10相当与10+10+10+10+10
这样想就可以了啊
7 楼
不是归人 [专家分:1400] 发布于 2005-06-16 17:51:00
先转一下以前我回的帖,楼主可能没看到
/*
根据费马小定理,n^5==n%5
所以n^(4k+1)==n%5
偶有个想法,懒得编了,先说说吧。
求n!末尾非零数
定义func(n)为所有小于n的非5的倍数的数之积的末尾非零数;
则n!=(func(n) * func(n/5)*5^(n/5) * ...)
ans=(func(n) * func(n/5)/2^(n/5) * func(n/25)/2^(n/25) ...)%5 * 2 //这里有问题,这个解释起来很麻烦,但是不是很难理解。后加
现在需要优化func(n)使其时间消耗为O(1),(O(log(n))可能也成,反正别是O(n)),优化成log(n)好像不是很困难。
还有求( n / 2^m )%10 (n%2^m==0)也是O(1)的,然后大概就解决了,程序我就懒得编了。
*/
前面意思大概一样,但是我不明白,后面只要把所有的5除掉,计算出5的个数,然后求出剩下的除5的余数就行了,为什么要求除5^17的余数?
8 楼
不是归人 [专家分:1400] 发布于 2005-06-16 18:41:00
1楼转的那帖的说法解不了这道题的,你想想,10^1000所含2的个数大到了一定程度,1楼的方法行不通
详细解释我的想法一下吧:
先定义SpeFact(n)为 所有小于n的非5的倍数的数之积;
SpeFact(n) := 1*2*3*4*6*7*8*9*11*12*...*N;
定义func(n)为 (所有小于n的非5的倍数的数之积 % 5);
func(n) := SpeFact(n) % 5;(实在想不出用什么做函数名)
简称 ans 为 n!末尾非零数
ans := n!末尾非零数;
有:
n! == (SpeFact(n) * SpeFact(n/5)*5^(n/5) * SpeFact(n/25)*25^(n/25) * ...); (直到(n/5^k)==0)
ans == ((n/10^k)%5)*2; (k为n中含有5的个数。1楼有解释)
n/5^k == (SpeFact(n) * SpeFact(n/5) * SpeFact(n/25) * ...)
所以
n/5^k % 5 == (SpeFact(n) * SpeFact(n/5) * SpeFact(n/25) * ...) % 5
n/5^k % 5 == (func(n) * func(n/5) * func(n/25) * ...) % 5;
有:
n/10^k % 5 == (n/5^k)/2^k % 5
(n/10^k)*2^k % 5 == (n/5^k) % 5
(n/10^k % 5)*2^k % 5 == (n/5^k) % 5
n/10^k % 5 == 1,2,3,4时
(n/10^k % 5)*2^k % 5 ==
k==4i+1 : 2,4,1,3
k==4i+2 : 4,3,2,1
k==4i+3 : 3,1,4,2
k==4i+4 : 1,2,3,4
所以
由 n/10^k % 5 可得出 n/10^k % 5的值,即可得出ans。
而
n/10^k % 5 == ( (func(n) * func(n/5) * func(n/25) * ...) % 5 )
如果将func(n)简化为O(1),则本题可简化为O(log n), k*(log(10^1000))=k*1000对本题来说是没有问题的,即,本题解决。
下面将func(n)的复杂度简化为O(1)
1*2*3*4*6*7*8*9*11*12*...*N % 5
== 1*2*3*4 * 1*2*3*4 * 1*2*...*(n%5) % 5
== -1 * -1 * ... * (n%5)! % 5
== (-1)^(n/5) * (n%5)! % 5
== (-1)^(n/5 % 2) * (n%5)! % 5
本题解决。
改了几次,符合逻辑,但看着太费事,各位将就看吧。本人还是高中生,也只会用这种方式表达,难为读我的帖子的人了,谢谢各位指正。
现在想来,有个地方写麻烦了,改过来看着更头疼,就是把func(n)提出个(-1)^(n/5)。不改了。
9 楼
yyw57156 [专家分:100] 发布于 2005-09-18 12:21:00
for i:=1 to 1000 do
begin
s:=s+i;
if s mod 10 <>0 then s:=s mod 10
else s:=s div 10
end
10 楼
yyw57156 [专家分:100] 发布于 2005-09-18 12:24:00
8楼的太复杂了!!
我来回复