前言:
相信很多朋友们都对“丑数”有所了解,当然肯定也有人觉得这个名字好奇怪,咦,什么样的数才算是丑数咧?实际上丑数就是只包括2,3,5这三种因子的数,另外我们一般把“1”当作第一个丑数。
知道了丑数,我们很容易发现,丑数有点像质数一样,总是没有最大的。不过丑数的寻找可比质数的寻找简单得多啦,那让我们来试一试找出从小到大排序的第1500个丑数吧。
解法一:
既然知道了这个丑数的判断方法,也就是任意给我们一个自然数,我们可以判断它究竟是不是丑数。方法当然就是把这个数 a 先不断除以2,直到有余数,恢复一步,得到 x;再把 x 不断除以3,直到有余数,恢复一步,得到 y ;最后把 y 不断除以5,如果最后余数为 1 ,就说明这个数
a 是丑数,余数不为 1 则说明 a 包含其他除了2,3,5的因子。
看一看代码:
int number = 1500; //要求输出丑数的数
int index = 1; //数字本身
while(number > 0){
int temp = index;
while(temp%2 == 0)temp = temp/2;
while(temp%3 == 0)temp = temp/3;
while(temp%5 == 0)temp = temp/5;
if(temp == 1)
{
System.out.println(index+"number="+number);
number--;
}
index++;
}
但是,但是,我们发现运行以上代码,电脑就开始不停地算了~~~~算啊算啊算啊算,差不多要几十秒才能得到我们想要的数。这是为什么呢?噢,看一下我们得到的那个第1500个丑数就明白了,859963392。。。好大。。。
也就是说,我们的计算机从1开始一直判断所有的数是不是丑数,一直判断到我们这个 8 亿多这么大的这个数。。。原来人家计算机几十秒算了这么多东西在此,我想崇敬地说一句“计算机,您辛苦了(*^__^*) ”
可是,这样计算机太可怜了,一定有什么方法帮帮她~~~~哎,对噢,判断需要判断8亿次,但是丑数只有1500个,如果我们能只做1500次操作而不是8亿次操作,效率自将大升。我们可以看看能不能直接计算出丑数,也就是说,给出现有的丑数,我们来找出下一个丑数,可以吗?当然啦~
解法二:
赶紧来试一试。不过假设给了我们从1开始的10个丑数,怎么找出第11个呐?等等,诶,丑数不是只由2,3,5构成的嘛,而且我们拿到的数也是由2,3,5构成的,而且是最小的10个,那么第11个一定就比前十个数中的某一个多一个因子2,或者因子3,或者因子5……也就是说,第11个丑数一定是前十个丑数中某一个丑数的2倍,或者3倍,或者5倍!!
这个很关键,再仔细想一想噢。现在知道了第11个丑数一定是前十个丑数中某一个丑数的2倍,或者3倍,或者5倍,还是不行呢,那到底是2倍还是3倍还是5倍呢o(︶︿︶)o。。。
想起来啦,我们的第十一个丑数一定是,大于第十个丑数的所有丑数中,最小的!!!那就是说,我们可以在第一到第十个丑数中找出某个丑数 a ,它的两倍刚好大于第十个丑数(刚好的意思就是,上一个数的两倍还小于第十个丑数,而它自己的两倍却大于第十个丑数了);然后再找出前十个丑数中的三倍刚好大于第十个丑数的家伙 b ;最后找出前十丑数中某丑数5倍刚好大于第十个丑数的丑数
c ~~~2*a,3*b,5*c,找出这三个中最小的就行啦(*^__^*)
来来来,看看代码咯:
int num[]=new int[1501];
num[0] = 0;
num[1] = 1;
int two = 0,three = 0,five = 0;//刚好大于现有最大丑数的三个
for(int i = 1; i <= 1499; i++)
{//每次找出一个丑数
for(int j = 1; j <= i ; j++)//从头向后扫描,若某数的两倍刚好大于上回找出的丑数,将它值记录下来
if((2*num[j-1] <= num[i])&&(2*num[j] > num[i])) two=num[j]*2;
for(int j = 1; j <= i ; j++)//从头向后扫描,若某数的三倍刚好大于上回找出的丑数,将它值记录下来
if((3*num[j-1] <= num[i])&&(3*num[j] > num[i])) three=num[j]*3;
for(int j = 1; j <= i ; j++)//从头向后扫描,若某数的五倍刚好大于上回找出的丑数,将它值记录下来
if((5*num[j-1] <= num[i])&&(5*num[j] > num[i])) five=num[j]*5;
//比较出三个数中最小的
if(two>=three&&five>=three) num[i+1] = three;
else if(two>=five&&three>=five) num[i+1] = five;
else if(five>=two&&three>=two) num[i+1] = two;
}
System.out.println("第1500个丑数 = "+num[1500]);
赶紧去跑一跑,有没有发现这速度比前一条快的不是一点半点哈哈,瞬间得到结果有木有
后话:
我后来继续思索这个问题,隐隐约约感觉到还有更优的方法。因为丑数不是仅由2,3,5构成嘛,所以从理论上出发,完全可以自己用2,3,5自主构建出从小到大的丑数。我感觉比如拿到一个丑数,它的2,3,5的构成个数就一定了,那应该可以推断出下一个丑数的2,3,5的构成。如果能完成这个功能,其效率肯定比解法二更优。
不过理论成立,实际不太可行,因为虽然知道某丑数2,3,5构成,下一个数的2,3,5构成确实应该是一定的,但是我们没办法通过简单的算法计算得到。
所以实现此方法的唯一方式只能通过人为映射(就是相当于打表)。而这样的映射的复杂性增加速度是很恐怖的,所以这样的方法看来确实没有实现的可能了。
当然这样的思路也提供了另一种方法,比如某丑数的2,3,5因子数分别为x,y,z ,那么下一个数的2,3,5因子数必将在0—(x+y*y+z*z*z),0—(x+y+z*z),0—(sqrt(x)+y+z)范围内,由此推断下一个。好吧,这个方法也麻烦的可以,不过我就想告诉大家,算法题的解法总是很多样化的,大家一定要亲自去探索,很可能获取一些意外收获噢(*^__^*)
分享到:
相关推荐
《程序员算法趣题——随书源码》是一个与算法相关的学习资源,包含了增井敏克著作《程序员算法趣题》中的实例代码。增井敏克是算法领域知名的专家,他的书籍通常深入浅出,旨在帮助程序员提升算法思维和解决实际问题...
《算法设计经典题——王晓东编著(第三版)程序流程图加实验报告》是一份极其宝贵的资源,针对想要深入理解和掌握算法设计的初学者。这份资料包含了王晓东教授在《计算机算法设计与分析》第三版中精选的各章习题,...
综上所述,本书《计算机算法——设计与分析导论》通过介绍算法的基本概念、分析算法的方式、使用数学背景来描述算法性能,以及提供具体的算法实例,为读者提供了一个全面了解计算机算法设计与分析的框架。...
数据结构与算法分析——Java语言描述(第二版)是普林斯顿大学Mark Allen Weiss的经典之作,但是网上很少能找到Java描述第二版的课后习题,连作者的个人主页也明确表示不提供课后习题,只能到出版商那里去索取,这个...
经典算法题每日演练——第十五题 并查集 经典算法题每日演练——第十四题 Prim算法 经典算法题每日演练——第十三题 赫夫曼树 经典算法题每日演练——第十二题 线段树 经典算法题每日演练——第十一题 Bitmap算法 ...
《算法设计与分析》是计算机科学领域的一本经典教材,主要涵盖了算法的设计方法、分析技巧以及实际应用。这本书由陈慧南编著的第三版,提供了全面且深入的算法学习资源,尤其对于学习者来说,课后习题的答案是深化...
文本比较算法Ⅰ——LD算法.doc
编程的都知道这是经典之作,就不多说了,还是CHM的 读起来很方便!
机器学习决策树算法中特征选项的算法实现——信息熵 首先我们将信息熵的定义进行阐述: 熵经验熵 我们这里以网上数据贷款申请为例:数据来自(https://blog.csdn.net/c406495762/article/details/75663451) 在...
"Halcon直线检测算法——卡尺算法"是一个专门用于检测图像中直线的自定义方法,由用户根据实际需求开发。这个算法在工业自动化、质量控制等领域有着重要的应用,因为它可以帮助精确地定位和测量工件的特征。 直线...
在"经典聚类算法——K-Means算法实现(C#,适用于初学者)"中,提供的C#源码应该是一个控制台应用程序,它通过读取数据、执行上述步骤并输出结果来展示K-Means算法的工作原理。初学者可以通过这个程序理解K-Means...
《算法竞赛入门经典——训练指南》是一本深受编程爱好者和算法竞赛选手欢迎的书籍,由刘汝佳编著。这本书旨在帮助读者系统地学习和掌握基础及进阶算法,为参与算法竞赛或提升编程能力提供实用指导。代码仓库包含书中...