编程之美系列之一——阶乘的运算
前言:
本人一直以来都对算法很有兴趣,前些日子拿到《编程之美》这本书,爱不释手,遂有意将书中的一些本人觉得较有意思的题目以及自己的心得拿出来与大家分享,共同讨论,共同进步。
需提前要说明的是,本系列文章中许多的问题都直接或间接来自《编程之美》一书。至于解法,则有的来自书中的讲解,有的是本人自己的领悟。
闲话不再多说,我们立刻开始。
问题描述:
- 给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N!=362800,N!的末尾有两个0;
- 求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的个数,然后求和。
代码如下:
解法二:
那么还有没有更简单点的方法呢?我们想,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] + …
代码如下:
问题2的求解:
分析:
首先我们来分析一个二进制数乘以2和除以2的过程和结果是怎么样。
一个二进制数乘以2就是把将此二进制数向左移一位,末位补零。除以2时,则要判断末位是否为0,若为0,向右移一位,若不能为0,则不能被2整除。
所以,其实本问题其实是求N!含有多少个2,最低位1的位置等于N!中含有2的个数加1。
代码如下:
为了检验以上算法的正确性,我运用字符串存储的方法计算出了任意数的阶乘(代码见附录),经过测验,以上算法计算结果正确。
附录:
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;
}
}