ACM_数论_阶乘N!的末尾有几个零



首先阶乘的一个常识要知道就是25!的末尾6位全是0;

前言:

《编程之美》这本书,爱不释手!

 

问题描述:

  1. 给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N!=362800,N!的末尾有两个0;
  2. 求N!的二进制表示中最低位1的位置。

 

问题1的求解:

分析:

解法一:

       首先,最直接的算法当然是直接求出来N!然后看末尾有几个0就行了。但这里存在两个问题:

     (1)不管使用long或者double一定会产生溢出。

     (2)效率低下。

      对于问题(1),我们可以采用字符串存储的办法解决,但问题(2)是由本身算法决定的,所以只能采用其他的算法。

那到底有没有更好的算法呢?我们来分析,N!能产生0的质数组合只能是2 * 5,也就是说当对N!进行质数分解之后,N!末尾0的个位M取决于2的个数X和5的个数Y的最小值,即M = min(X,Y)。又因为能被2整除的数出现的频率比能被5整除的数高得多,且出现一个5的时,最少会同时出现一个2,所以M = Y。即得出Y的值就可以得到N!末尾0的个数。

计算Y,最直接的方法,就是计算机1…N的因式分解中5的个数,然后求和。

     代码如下:

     ACM_数论_阶乘N!的末尾有几个零 - Essence - ACM_Of_Essence

 

 

解法二:

      那么还有没有更简单点的方法呢?我们想,Y还能怎么样得到?举个例子 25的阶乘中,总共有6个五,其中5,10,15,20,各贡献一个,25贡献两个,也可以说成,5,10,15,20,25各贡献一个,25又额外贡献 一个,即5的倍数各贡献一个5,25的倍数各贡献一个5,即Y=[25/5] + [25/25]。同理,125中,5的倍数各贡献一个5,25的倍数各贡献一个5,125的倍数也各贡献一个5,所以Y=[125/5] + [125/25] + [125/125],所以可得公式:

Y = [N/5] + [N/52] + [N/53] + …

     代码如下:

      ACM_数论_阶乘N!的末尾有几个零 - Essence - ACM_Of_Essence

 

 

问题2的求解:

分析:

     首先我们来分析一个二进制数乘以2和除以2的过程和结果是怎么样。

     一个二进制数乘以2就是把将此二进制数向左移一位,末位补零。除以2时,则要判断末位是否为0,若为0,向右移一位,若不能为0,则不能被2整除。

     所以,其实本问题其实是求N!含有多少个2,最低位1的位置等于N!中含有2的个数加1。

     代码如下:

     ACM_数论_阶乘N!的末尾有几个零 - Essence - ACM_Of_Essence

 

 

 

     为了检验以上算法的正确性,我运用字符串存储的方法计算出了任意数的阶乘(代码见附录),经过测验,以上算法计算结果正确。

 

 

附录:

import java.io.*;

import java.util.*;

 

/*

* 此类为计算任意数阶乘,采用大数相乘,大体算法为,将接收到两个数字字符串存放在两个链表中,每个链表的一个节点代表一位数字,

 * 链表的顺序是从后往前。然后分别用第二个链表的每一位数字乘以第一个链表的整体,再将得到的链表相加即为所求。

 *

 */

 

public class Fantorial

{

    public static void main(String[] args)

    {

       System.out.print("输入n: ");

      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

       try

       {  

           String s = br.readLine();   //

           String s = getResult (new Long(s).longValue());

           System.out.println(s);

       }

       catch(Exception e)

       {

           e.printStackTrace();

       }

    }

   

/*

 *计算阶乘

 */

    public static String getResult(long n)

    {

       if(n == 0)

           return "1";

       else

       {

       return BigCount.mul(new Long(n).toString(), getResult(n - 1));

       }  

    }

   

    public static String mul(String s1, String s2)

    {

       LinkedList<Integer> nl1,nl2;

       nl1 = stringToList(s1);

       nl2 = stringToList(s2);

       LinkedList<Integer> temp = mul(nl1, nl2);

       String s = listToString(temp);

       return s;

      

    }

   

    /*

     * 将链表转化成字符串

     */

    public static String listToString(LinkedList<Integer> nl)

    {

       StringBuilder s = new StringBuilder();

       for(int i = nl.size() - 1; i >= 0; i--)

       {

           s.append(nl.get(i));

       }  

       return s.toString();

    }

   

    /*

     * 将字符串转化成链表

     */

    public static LinkedList<Integer> stringToList(String s)

    {

       LinkedList<Integer> nl = new LinkedList<Integer>();

       //按从后到前存入链表

       for(int i=s.length()-1;i>=0;i--)

       {

           nl.add(Character.getNumericValue(s.charAt(i)));

       }

       return nl;

    }

   

    /*

     * 计算两链表相乘

     */

    public static LinkedList<Integer> mul(LinkedList<Integer> nl1,LinkedList<Integer> nl2)

    {

       LinkedList<Integer> temp = new LinkedList<Integer>();

       LinkedList<Integer> temp0 = null;

       temp.add(0);

       for(int i=0;i<nl1.size();i++)

       {

           temp0 = mul(nl2, nl1.get(i).intValue());

           //不同位的数字有不同的权值,如123中最后一个3就代表3,2代表20,1代表100

           for(int j=0;j<i;j++)

           {

              temp0 = mul(temp0, 10); 

           }

           temp = plus(temp ,temp0);

       }

       return temp;

    }

   

    /*

     * 计算一个链表和一个数相乘

     */

    public static LinkedList<Integer> mul(LinkedList<Integer> nl,int k)

    {

       int temp = 0;

       int x;

       LinkedList<Integer> tempList = new LinkedList<Integer>();

        for(int i=0;i<nl.size();i++)

       {

           x = nl.get(i).intValue() * k + temp;

           tempList.add(x % 10);

           temp = x / 10;

       }

       if(temp != 0)

           tempList.add(temp);

       return tempList;

    }

   

    /*

     * 计算机两链表相加

     */

    public static LinkedList<Integer> plus(LinkedList<Integer> nl1,LinkedList<Integer> nl2)

    {

       LinkedList<Integer> nl = new LinkedList<Integer>();

       LinkedList<Integer> tempList = new LinkedList<Integer>();

       int x;

       int temp = 0;

       int i;

       for(i=0;i<nl1.size() && i<nl2.size();i++)

       {

           x = nl1.get(i).intValue() + nl2.get(i).intValue() + temp;

           nl.add(x % 10);

           temp = x / 10;

       }

       if(i == nl1.size() && i == nl2.size())

       {

           if(temp != 0)

              nl.add(temp);

       }

       else if(i == nl1.size())

       {

           for(int j=i;j<nl2.size();j++)

           {

              tempList.add(nl2.get(j).intValue());

           }

           tempList = plus(tempList,temp);

           for(int j=0;j<tempList.size();j++)

           {

              nl.add(tempList.get(j).intValue());

           }

   

       }

       else if(i == nl2.size())

       {

           for(int j=i;j<nl1.size();j++)

           {

              tempList.add(nl1.get(j).intValue());

           }

           tempList = plus(tempList,temp);

           for(int j=0;j<tempList.size();j++)

           {

              nl.add(tempList.get(j).intValue());

           }

   

       }

       return nl;

    }

   

    /*

     * 计算一个链表和一个数字相加

     */

    public static LinkedList<Integer> plus(LinkedList<Integer> nl,int k)

    {

       int temp = k;

       int x;

       for(int i=0;i<nl.size();i++)

       {

           x = nl.get(i).intValue() + temp;

           nl.set(i,x % 10);

           temp = x / 10;

       }

       if(temp != 0)

           nl.add(temp);

       return nl;

    }

}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值