1,求n!的末尾0的个数,如5!=120,0的个数为1。

       首先想到的是直接计算出n!的结果,然后再对结果进行求余和求整,最后得到想要的结果。但是假设n是一个足够大的数,那么n!的结果也是一个无限大的数。这样就超出int的范围,甚至long的范围。当n足够大时,此方法无效。

       如果n!=k*10M,(0!=k%10),那么M就是想要的结果。同时把n!进行质解,n!=2X*3Y*5Z...,由于10=2*5,那么n!的末尾0的个数就是min(X,Z),显然n!分解之后2的个数比5多。这样,M==Z。根据分析,要计算 Z,最直接的方法,就是计算i(i =1, 2, …, N)的因式分解中5 的指数,然后求和。

public int numberOfZero(int n){
     int num=0,temp;
     for(int i=5;i<=n;i+=5){
           temp=i;
           while(temp%5==0){
                 num++;
                 temp=temp/5;
            }
     }
     return num;
}

      公式:Z = [N/5] +[N/52] +[N/53] + …(不用担心这会是一个无穷的运算,因为总存在一个K,使得5K > N,[N/5K]=0)。公式中,[N/5]表示不大于N 的数中5 的倍数贡献一个5,[N/52]表示不大于N 的数中52的倍数再贡献一个5...。基于这一点,还可以再优化一下。

//第一种方法
public int numberOfZero(int n){
    int num=0;
    while(n!=0){
          num+=n/5;
          n=n/5;
     }
}

//第二种方法
public int numberOfZero(int n){
     int num=0;
     for(int i=5;i<=n;i*=5){
          num+=n/i;
     }
     return num;
}

2,求N!的二进制表示中最低位1的位置。如3!=6,二进制为1010,所以最低位1的位置在第二位。

其实就是求二进制末尾0的个数,由于是二进制,所以每次乘以2末尾就会产生一个0。于是,转换为求N!含有多少个2的个数。

public int numberOfTwo(int n){
     int num=0;
     while(n!=0){
          num+=n/2;
          n=n/2;
     }
     return num;
}

//除以 n / 2 == n >> 1。不过位运算的速度快多了
public int numberOfTwo(int n){
      int num=0;
      while(n!=0){
           n>>=1;
           num+=n;
       }
       return num;
}

public int locationOfLastOne(int n){
      int numer=numberOfTwo(n)+1;
      return numer;
}

3,一个数N,输出N!的值

两个大数一般用字符串来处理。

public String multiply(String s1,String s2){
      //将字符串转换为字符数组
      char[] c1=s1.toCharArray();
      char[] c2=s2.toCharArray();
      //新建数组,用来存放两个数的积,字符的初始值为'\u0000',也就是0
      int len=c1.length+c2.length;
      char[] c=new char[len];
      //整数的低位放在字符串的末尾,从末尾遍历计算
      for(int i=c1.length-1;i>=0;i--){
           int index=len-1;
           //用来存放进位
           int carry=0;
           for(int j=c2.length-1;j>=0;j--){
                //当前两个数的乘积加上原数组对应的数加上进位
                int temp=(c1[i]-'0')*(c2[j]-'0')+c[index]+carry;
                carry=temp/10;
                c[index--]=(char)(temp%10);
           }
           c[index]=(char)carry;
           len--;
      }
      //把c中的字符加上'0'
      for(int i=0;i<c.length;i++){
           c[i]+='0';
      }
      String s=new String(c);
      //n位数乘以m位数得到积的位数为(m+n)位数或者(m+n-1)位
      //判断c[0]是否为0,为0则把它舍弃
      if(c[0]=='0'){
           return s.substring(1);
      }else{
           return s;
      }
}
public String factorial(int n){
      String s="1";
      for(int i=2;i<=n;i++){
           s=multiply(s,i+"");
      }
      return s;
}

 

 posted on 2019-03-19 19:55  会飞的金鱼  阅读(351)  评论(0)    收藏  举报