编程之美系列之一——阶乘的运算
前言:
本人一直以来都对算法很有兴趣,前些日子拿到《编程之美》这本书,爱不释手,遂有意将书中的一些本人觉得较有意思的题目以及自己的心得拿出来与大家分享,共同讨论,共同进步。
需提前要说明的是,本系列文章中许多的问题都直接或间接来自《编程之美》一书。至于解法,则有的来自书中的讲解,有的是本人自己的领悟。
闲话不再多说,我们立刻开始。
问题描述:
- 给定一个整数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;
}
}
编程之美系列之一——阶乘的运算相关推荐
- 编程之美系列之三——计算1的个数
问题描述: 给定一个十进制整数N,求出从1到N的所有整数中出现"1"的个数. 例如:N=2,1,2出现了1个"1". N=12,1,2,3,4,5,6,7,8 ...
- 编程之美----不要被阶乘吓到
任一个正整数都能分解成质数的连乘,因此求N!末尾有多少个0,等于质因数分解之后5的个数,而求5的个数可以用如下代码实现: ret =0; while(N) {ret += N/5;N/=5; } Vi ...
- 编程之美系列之二——寻找出现频率超过一半的数
问题描述: 现在有一数组存放int型整数,数字有重复,且有一数字出现的频率超过了50%,请找出这个数字. 补充:主要考虑数据量很大的情况. 问题求解: 分析: 最直接的方法就是对数组中所有的数字排序, ...
- 学习思考之《编程之美》.
一.智者说:无聊的时候来几道算法题,可以训练训练自己的思维嘛!难怪之前人家说数学好的人编程起来事半功倍,写算法的过程中真是深有体会啊!感觉就像是在做大学的高数题......本博文仅用来记录自己学习算法 ...
- 【编程之美】一摞烙饼的排序
一,问题: 星期五的晚上,一帮同事在希格玛大厦附近的"硬盘酒吧"多喝了几杯.程序员多喝了几杯之后谈什么呢?自然是算法问题.有个同事说:&qu ...
- 编程之美计算0到N中包含数字1的个数
转自:http://blog.csdn.net/hongjuntu123/article/details/8743266 有这样一个函数f(n),对于任意正整数n,它表示从 0 到 n 之间出现&qu ...
- 数学之美 系列十 有限状态机和地址识别
数学之美 系列十 有限状态机和地址识别 地址的识别和分析是本地搜索必不可少的技术,尽管有许多识别和分析地址的方法,最有效的是有限状态机. 一个有限状态机是一个特殊的有向图(参见有关图论的系列),它包括 ...
- 数学之美 系列九 -- 如何确定网页和查询的相关性
数学之美 系列九 -- 如何确定网页和查询的相关性 [我们已经谈过了如何自动下载网页.如何建立索引.如何衡量网页的质量(Page Rank).我们今天谈谈如何确定一个网页和某个查询的相关性.了解了这四 ...
- 数学之美系列五 -- 简单之美:布尔代数和搜索引擎的索引
数学之美系列五 -- 简单之美:布尔代数和搜索引擎的索引 [建立一个搜索引擎大致需要做这样几件事:自动下载尽可能多的网页:建立快速有效的索引:根据相关性对网页进行公平准确的排序.我们在介绍 Googl ...
最新文章
- 如何从JavaScript中的给定数字中形成最小的数字
- pt100 c 语言,PT100计算公式 C程序.doc
- ORA-00060 Deadlock detected
- C#刷遍Leetcode面试题系列连载(4): No.633 - 平方数之和
- Nginx学习之四-Nginx进程同步方式-自旋锁(spinlock)
- spring中怎么访问MySQL过程_DB数据源之SpringBoot+MyBatis踏坑过程(六)mysql中查看连接,配置连接数量...
- 内存cgroup---CGroup中参数由来篇
- Python –将NumPy数组转换为列表
- 每日一句20191105
- 【随笔】hi3531D 音频
- Spring的事务传播机制(大白话)
- 洛菲创意字体设计分享--绿斗堂字体网
- The RSpec Book笔记《四》Describing Code with RSpec用RSpec描述代码
- JSch连接SFTP Exception:Algorithm negotiation fail问题解决
- 光大祖业 奉子成婚——SAS与SATA-Ⅱ专题
- SUCTF_2019部分题解复现
- java打字小游戏_java实现打字游戏小程序
- pytorch学习第三天: 阿里云物联网平台使用
- tomcat 启动有报错,但是很快就自动关闭了,太快了看不到信息,解决方法
- 计算机蓝屏无法启动代码50,电脑蓝屏代码0x0000002E/3F/44/50的原因与解决方法