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
浙公网安备 33010602011771号