找到第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大的數組,浪費了6KB左右的內存,所以這是以空間換取時間的典型。

    通過測試,第一種方法的找到第1500個醜數大概需要50s左右(VS2013),而第二種方法只需要3s左右。















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章