找到第N个丑数

         什么是丑数呢?丑数就是只包含因子2,3,5的数称作是丑数,在这里这种定义我是比较模糊的,所以上网查找了更加浅显的定义,丑数就是另一个丑数乘以2,3,5以后的结果(1除外,通常认为1是最小的丑数)。

       通常,最简单的方法肯定是遍历数组,然后确定每一个数是否是丑数,这种方法是最简单并且直观的。

<span style="font-family:Microsoft YaHei;font-size:14px;">bool IsUgly(int num)
{
	while (num % 2 == 0)
		num /= 2;
	while (num % 3 == 0)
		num /= 3;
	while (num % 5 == 0)
		num /= 5;
	if (num == 1)
		return true;
	else
		return false;
}

int GetNUgly(int n)
{
	if (n <= 0)
		return 0;
	int count = 0;
	int num = 0;
	while (count < n)
	{
		num++;
		if (IsUgly(num))
		{
			count++;
		}
	}
	return num;
}</span>

        使用模除法判断一个数是不是丑数的时间效率是O(n*log n)。我们需要更加高效的算法。


根据丑数的定义,一个丑数必定是由另外一个丑数得来。这就是提升效率的地方,我们在上一种方法中浪费了太多的时间在判断不是丑数的数的上面,我们优化的地方就是,使用一个数组,里面放都是丑数,数组后面的数必定是前面的数乘以2,3,5得来的,关键就是确保数组里的丑数是有序的,

以下是构建有序丑数序列的算法

   我们把现有的最大丑数记做M。现在我们来生成下一个丑数,该丑数肯定是前面某一个丑数乘以23或者5的结果。

   我们首先考虑把已有的每个丑数乘以2。在乘以2的时候,能得到若干个结果小于或等于M的。由于我们是按照顺序生成的,小于或者等于M肯定已经在数组中了,我们不需再次考虑;我们还会得到若干个大于M的结果,但我们只需要第一个大于M的结果,因为我们希望丑数是按从小到大顺序生成的,其他更大的结果我们以后再说。我们把得到的第一个乘以2后大于M的结果,记为M2

   同样我们把已有的每一个丑数乘以35,能得到第一个大于M的结果M3M5。那么下一个丑数应该是M2M3M5三个数的最小者。

<span style="font-family:Microsoft YaHei;font-size:14px;">#include <iostream>     
using namespace std;     
    
int Min(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 = Min(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;     
    }     

</span>
<span style="font-family:Microsoft YaHei;font-size:14px;">    int result = ugly[n-1];     
    delete[] ugly;     
    return result;     
}     
int main()     
{     
    int num;  
    cout << "input the number : " ;  
    cin >> num;  
    cout << FindUgly(num) << endl;  
    return 0;     
}  </span>

        与一种方法相比,这种算法不会在非丑数上浪费时间,效率肯定提升,但是,凡是有利有弊,次算法需要new出一个1500大的数组,浪费了6 KB左右的内存,所以这是以空间换取时间的典型。

    通过测试,第一种方法的找到第1500个丑数大概需要50s左右(VS2013),而第二种方法只需要3s左右。















  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值