`
gaofen100
  • 浏览: 1243374 次
文章分类
社区版块
存档分类
最新评论

计算阶乘n!末尾所含0的个数

 
阅读更多

From : http://www.chinaunix.net/jh/23/926848.html

[color=Orange]问题描述[/color]
给定参数n(n为正整数),请计算n的阶乘n!末尾所含有“0”的个数。
例如,5!=120,其末尾所含有的“0”的个数为1;10!=3628800,其末尾所含有的“0”的个数为2;20!=2432902008176640000,其末尾所含有的“0”的个数为4。

[color=Orange]计算公式[/color]
这里先给出其计算公式,后面给出推导过程。
令f(x)表示正整数x末尾所含有的“0”的个数,则有:
当0<n<5时,f(n!)=0;
当n>=5时,f(n!)=k+f(k!),其中k=n/5(取整)。

[color=Orange]问题分析[/color]
显然,对于阶乘这个大数,我们不可能将其结果计算出来,再统计其末尾所含有的“0”的个数。所以必须从其数字特征进行分析。下面我们从因式分解的角度切入分析。

我们先考虑一般的情形。对于任意一个正整数,若对其进行因式分解,那么其末尾的“0”必可以分解为2*5。在这里,每一个“0”必然和一 个因子“5”相对应。但请注意,一个数的因式分解中因子“5”不一定对应着一个“0”,因为还需要一个因子“2”,才能实现其一一对应。

我们再回到原先的问题。这里先给出一个结论:
结论1:对于n的阶乘n!,其因式分解中,如果存在一个因子“5”,那么它必然对应着n!末尾的一个“0”。
下面对这个结论进行证明:
(1)当n<5时,结论显然成立。
(2)当n>=5时,令n!=[5k*5(k-1)*...*10*5]*a,其中n=5k+r(0<=r<=4),a是一个不含因子“5”的整数。
对于序列5k,5(k-1),...,10,5中每一个数5i(1<=i<=k),都含有因子“5”,并且 在区间(5(i-1),5i)(1<=i<=k)内存在偶数,也就是说,a中存在一个因子“2”与5i相对应。即,这里的k个因子 “5”与n!末尾的k个“0”一一对应。
我们进一步把n!表示为:n!=5^k*k!*a(公式1),其中5^k表示5的k次方。很容易利用(1)和迭代法,得出结论1。

上面证明了n的阶乘n!末尾的“0”与n!的因式分解中的因子“5”是一一对应的。也就是说,计算n的阶乘n!末尾的“0”的个数,可以转换为计算其因式分解中“5”的个数。

令f(x)表示正整数x末尾所含有的“0”的个数,g(x)表示正整数x的因式分解中因子“5”的个数,则利用上面的的结论1和公式1有:
f(n!)=g(n!)=g(5^k*k!*a)=k+g(k!)=k+f(k!)
所以,最终的计算公式为:
当0<n<5时,f(n!)=0;
当n>=5时,f(n!)=k+f(k!),其中k=n/5(取整)。

[color=Orange]计算举例[/color]
f(5!)=1+f(1!)=1
f(10!)=2+f(2!)=2
f(20!)=4+f(4!)=4
f(100!)=20+f(20!)=20+4+f(4!)=24
f(1000!)=200+f(200!)=200+40+f(40!)=240+8+f(8!)=248+1+f(1)=249
...

终于写完了,困S了:)



yg 回复于:2007-04-21 16:35:30

也算递归吧


win_hate 回复于:2007-04-21 16:43:35

Pot_p(n!)=[n/p]+Pot_p([n/p]!)

递归使用这个公式就可以得到Pot_p(n!)的表达式。

可以参考柯召,孙琦的《数论讲义》上册,数论函数部分。


tyc611 回复于:2007-04-21 16:48:41

引用:原帖由win_hate 于2007-4-2116:43发表
Pot_p(n!)=[n/p]+Pot_p([n/p]!)

递归使用这个公式就可以得到Pot_p(n!)的表达式。

可以参考柯召,孙琦的《数论讲义》上册,数论函数部分。


恩,我上面推出的公式就是这个,p为5吧?
哎,又吃了数字的亏了:em10:
手头没这本书,看能不能找到


圆点坐标 回复于:2007-04-21 16:53:09

我去年去学校招聘的时候,给应届生出了这个题目,只有2个人的想法靠近这个答案。


win_hate 回复于:2007-04-21 16:54:50

引用:原帖由tyc611 于2007-4-2116:48发表

恩,我上面推出的公式就是这个,p为5吧?
哎,又吃了数字的亏了:em10:
手头没这本书,看能不能找到



是的。


我以前写过一个资料,复制到这里吧:

......
另一很精致的方法来自柯召的“数论讲义”,把ord_p简记为ord:

*ord(ab)=ord(a)+ord(b)

*ord(n!)=[n/p]+ord([n/p]!),因为:
ord(n!)
=ord(1)+...+ord(n)
=ord(p)+ord(2p)+...+ord([n/p]p)
=(ord(1)+ord(p))+(ord(2)+ord(p))+...+(ord([n/p])+ord(p))
=(ord(1)+1)+(ord(2)+1)+...+(ord([n/p])+1)
=[n/p]+ord([n/p]!)

*[[n/p]/p]=[n/p^2]
若把n写成p进制表达,则上面这个式子很好理解

现在只要递归地调用ord(n!)=[n/p]+ord([n/p]!)就可以得到ord(n!)的表达式:

[n/p]+[n/p^2]+[n/p^3]+....


awake 回复于:2007-04-21 18:23:54

赞排版。


ArXoR 回复于:2007-04-21 19:42:50

一个和此问题有点关系的有趣问题是:
给定质数p,求杨辉三角第n行有多少个元素被p整除.


win_hate 回复于:2007-04-21 22:18:31

引用:原帖由ArXoR 于2007-4-2119:42发表
一个和此问题有点关系的有趣问题是:
给定质数p,求杨辉三角第n行有多少个元素被p整除.



嗯,柯召那本书的pot一节最后一个定理就是求pot_p(binomial(n,r))的表达式.


ArXoR 回复于:2007-04-21 22:49:21

引用:原帖由win_hate 于4/21/0722:18发表


嗯,柯召那本书的pot一节最后一个定理就是求pot_p(binomial(n,r))的表达式.



pot_p(binomial(n,r))有closefrom?我只知道级数表达的形式.
不过不妨碍我说的这个问题的解决,呵呵.


win_hate 回复于:2007-04-22 00:19:43

引用:原帖由ArXoR 于2007-4-2122:49发表


pot_p(binomial(n,r))有closefrom?我只知道级数表达的形式.
不过不妨碍我说的这个问题的解决,呵呵.



不是close的,一个关系式吧,在你给的这个题目用不上。

考虑pot_p(binomial(n,r)),

[n/p^i]>=[(n-r)/p^i]+[r/p^i].如果p不整除binomial(n,r),意味着对所有的i,等号都成立.

设n=(n_m...n_0)_p,r=(r_s...r_0)_p,等号成立要求

n_m...n_i=[n_m...n_i.n_{i-1}...n_0-r_s...r_i.r_{i-1}...r_0]+r_s...r_i
=n_m...n_i+[0.n_{i-1}...n_0-0.r_{i-1}...r_0]

也就是

[0.n_{i-1}...n_0-0.r_{i-1}...r_0]=0.

即对每个i,nmodp^i>=rmodp^i,能被p整除的个数为

n+1-(n_m+1)...(n_0+1).

[本帖最后由win_hate于2007-4-2200:31编辑 ]


ArXoR 回复于:2007-04-22 00:39:08

引用:原帖由win_hate 于4/22/0700:19发表


不是close的,一个关系式吧,在你给的这个题目用不上。

考虑pot_p(binomial(n,r)),

[n/p^i]>=[(n-r)/p^i]+[r/p^i].如果p不整除binomial(n,r),意味着对所有的i,等号都成立.

...



Bingo...:)


yuanchengjun 回复于:2007-04-22 09:38:33

#include<stdio.h>
#include<string.h>
#include<stdint.h>

intmain(void)
{
uint32_tcount=0;
uint32_ttmp;
uint32_tn;

for(n=1;n<=1000;n++)
{
tmp=n;
while((0==(tmp%5))&&(tmp>0))
{
tmp/=5;
count++;
}
}

printf("%d",count);

return0;
}


tyc611 回复于:2007-04-22 16:35:44

win_hate和ArXoR兄的回复太深奥了,发晕中。。。


tyc611 回复于:2007-04-22 16:40:01

引用:原帖由yuanchengjun 于2007-4-2209:38发表
#include<stdio.h>
#include<string.h>
#include<stdint.h>

intmain(void)
{
uint32_tcount=0;
uint32_ttmp;
uint32_tn;

for(n=1;n<=1000;n++)
{
t...


你这个太慢了,看看我上面的示例计算1000时才几步,你的步进方式有问题,还有应该利用前面已经计算的结果

我写个上面公式的实现吧,收敛速度很快的:5^x=num/5*5,x递归或者迭代次数
(一个递归,一个迭代)


#include<stdio.h>



intf_r(intnum)

{

if(num<5)

return0;

else

return(num/5)+f_r(num/5);

}



intf_i(intnum)

{

intcount=0;

for(;num>=5;num/=5)

count+=num/5;

returncount;

}



intmain(void)

{

intnum=1000;

printf("%d/n",f_r(num));

printf("%d/n",f_i(num));



return0;

}




openq 回复于:2007-04-24 12:14:03

LZ的方法部可取,N很大时n/5的阶乘就溢出了!


tyc611 回复于:2007-04-24 12:20:51

引用:原帖由openq 于2007-4-2412:14发表
LZ的方法部可取,N很大时n/5的阶乘就溢出了!


不好意思,可能是我公式没写好,其实根本就不计算阶乘的

看下我15楼给的算法实现就明白了

[本帖最后由tyc611于2007-4-2412:22编辑 ]


yuanchengjun 回复于:2007-04-24 12:35:30

引用:原帖由圆点坐标 于2007-4-2116:53发表
我去年去学校招聘的时候,给应届生出了这个题目,只有2个人的想法靠近这个答案。




要求太高了,只要计算结果正确,就应该算通过。


算法有很多,题目没有给出“最优算法”的信息,
如果那样,假设满分10分,凡正确的6分,剩下4分安计算复杂度给分,
最优的或者达到什么程度以上的10分。


计算机和数学不一样的,有时候要考虑具体运算环境。
1,计算机除了算法以外要考虑速度和时间。
2,有的时候,正确性和稳定性比优秀算法、高速更重要。
3,有的时候,多循环10次,比多一次递归调用节省时间。


运算速度:
1,公式。
2,枚举,只举出有效情况。不是枚举所有,再判断是否有效。
3,枚举,尽可能缩小有效情况范围。
4,枚举所有。最无奈的情况。


有的时候想不出来,有的时候时间有限,而且我不是大侠:-(
想不出公式就枚举;枚举的时候尽可能缩小范围;
如果不知到缩小范围条件,就枚举所有……
想这些东西需要一定的基础,还有时间的,……

[本帖最后由yuanchengjun于2007-4-2412:53编辑 ]


ArXoR 回复于:2007-04-24 12:58:53

引用:原帖由yuanchengjun 于4/24/0712:35发表



要求太高了,只要计算结果正确,就应该算通过。


算法有很多,题目没有给出“最优算法”的信息,
如果那样,假设满分10分,凡正确的6分,剩下4分安计算复杂度给分,
最优的或者达到什么程度以上的10分...



这是在知道可以有更优算法的情况下才有这些选择...
连知道都不知道的话在应用规模很大的时候就完全没有办法了.
就像如果知道可以用SimplexAlgorithm去做线性规划至少应该知道线性规划是有P类算法的.

而且基本上没法要求最优算法,求解并证明最坏复杂度紧下界不是一件简单的事情.


babyfrog 回复于:2007-04-24 13:13:57

ACM里面这么写会timeout否


yuanchengjun 回复于:2007-04-24 13:15:32

同意楼上,一般这些算法需要千锤百炼,好的软件公司会把类似这些东西沉淀下来,要用调一下就OK。
再帖一次,按照楼主算法。



#include<stdio.h>
#include<stdint.h>

intmain(void)
{
uint32_tcount;
uint32_tn;

n=1000;
count=0;
while(n>4)
count+=n/=5;

printf("%d/n",count);

return0;
}

[本帖最后由yuanchengjun于2007-4-2413:17编辑 ]


slay78 回复于:2007-04-24 14:06:56

从2开始,阶乘的最后一位数字非零数字都是偶数,所以最后一位肯定不是5,那么这些偶数只有碰到5或者10乘起来才会再多一个0


thinkinnight 回复于:2007-04-24 15:16:19

引用:原帖由win_hate 于2007-4-2116:54发表


是的。


我以前写过一个资料,复制到这里吧:

......
另一很精致的方法来自柯召的“数论讲义”,把ord_p简记为ord:

*ord(ab)=ord(a)+ord(b)

*ord(n!)=[n/p]+ord([n...



=ord(1)+...+ord(n)
=ord(p)+ord(2p)+...+ord([n/p]p)

对于这一步不知道是怎么转的


win_hate 回复于:2007-04-24 17:32:55

引用:原帖由thinkinnight 于2007-4-2415:16发表


=ord(1)+...+ord(n)
=ord(p)+ord(2p)+...+ord([n/p]p)

对于这一步不知道是怎么转的



如果a不是p的倍数,则ord(a)=0。把值为0的项去掉,剩下的就只有pk形式的了,再估计一下k的范围即可。


shhgs 回复于:2007-04-24 19:54:37

Pycode

defzeros(n):

result=0

n=n/5

whilen!=0:

result+=n

n=n/5

returnresult



That'ssosimple.Primaryschool'smathproblem.


thinkinnight 回复于:2007-04-24 21:31:30

哦,精巧


局外人 回复于:2007-04-24 22:10:22


(define(fnpr)

(let((t(quotientnp)))

(if(>n0)(ftp(+rt))r)))




shhgs 回复于:2007-05-02 11:44:41

10=2*5

由于在[1,n]中,2的因子远远多于5的因子,因此只要计算[1,n]当中有多少5的因子就能知道n!的末尾有多少连续的0了。

那么[1,n]当中有多少5的因子呢?

假设,现在有一个p,使得5**p<=n<5**(p+1),
则,[1,n]中,能整除5的数有n/5,整除25的数有n/25,...,n/(5**p)--这里的除号作整除讲
则[1,n]中,5的因子共有n/5+n/25+n/125+...+n/(5**p)个

只不过是小学数学竞赛的题目,用得着动用那么吓人的数学知识吗?


cjaizss 回复于:2007-05-02 12:44:27

太easy了,

inthow_much_0(inti)

{

intret=0;

while(i){

i/=5;

ret+=i;

}

returnret;

}




局外人 回复于:2007-05-02 13:42:08

引用:原帖由shhgs 于2007-5-211:44发表
只不过是小学数学竞赛的题目,用得着动用那么吓人的数学知识吗?
...



同样一个题目,小学生有小学生的做法,大学生有大学生的做法,这是个层次的问题......


bitzilla 回复于:2007-05-11 15:56:56

殊途同归,


oopos 回复于:2007-10-20 20:05:01

不错,分析的很经典!!!
呵呵,我原来是这样想的,
把所得的乘积分解质因式,则2的个数肯定比5多,而又只有:
2x5才能产生末0..
因此:
yinzi=0;
for(;n;){n/=i;yinzi+=n;}

其中:i=5;


feiyang21687 回复于:2007-10-20 20:46:03

LZ的递归式麻烦了:
多少个零可以算包含有多少个5,多少个25,多少个5^3...
f(a!)=a/5+2*(a/25)+3*(a/5^3)+...


win_hate 回复于:2007-11-05 18:57:46

引用:原帖由ArXoR 于2007-4-2119:42发表[url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=6708836&ptid=926848]
一个和此问题有点关系的有趣问题是:
给定质数p,求杨辉三角第n行有多少个元素被p整除.




我在工作中居然真的碰到了这个问题,:shock:

这个东西跟所谓的lucas公式有关,如果







我们老大给了个漂亮的证明,在这里共享一下:

在F_p[z]上展开(1+z)^n,有




对比一下两边的系数即可。

分享到:
评论

相关推荐

    C++版本计算n阶乘末尾0的个数原理讲解及代码实现

    ### C++版本计算n阶乘末尾0的个数原理讲解及代码实现 #### 概述 本篇文章主要介绍如何使用C++编程语言来计算一个正整数n的阶乘末尾0的数量,并通过示例代码加以说明。该方法不仅阐述了理论基础,还提供了具体的实现...

    求n!末尾0的个数

    求n!数的末尾0的个数.用c语言实现。 简单方便

    n的阶乘末尾有多少个0_n的阶乘末尾的0_

    然而,当n变得非常大时,直接计算阶乘可能会导致整数溢出,因为n!的增长速度非常快。为了解决这个问题,我们可以关注阶乘末尾的零个数,而不是整个数值本身。题目“n的阶乘末尾有多少个0”就关注了这个特定的数学...

    n的阶乘问题--阶乘位数--阶乘末尾0的个数

    本文将深入探讨“n的阶乘问题”,包括阶乘的定义、计算阶乘位数的方法以及如何确定阶乘末尾零的个数。 首先,阶乘是指一个正整数n与小于等于它的所有正整数的乘积。用数学符号表示为`n! = n × (n-1) × (n-2) × ....

    判断阶乘末尾有几个零

    判断阶乘末尾有几个零 阶乘 不计算阶乘 不计算阶乘

    千万不要被阶乘吓倒

    阶乘(Factorial)是个很有意思的函数,但是不少人都比较怕它,我们来看看两个与阶乘相关的问题: 1、 给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N!=3 628 800,N!的末尾有两个0。2、求N!的...

    n的阶乘(n!)末尾有多少个0

    n的阶乘(n!)末尾有多少个0? 代码实现非常简单!!!—- n!的末尾有count个0. int n; // n!的末尾有count个0. int count = 0; for (int i = 0; i &lt; n; i++) { n /= 5; count += n; } 由于在N特别大的时候...

    C语言编程训练:循环结构-求阶乘末尾零个数

    数的阶乘定义为 N!=1 x 2 x 3 x ... N。对于任何给定的整数N,Z(N)指以十进制表示的N!的末尾零的个数。例如10!=3628800,则Z(N)=2。 编写计算机程序有效的确定Z的值。 【输入说明】 输入的第一行是一个单个的确定...

    java阶乘计算获得结果末尾0的个数代码实现

    在Java编程中,计算阶乘并确定结果末尾零的数量是一项常见的任务,特别是在解决数学问题或算法挑战时。阶乘是指一个正整数n的所有小于等于n的正整数的乘积,表示为n!。例如,5!(5的阶乘)等于5 × 4 × 3 × 2 × 1...

    用C语言实现n!最后一位非零数字的算法与程序分析.pdf

    在传统的计算机编程中,当n的值超过20时,使用整型或长整型数据类型直接计算阶乘往往会导致溢出。这不仅使得结果不准确,而且还会导致计算过程中的资源浪费和性能下降。为了解决这个问题,文章提出了一种非传统的...

    python 给你一个正整数列表 L, 输出L内所有数字的乘积末尾0的个数

    # 给你一个正整数列表 L, 输出L内所有数字的乘积末尾0的个数。(提示:不要直接相乘,数字很多,相乘得到的结果可能会很大)。 # 输入示例 # 输入:L=[2,8,3,50] # 输出示例 # 输出:2 # 解析 # 所有元素相乘, 算最后是...

    c语言编程题之数学问题阶乘后的0.zip

    printf("%d的阶乘末尾有%d个0\n", num, zeros); return 0; } ``` 这个程序首先定义了一个`countTrailingZeros`函数,它通过循环计算5的幂并累加n能被这些幂整除的次数来找到零的个数。在`main`函数中,用户输入一...

    C++计算一个数字的二进制中0或1的个数原理及代码

    ### C++ 计算一个数字的二进制中0或1的个数原理及代码解析 在计算机科学中,二进制表示法是基础之一,它不仅被用于数据存储,还在算法设计、加密技术以及系统优化等多个方面发挥着重要作用。本篇文章将详细探讨如何...

    简单代码之matlab计算阶乘

    小程序之matlab计算阶乘,阶乘函数的实现

    高精度计算1000阶乘

    为了处理这个问题,我们可以采用数组来存储大数,并通过逐位相乘的方式来计算阶乘的结果。在这个例子中,我们定义了一个长度为100000的数组`a[]`来存放每一位的数字。每个元素代表一个十进制数位上的数值。 #### ...

    求1000阶乘的结果末尾有多少个0

    在编程和算法问题中,有时会遇到计算阶乘并寻找结果末尾零的数量的问题。这个问题在本例中是计算1000的阶乘(1000!)末尾有多少个零。解决这类问题的关键在于理解零出现在末尾是因为数字可以被10整除,而10由2和5的...

    刷题遇到的一些题目(Java)——持续更新

    (即阶乘)末尾有多少个0? 比如: n = 10; n! = 3628800,所以答案为2 原题链接:https://www.nowcoder.com/questionTerminal/6ffdd7e4197c403e88c6a8aa3e7a332a 算法思想:最简单的就是分解质因数 n! = n * (n-1) *...

    C语言N阶乘

    在实际应用中,更常见的做法是使用递归或循环直接计算阶乘,这通常更为高效。 在提供的文件"eb1c7c2ad48f46e38df75117740cf08f"中,可能包含了实现这种链表方法的C语言源代码。通过分析和运行这段代码,你可以更...

Global site tag (gtag.js) - Google Analytics