寻找丑数

寻找丑数
诺西笔试最后一道题,题意:
把只包含质因子2、3和5的数称作丑数(Ugly Number),例如:2,3,4,5,6,8,9,10,12,15,等,习惯上我们把1当做是第一个丑数。
写一个高效算法,返回第n个丑数。

 

最普通(也最耗时)的做法是从1开始遍历,然后判断这个数的因式分解中只包含2,3,5,满足则找到了一个,一直找下去,直到第n个被找出!测试了一下,找第1500个丑数耗时40秒!

 

我最开始的想法:开一个很大数组,然后把里面的素数和由素数生成的数都去掉,这样剩下的就是丑数了。这个方法弱爆了,比上面的还若,寻找300个丑数就要开启10000的数组,而且每次判断素数耗时巨大,代码都不要意思贴了;

 

以下是网络上找到的一个牛人的算法,其中的“竞争”思想真是精妙,是目前我看到的最简单的算法了

分析:假设数组ugly[N]中存放不断产生的丑数,初始只有一个丑数ugly[0]=1,由此出发,
下一个丑数由因子2,3,5竞争产生,得到ugly[0]*2, ugly[0]*3, ugly[0]*5, 显然最小的那个数是新的丑数,
所以第2个丑数为ugly[1]=2,开始新一轮的竞争,由于上一轮竞争中,因子2获胜,
这时因子2应该乘以ugly[1]才显得公平,得到ugly[1]*2,ugly[0]*3,ugly[0]*5, 因子3获胜,ugly[2]=3,
同理,下次竞争时因子3应该乘以ugly[1],即:ugly[1]*2, ugly[1]*3, ugly[0]*5, 因子5获胜,得到ugly[3]=5,
重复这个过程,直到第n个丑数产生。总之:每次竞争中有一个(也可能是两个)因子胜出,下一次竞争中 胜出的因子就应该加大惩罚!

程序如下所示(只要把程序中的因子改一下就可以得到新的题目),耗时忽略不计:
运行结果:第1500个丑数:859963392, 第1691个丑数2 125 764 000,第1692个丑数就越界了。
int表示的最大整数是2,147,483,647,可由std::cout<<(std::numeric_limits<int>::max)()<<"\n";给出!

#include <iostream>   
using namespace std;   
  
int mymin(int a, int b, int c)   
{   
    int temp = (a < b ? a : b);   
    return (temp < c ? temp : c);   
}   
int FindUgly(int n) //
{   
    int* ugly = new int[n];   
    ugly[0] = 1;   
    int index2 = 0;   
    int index3 = 0;   
    int index5 = 0;   
    int index = 1;   
    while (index < n)   
    {   
        int val = mymin(ugly[index2]*2, ugly[index3]*3, ugly[index5]*5); //竞争产生下一个丑数   
        if (val == ugly[index2]*2) //将产生这个丑数的index*向后挪一位;  
            ++index2;   
        if (val == ugly[index3]*3)   //这里不能用elseif,因为可能有两个最小值,这时都要挪动;
            ++index3;   
        if (val == ugly[index5]*5)   
            ++index5;   
        ugly[index++] = val;   
    }   

    //for (int i = 0; i < n; ++i)   
        //cout << i+1<<": "<<ugly[i] << endl;   

    int result = ugly[n-1];   
    delete[] ugly;   
    return result;   
}   
 
int main()   
{   
    int num=1;
      printf("input the number: \n");
    scanf("%d", &num);
    printf("%d \n",FindUgly(num)); 
	system("pause");
    return 0;   
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值