N!最后一位非0位的求法

本文介绍了一种高效求解N!末位非0数字的方法,避免了直接计算阶乘带来的巨大开销。通过对2、5、3、7、9等因子的分析与循环规律的应用,提出了一套递归算法来解决该问题。
本文内容遵从CC版权协议 转载请注明出自:   http://blog.youkuaiyun.com/masterluo

        PS:这篇文章是很早以前我在使用博客圆的时候写的,后来换了两次搏客,都把这篇文章拿过来了。

        问题是求关于N!的最后一位非0位, 如3!=6,最后一位非0位为6, 5!=120, 最后一位非0位为2.怎么样快速的求出最后一位非0位呢?

     最朴素的想法就是先求出N!的结果,再求出结果的最后一位非0位.当N比较小时,是可以承受的,但是当N达到一定规模的时候,时间,空间都不会太理想.这里需要一些技巧.既然是求最后一位非0位,我们就可以先除非所有对结果没有影响的数,如10的倍数.于是先把N!因子分解得到形如2^a*5^b*c.这个时候我们去掉一个b个5因子和b个2因子,最后一位非0位是不变的.(N!中2的因子一定不会比5的因子少).于是我们的要求的结果就变为(2^(a-b)*c)%10.由(a*b)%10=((a%10)*(b%10))%10我们可以得((2^(a-b)%10)*(c%10))%10,由于c不会产生未位为0,故只保留c的最未位即可.于是可将c转化为1,3,7,9因子的相乘得到的结果的最未位(因为1,3,7,9因子相乘不会产生最未非0位,故去掉高位不会对结果产生影响,同时1*n=n可以去掉1的因子).

     2,3,7,9因子规律如下:

                      2^1=2, 2^2=4, 2^3=8, 2^4=16->(6), 2^5=32->(2)

                      3^0=1, 3^1=3, 3^2=9, 3^3=27->(7), 3^4=81->(1)

                      7^0=1, 7^1=7, 7^2=49->(9), 7^3=343->(3), 7^4=2401->(1)

                      9^0=1, 9^1=9, 9^2=81->(1), 9^3=729->(9), 9^4=6561->(1)

     它们都是以4为循环周期的.于是我们只要求出2, 5, 3, 7, 9因子的个数即可.

     首先我们求2,5因子在N!中的个数.2的因子的每个偶数到少有1个,同时将数列中每个数/2,其中的偶数还有一个2因子.直至n=1或n=0结束.5因子求法相同.代码如下:

  1. int getFactor25 ( int n, int f ) {
  2.     int ret= 0;
  3.     while (n> 0 ) {
  4.         ret+=n/f;
  5.         n/=f;
  6.     }
  7.     return ret;
  8. }

     3,7,9因子的个数有多少呢?对于1,2,3,4......n-1,n来说,未尾以3,7,9结束的数的个数为n/10+(n%10³f?1:0),(f=3,7, 9).同时我们对于对于奇数数列/5可以得到一个新的数列也有3,7,9因子,对于偶数数列/2也可以得到新的数列也有3,7,9的因子,将所有的3,7,9因子相加即可得到总的3,7,9因子的个数.得到3,7,9因子的个数后,我们可以将其全部转化为因子3的个数.因为9=3*3(3^2), 7=(3*3*3(3^3))%10,设f3, f7, f9为3, 7, 9因子的个数,全部转化为因子3的个数为f3+2*f9+3*f7.

     于是我们可以用递归同时求2,3,5,7,9因子的个数,代码如下:

  1. void getFactor ( int n ) {
  2.     if (n== 0 )
  3.         return;
  4.     for ( int m=n; m> 0; m/= 5 ) {
  5.         int t=m/ 10, r=m% 10;
  6.         f3+=t+ (r>= 3 );
  7.         f5+=t+ (r>= 5 );
  8.         f7+=t+ (r>= 7 );
  9.         f9+=t+ (r>= 9 );
  10.     }
  11.     f2+=n/ 2;
  12.     getFactor (n/ 2 );
  13. }

 

     可用两个数组表示循环:

  1. int p2 [ 4 ]= { 6, 2, 4, 8 };
  2. int p3 [ 4 ]= { 1, 3, 9, 7 };

 

     故结果为(1):当2, 5因子个数相同时,只与3因子相关,结果为p3[f3%4]%10;(2):当2因子大于5因子时,结果同时与3因子和2因子相关,为(p2[f2%4]*p3[f3%4])%10.

     其实通过N!的最未非0位的方法我们可以求排列组合数NPM,C(N,M)的最未非0位,用上面的各因子个数减去下面的各因子个数就是结果的各因子个数.只是此时需要注意的是5的因子可能会比2的因子多.当5的因子比2的因子多时,未位一定为5.其余情况与上面相同. 

<think>好的,用户现在想用递归方法实现计算n阶乘的函intf(intn)。我需要详细思考如何构建这个递归函,并确保正确性。首先,阶乘的定义是n! =n ×(n-1)!,当n >0时,且0! =1。这本身就是一个递归定义,所以递归实现起来应该很直接。关键点在于确定递归的终止条件,也就是当n等于0或1时返回1,否则返回n乘以f(n-1)。接下来要考虑递归的终止条件是否正确。如果用户输入的n是负怎么办?题目中函是int n,但可能没有处理负的情况。用户的问题中没有提到异常处理,可能只需要考虑负整的情况。所以函应该假设n是负的,或者在调用前检查。然后,递归调用的过程是否会导致栈溢出?对于较大的n值,比如常大的整,递归深度过大会导致栈溢出。但一般情况下,阶乘增长常快,n超过20时结果已经超过64的范围,所以可能在实际应用中不需要处理太大的n。因此函可能不需要处理这种情况,但可以提醒用户注意这个问题。学表达方面,阶乘的递归定义可以写成: $$n! =\begin{cases}1 &\text{if }n =0\\ n\times(n-1)! &\text{if }n >0\end{cases}$$ 这样在代码中就可以直接对应递归函的结构。现在编写代码示例,确保递归终止条件和递归步骤正确。例如,当n=5时,函应该计算5*4*3*2*1=120。测试用例需要包括n=0,n=1,以及较大的n值,比如n=5,验证结果是否正确。另外,需要提醒用户递归实现的潜在问题,比如栈溢出和性能问题。对于较大的n,迭代方法可能更高效,但用户明确要求递归实现,所以这些可以作为注意事项提及。 相关问题的生成需要考虑用户可能进一步的问题,比如如何处理负输入,如何优化递归,或者比较递归和迭代的优劣。还可以涉及溢出问题,或者阶乘的其他应用场景。 最后,确保回答结构清晰,分步骤解释递归的实现,提供代码示例,并包含学公式的正确格式。同时生成的相关问题要覆盖用户可能的后续疑问,帮助其扩展知识。</think>### 递归实现阶乘函 #### 1. 递归定义与学表达 阶乘的递归定义可表示为: $$ n! = \begin{cases} 1 & \text{if } n = 0 \quad (\text{终止条件}) \\ n \times (n-1)! & \text{if } n > 0 \quad (\text{递归步骤}) \end{cases} $$ 该定义直接对应递归函的实现逻辑[^1]。 #### 2. 完整代码实现 ```c #include <stdio.h> int f(int n) { if (n == 0) { // 终止条件 return 1; } else { // 递归步骤 return n * f(n - 1); } } int main() { printf("5! = %d\n", f(5)); // 输出120 printf("0! = %d\n", f(0)); // 输出1 return 0; } ``` #### 3. 关键点解析 | 功能点 | 实现方式 | |----------------|--------------------------------------------------------------------------| | 终止条件 | 当`n == 0`时返回1,结束递归调用链 | | 递归调用 | 通过`n * f(n-1)`实现自顶向下的计算过程 | | 时间复杂度 | $O(n)$,每个递归调用执行1次乘法操作 | #### 4. 递归调用过程演示 以计算$3!$为例: ``` f(3) → 3 * f(2) ↓ 2 * f(1) ↓ 1 * f(0) ↓ 1 最终结果:3*2*1*1=6 ``` #### 5. 注意事项 1. **输入范围限制**:当$n \geq 13$时,`int`类型会溢出(32系统最大值为$2^{31}-1$)[^2] 2. **法输入处理**:未对$n<0$的情况做校验,实际应用中需添加错误处理 3. **栈溢出风险**:当$n$极大时可能耗尽调用栈空间(一般系统栈深度约$10^4$层) #### 6. 学证明 递归正确性可通过学归纳法证明: - **基础情形**:$n=0$时$f(0)=1$成立 - **归纳假设**:假设$f(k)=k!$对所有$k < n$成立 - **归纳步骤**:$f(n) = n \times f(n-1) = n \times (n-1)! = n!$
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值