您的位置:首页 > 其它

动态规划 求第 n 个 Humble Number(丑数)

2018-03-23 01:35 267 查看

题目:

 质因数分解是将一个数分解为若干个质数相乘的形式,这些因数可以重复。比如 30 = 2×3×5,20 = 2×2×
5,81 = 3×3×3×3。现在我们将质因数分解之后只出现 2,3,5,7 的数称为 Humble Number,则 20,30,81 均为
Humble Number,而 11,22 不是。前 20 个 Humble Number 的序列为 1,2,3,4,5,6,7,8,9,10,12,14,15,16,18,
20,21,24,25,27。设计一个动态规划算法,求解第 n 个 Humble Number。

优化子结构及子问题重叠性

思考:动态规划问题的关键在于找到优化子结构,子结构的形式并不统一,另外个人认为同时还要考虑从子问题的重叠性来考虑,对于这题灵感就来源于子问题的重叠性。
设需要判断M是否为Humble Number,而且已经知道M之前所有的Humble Number,记为Humble_Number_Before_M,注意这是个数组。
首先,考虑的方向是如果知道Humble_Number_Before_M,那么,如何利用Humble_Number_Before_M(子问题)来判断M是否也是Humble Number,找出当前问题(数M)和子问题(Humble_Number_Before_M)的关系(优化子结构),问题就迎刃而解了。这个关系(优化子结构)并不一定是像“最长公共子序列”那样的递推关系式,但是,优化子结构的核心是:
“如果优化问题的解可以通过它的一系列子问题的解构造得到,则称该优化问题具有优化子结构”(P58)
                                                                                                                     --摘自《算法设计与分析》骆吉洲
那么数M可不可以利用它之前的Humble Number来求解(肯定不是指蛮力)呢?要知道,如果不能,那么只能是蛮力法,需要从1遍历到M/2来判断M中是否包含其他非2,3,5,7的因子,则当前的时间复杂度为O(M/2)。
 之前提到灵感来源于:考虑子问题的重叠性来源于哪里。我们知道,如果M(M>=7)是一个Humble Number,那么它的所有因子一定都来源于Humble_Number_Before_M(反证:如果M是Humble Number,且它的某个因子不在Humber_Number_Before_M中,那么与M是Humble Number 矛盾(Humble Number的定义))。例如,675是Humble Number,4725=625*7也是Humble Number,我们之前可能通过某种手段求出675是Humble Number,现在要判断4725是不是Humble Number还需要一步步先判断675是不是Humble Number吗?
分析到这,我们再多花点时间,一定能发现问题的关键之处。我们发现如果M满足:
M%2=0  AND  M/2  IN  Humble_Number_Before_M
或者
M%3=0  AND  M/3  IN  Humble_Number_Before_M
或者
M%5=0  AND  M/5  IN  Humble_Number_Before_M
或者
M%7=0  AND  M/7  IN  Humble_Number_Before_M
那么,M一定也是Humble Number(答案都给了,自己想),这样我们就充分利用了当前问题的子结构构造当前问题的解。
对于Humble_Numble_Before_M,我们可以使用关联容器Set(C++)进行存储,可以实现高效查找。下面是伪代码。define Set<int> Hash_Humble_Number(简写为HHN) //key值保存已经求出的所有Humble Number
HHN={1,2,3,4,5,6,7} //初始化前7个Humble Number
int Humble_Numer_N; //记录第n个Humble Number
index=8 //记录第index个Humble Number
i=8 //开始从i向后遍历
while(index<=n) Do
If ( (i%2=0 && HHN.find(i/2) = HHN.end())         //未找到key为i的元素,C++ set写法
|| (i%3=0 && HHN.find(i/3) = HNN.end())
|| (i%5=0 && HHN.find(i/5) = HNN.end())
|| (i%7=0 && HHN.find(i/7) = HNN.end())
) Then
HHN.insert(i); //插入第index个Humble Number i
Humble_Numer_N=i; ++index; //目前已经求到第index个
++i         //依次遍历,直到index=n,即找到第n个Humble Number
Return Humble_Numer_N                                        

时间复杂度

从伪代码中可以看出,循环结束的条件是找到第n个Humble Number M,那么时间复杂度为:
T(n)=O(M)

总结

如果一开始就看代码,可能看不出来这是动态规划,但是如果从头去想去做就能很清楚的知道。这大概就是看人家的代码和自己写代码的区别吧。把握问题的关键点,每一个程序都可能是动态规划。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: