擂台:求N!的最后九个非零尾数

xstring 2004-04-20 11:48:50
N!的精确值以十进制表示,将最后面的连续的零都去掉。求最后面的几个数字。

大家都觉得求一个简单,而且以前也讨论过,那就来九个吧。

...全文
703 87 打赏 收藏 转发到动态 举报
写回复
用AI写文章
87 条回复
切换为时间正序
请发表友善的回复…
发表回复
junmayang 2004-09-07
  • 打赏
  • 举报
回复
牛人
BaiYunfeng 2004-08-29
  • 打赏
  • 举报
回复
楼主的方法是什么呢?
myh9999 2004-08-29
  • 打赏
  • 举报
回复
to 无心人(yaos) :
代码中确实还存在一个Bug,是因为我对分组后的处理过于简单而出现的。现修改代码如下,并测试了非10^n的数,未发现问题。并且我还对一个连续区域(320000-350000)之间的数测试均未出现差错。
  发现这个问题还得感谢你提的一问:从n分起的数学原理能解释一下么?
  至于这个问题,下面的代码会解释的。即如果不是刚好分完,则将其分为两段,再计算。比如:120001-320000可分为120001-279999各280000-320000,分别为T[79999]和T[20000].

  while (n > 0)
{
//计算少乘的2的个数
count_2 += n/2; //偶数的个数
count_2 -= n/10; //个位为0的偶数
count_2 -= n/5; //含因子5的数

unsigned long x = n % GROUPS;
if (x)
{
result = ModMul(result, T[x]);
x %= BASE;
result = ModMul(result, ModPow(ModMul(T[x], T[BASE-x-1]), (n/GROUPS)<<1));
}
else
{
count_group += n/GROUPS;
}
n /= 5;
}
无心人 2004-08-29
  • 打赏
  • 举报
回复
从n分起的数学原理能解释一下么?想不明白

这个是不是为了避免剩余数的处理

嘿嘿,俺上边的算法的意思是只计算末尾数字为1, 3, 7, 9的数字的乘积,然后乘以2的乘方
可以证明,这个和最后的结果是一致的
myh9999 2004-08-28
  • 打赏
  • 举报
回复
已发过来了,请查收
myh9999 2004-08-28
  • 打赏
  • 举报
回复
result = (result* T[200000]^count_2) % 10^9,得到结果
更正一下:
  result = (result* T[200000]^count_group) % 10^9,得到结果
myh9999 2004-08-28
  • 打赏
  • 举报
回复
我的算法也同样能计算n很大的情况,只是我没有去用程序语言实现而亦。
下面是我的算法的最终版本概述,我已不准备再去改进了。
欢迎与我讨论:myh9999@vip.sina.com

*****************************
求n!的最后九个非零尾数

不论n多大,都采用同样的方法,只是数的表示方法可以要变化,比如count_2可能要
用64位数或者用能表示更大数的整数类来表示.

T[200001]:预先生成的一个数组。T[i]为1至i之间非5的倍数的乘积(偶数只乘一半)
count_2: 少乘的2的个数(不包括与5低消的数字)
count_group: 分组数
result : 结果,

result = 1
count_2 = 0
count_group = 0
while (n>0)
{
count_2 += (n/2 -n/10 - n/5);
count_group += n/200000;
result = ( result * T[n % 200000] ) % 10^9
n = n/5
}

result = (result* 2^count_2) % 10^9
result = (result* T[200000]^count_2) % 10^9,得到结果

*****************************
这个算法已经非常简练了,要想再优化已很难了,除非全部使用
汇编并且尽量减少函数调用、分支,还要考虑指令配对等等。
或者是找到更好的算法。

如果有其它算法,做出来比较一下就可以了
yaos 2004-08-28
  • 打赏
  • 举报
回复
yaojialin@263.net

发过来

似乎还有优化的余地
无心人 2004-08-28
  • 打赏
  • 举报
回复
显然,这个能计算到n很大的情况,感兴趣的是,他比你的程序效率高么?
无心人 2004-08-28
  • 打赏
  • 举报
回复
酱子好不好

保存1-200000内不包括5的倍数的奇数的乘积

则有下列算法

首先,计算20万分组的不包括5的奇数的乘积,这个已经解决,只是乘方和一个乘法

然后还剩下的数字,分成2种,
2^k * m, m = 10t + 1, 3, 7, 9
5^k * m, m = 10t + 1, 3, 7, 9

n通过不断除以2,能得到前一种的情况
n通过不断除以5,能得到后一种的情况

然后,n = n / 10重复上边过程,直到n = 0为止,
此时,然后开始计算2的乘方,次数是n!中2的乘方次数减5的乘方次数,可以证明 n = n /10不影响这个,因为n/10同时减掉2和5的相同的因子

yaos 2004-08-28
  • 打赏
  • 举报
回复
初始化开始...

*******************************************************
Totol Time : 0.0594418s
The CPU frequence : 1999 MHz
*******************************************************
初始化完成!

*******************************************************
Totol Time : 1.35668e-006s
The CPU frequence : 1999 MHz
*******************************************************
n = 1
Result = 1
Check = 1

*******************************************************
Totol Time : 1.17059e-006s
The CPU frequence : 1999 MHz
*******************************************************
n = 10
Result = 36288
Check = 36288

*******************************************************
Totol Time : 2.16108e-006s
The CPU frequence : 1999 MHz
*******************************************************
n = 100
Result = 210916864
Check = 210916864

*******************************************************
Totol Time : 4.69635e-006s
The CPU frequence : 1999 MHz
*******************************************************
n = 1000
Result = 27753472
Check = 27753472

*******************************************************
Totol Time : 5.61281e-006s
The CPU frequence : 1999 MHz
*******************************************************
n = 10000
Result = 1579008
Check = 1579008

*******************************************************
Totol Time : 6.64332e-006s
The CPU frequence : 1999 MHz
*******************************************************
n = 100000
Result = 957162496
Check = 957162496

*******************************************************
Totol Time : 6.52926e-006s
The CPU frequence : 1999 MHz
*******************************************************
n = 1000000
Result = 58412544
Check = 58412544

*******************************************************
Totol Time : 8.81241e-006s
The CPU frequence : 1999 MHz
*******************************************************
n = 10000000
Result = 574194688
Check = 574194688

*******************************************************
Totol Time : 1.08474e-005s
The CPU frequence : 1999 MHz
*******************************************************
n = 100000000
Result = 840754176
Check = 840754176

*******************************************************
Totol Time : 1.58339e-005s
The CPU frequence : 1999 MHz
*******************************************************
n = 1000000000
Result = 933638144
Check = 0

Tail.exe 中的 0x77ea9a91 处最可能的异常: 0x40010005: Control-C 。
yaos 2004-08-28
  • 打赏
  • 举报
回复
在我的机器上有错误发生,但是不影响用

我的是P4 XEON 2 * 2
yaos 2004-08-28
  • 打赏
  • 举报
回复
3X
myh9999 2004-08-27
  • 打赏
  • 举报
回复
最后还要补充计算少乘的2,即总的少乘个数-因子5的个数
myh9999 2004-08-27
  • 打赏
  • 举报
回复
首先计算一个数组,保存1-100000的末九位数字(不乘含因子5的数,偶数还只乘一半)

预先计算出n的所有因子5的个数,易求得。上面讨论很多。

下面是一个递归过程。
 计算1-n的不含因子5的数的乘积,,偶数还只乘一半
 计算出少乘2的个数。
 n /= 5;
 重复上述步骤,直到n = 0;

在“计算1-n的不含因子5的数的乘积,,偶数还只乘一半”时采用分组策略,分组时从n分起,而不是先前的从1分起。
 分组时先按2*10^组大小分,余下的如果大于10^5,假设为10^5+x,则分组从10^5-x至10^5+x.
这样经过分组分成三部分,第一部分可查先前计算得到的数组100000的值,再根据组数乘方
 第二部分,即查数组x的值,再平方
 第三部分可直接查数组得到。

 对于第一部分的乘方,还可预先记下乘方次数,最后一起来计算,这样改进后,我的速度又加快了不少了,已全部在10^-6秒数量组上。如果楼主也是采用1.7G的CPU的话,速度已超过楼主了

 建立前面的数组也很快,1.7GCPU不到0.03秒。





myh9999 2004-08-27
  • 打赏
  • 举报
回复
没什么特别的,只是对上面的方法进行了改进。
如果需要代码的话,给出电子邮件,我发给你
   
yaos 2004-08-27
  • 打赏
  • 举报
回复
什么公式?写出来

除了递归公式,没有什么能大幅度提高速度的方法

我的那个方法只能做到0.001秒的水平计算10^9个连乘积
myh9999 2004-08-26
  • 打赏
  • 举报
回复
下面是我的运行结果

*******************************************************
Totol Time : 2.38539e-006s
The CPU frequence : 1697 MHz
*******************************************************
n = 1
Result = 1
Check = 1

*******************************************************
Totol Time : 2.55038e-006s
The CPU frequence : 1697 MHz
*******************************************************
n = 10
Result = 36288
Check = 36288

*******************************************************
Totol Time : 3.92457e-006s
The CPU frequence : 1697 MHz
*******************************************************
n = 100
Result = 210916864
Check = 210916864

*******************************************************
Totol Time : 5.06541e-006s
The CPU frequence : 1697 MHz
*******************************************************
n = 1000
Result = 27753472
Check = 27753472

*******************************************************
Totol Time : 5.7749e-006s
The CPU frequence : 1697 MHz
*******************************************************
n = 10000
Result = 1579008
Check = 1579008

*******************************************************
Totol Time : 5.98232e-006s
The CPU frequence : 1697 MHz
*******************************************************
n = 100000
Result = 957162496
Check = 957162496

*******************************************************
Totol Time : 6.97702e-006s
The CPU frequence : 1697 MHz
*******************************************************
n = 1000000
Result = 58412544
Check = 58412544

*******************************************************
Totol Time : 8.41721e-006s
The CPU frequence : 1697 MHz
*******************************************************
n = 10000000
Result = 574194688
Check = 574194688

*******************************************************
Totol Time : 1.09605e-005s
The CPU frequence : 1697 MHz
*******************************************************
n = 100000000
Result = 840754176
Check = 840754176

*******************************************************
Totol Time : 1.22593e-005s
The CPU frequence : 1697 MHz
*******************************************************
n = 1000000000
Result = 933638144
Check = 0
Press any key to continue
myh9999 2004-08-26
  • 打赏
  • 举报
回复
终于要解决了,我的算法已经能将时间控制在0.00002秒以内,跟楼主的结果已不相上下了
yaos 2004-08-25
  • 打赏
  • 举报
回复
阶乘尾数的计算,一点想法,请指正

以10000!的末尾2位非零数字为例

首先考虑末尾不是0的数字
(m, n, a, b,c)表示m ^ n * (a, b之间的末尾数字不是5的奇数的乘积) ^ c

则末尾不是0的数字可以分成下面的几个组
(1, 1, 1, 9999, 1)
(2, 1, 1, 4999, 1)
(2, 2, 1, 2499, 1)
(2, 3, 1, 1249, 1)
(2, 4, 1, 623, 1)
(2, 5, 1, 311, 1)
(2, 6, 1, 153, 1)
(2, 7, 1, 77, 1)
(2, 8, 1, 39, 1)
(2, 9, 1, 19, 1)
(2, 10, 1, 9, 1)
(2, 11, 1, 3, 1)
(2, 12, 1, 1, 1)
(5, 1, 1, 1999, 1)
(5, 2, 1, 399, 1)
(5, 3, 1, 79, 1)
(5, 4, 1, 13, 1)
(5, 5, 1, 3, 1)
然后考虑末尾一个0的情况,得到
(1, 1, 1, 999, 1)
(2, 1, 1, 499, 1)
(2, 2, 1, 249, 1)
(2, 3, 1, 123, 1)
(2, 4, 1, 61, 1)
(2, 5, 1, 31, 1)
(2, 6, 1, 13, 1)
(2, 7, 1, 7, 1)
(2, 8, 1, 3, 1)
(2, 9, 1, 1, 1)
(5, 1, 1, 199, 1)
(5, 2, 1, 39, 1)
(5, 3, 1, 7, 1)
(5, 4, 1, 1, 1)
依次类推,去掉2个零
(1, 1, 1, 99, 1)
(2, 1, 1, 49, 1)
(2, 2, 1, 23, 1)
(2, 3, 1, 11, 1)
(2, 4, 1, 3, 1)
(2, 5, 1, 3, 1)
(2, 6, 1, 1, 1)
(5, 1, 1, 19, 1)
(5, 2, 1, 3, 1)
去掉3个零
(1, 1, 1, 9, 1)
(2, 1, 1, 3, 1)
(2, 2, 1, 1, 1)
(2, 3, 1, 1, 1)
(5, 1, 1, 1, 1)
10000!的尾数应该由这些乘积产生
然后开始合并相对的项(根据2 ^ * 5 ^ k产生1的原理)产生:
(1, 1, 1, 9999, 1)
(1, 1, 1, 1999, 1)
(1, 1, 1, 399, 1)
(1, 1, 1, 79, 1)
(1, 1, 1, 13, 1)
(1, 1, 1, 3, 1)
(2, 1, 2001, 4999, 1)
(2, 2, 401, 2499, 1)
(2, 3, 81, 1249, 1)
(2, 4, 17, 623, 1)
(2, 5, 7, 311, 1)
(2, 6, 1, 153, 1)
(2, 7, 1, 77, 1)
(2, 8, 1, 39, 1)
(2, 9, 1, 19, 1)
(2, 10, 1, 9, 1)
(2, 11, 1, 3, 1)
(2, 12, 1, 1, 1)

(1, 1, 1, 999, 1)
(1, 1, 1, 199, 1)
(1, 1, 1, 39, 1)
(1, 1, 1, 7, 1)
(1, 1, 1, 1, 1)
(2, 1, 201, 499, 1)
(2, 2, 41, 249, 1)
(2, 3, 9, 123, 1)
(2, 4, 3, 61, 1)
(2, 5, 1, 31, 1)
(2, 6, 1, 13, 1)
(2, 7, 1, 7, 1)
(2, 8, 1, 3, 1)
(2, 9, 1, 1, 1)

(1, 1, 1, 99, 1)
(1, 1, 1, 19, 1)
(1, 1, 1, 3, 1)
(2, 1, 21, 49, 1)
(2, 2, 7, 23, 1)
(2, 3, 1, 11, 1)
(2, 4, 1, 3, 1)
(2, 5, 1, 3, 1)
(2, 6, 1, 1, 1)

(1, 1, 1, 9, 1)
(1, 1, 1, 1, 1)
(2, 1, 3, 3, 1)
(2, 2, 1, 1, 1)
(2, 3, 1, 1, 1)
现在全部5消去,整理得到
(1, 1, 1, 1, 15)
(1, 1, 3, 3, 14)
(1, 1, 7, 7, 12)
(1, 1, 9, 9, 11)
(1, 1, 11, 13, 10)
(1, 1, 17, 19, 9)
(1, 1, 21, 39, 8)
(1, 1, 41, 79, 7)
(1, 1, 81, 99, 6)

(1, 1, 101, 199, 5) = (1, 1, 1, 99, 5)
(1, 1, 201, 399, 4) = (1, 1, 1, 99, 4)
(1, 1, 401, 999, 3) = (1, 1, 1, 99, 3 * 6)
(1, 1, 1001, 1999, 2) = (1, 1, 1, 99, 2 * 20)
(1, 1, 2001, 9999, 1) = (1, 1, 1, 99, 1 * 80)

(2, 1, 3, 3, 1)
(2, 1, 21, 49, 1)
(2, 1, 201, 499, 1) = (2, 1, 1, 99, 20)
(2, 1, 2001, 4999, 1) = (2, 1, 1, 99, 30)

(2, 2, 1, 1, 1)
(2, 2, 7, 23, 1)
(2, 2, 41, 249, 1) = (2, 2, 41, 99, 1) * (2, 2, 1, 99, 1,) * (2, 2, 1, 49, 1)
(2, 2, 401, 2499, 1) = (2, 2, 1, 99, 25)

(2, 3, 1, 1, 1)
(2, 3, 1, 11, 1)
(2, 3, 9, 123, 1) = (2, 3, 9, 99, 1) * (2, 3, 1, 23, 1)
(2, 3, 81, 1249, 1) = (2, 3, 81, 99, 1) * (2, 3, 1, 99, 12) * (2, 3, 1, 49, 1)

(2, 4, 1, 3, 1)
(2, 4, 3, 61, 1)
(2, 4, 17, 623, 1) = (2, 4, 17, 99, 1) * (2, 4, 1, 99, 5) * (2, 4, 1, 23, 1)

(2, 5, 1, 3, 2)
(2, 5, 7, 31, 2)
(2, 5, 33, 311, 1) = (2, 5, 33, 99, 1) * (2, 5, 1, 99, 2) * (2, 5, 1, 11, 1)

(2, 6, 1, 1, 3)
(2, 6, 3, 13, 2)
(2, 6, 17, 153, 1) = (2, 6, 17, 99, 1) * (2, 6, 1, 53, 1)

(2, 7, 1, 7, 2)
(2, 7, 9, 77, 1)

(2, 8, 1, 3, 2)
(2, 8, 7, 39, 1)

(2, 9, 1, 1, 2)
(2, 9, 3, 19, 1)

(2, 10, 1, 9, 1)

(2, 11, 1, 3, 1)

(2, 12, 1, 1, 1)
这样子不断归并,最后得到结果
加载更多回复(67)

33,007

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧