寻找第k个丑数

把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

一个丑数C可以写成C=2X*3Y*5Z的形式(其中2X表示2的X次方)。

一,最初计算丑数的方法会想到用暴力的方法,就是从1到M判断每个数x是是不是丑数,一直累加到第N个丑数。在判断一个是数是不是丑数时使用如下方法:1,将x对2一直取余,直到不能取余;2,将余数对3一直取余,直到不能取余;3,将余数对5一直取余,直到不能取余;在1,2,3步骤中若取余的结果为1则结束,结果x是丑数,否则不是。

理论上这种方法是可以计算出第K个丑数的,但是实际上计算量会非常的大。

二,在得到一个丑数之后可以考虑从之前得到的丑数中得到下一个丑数。假设现在已知的十个丑数,那么第十一个丑数应该是在第十个丑数的基础上得到的。也就是前面十个丑数的某个数乘以2刚好大于第十个数(这里刚好大于第十个数的意思是说这个数前面的数乘以2并不大于第十个丑数),某个数乘以3刚好大于第十个数,某个数乘以5刚好大于第十个数,然后取这三个数中的最小值作为第十一个丑数。代码如下:

int num[k];
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;
}
这样就能极大的减小计算量。

三,但是在进一步的分析中发现,我们可以在获得最新的丑数时将其之前的状态记录下来,即使用动态规划的思想来获取第k个丑数。

int UglyNumber(int k){
    if(k<7)
       return k;
    int t2=0,t3=0,t5=0;
    vector<int> result(k);
    result[0]=1;
    for(int i=1;i<k;++i){
        result[i]=min(result[t2]*2,min(result[t3]*3,result[t5]*5));
        if(result[i]==result[t2]*2)
            t2++;
        if(result[i]==result[t3]*3)
            t3++;
        if(result[i]==result[t5]*5)
            t5++;
    }
    return result[k-1];
}
t2,t3,t5分别记录当前数组中与最末尾的丑数最接近的数(此处的最接近是指:t2最接近末尾丑数x则result[t2]*2最靠近x)。在比较时仅需要比较最靠近末尾丑数的三个值即可。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一杯拿铁go

你的打赏是我更新最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值