什麼是醜數呢?醜數就是隻包含因子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。現在我們來生成下一個醜數,該醜數肯定是前面某一個醜數乘以2、3或者5的結果。
我們首先考慮把已有的每個醜數乘以2。在乘以2的時候,能得到若干個結果小於或等於M的。由於我們是按照順序生成的,小於或者等於M肯定已經在數組中了,我們不需再次考慮;我們還會得到若干個大於M的結果,但我們只需要第一個大於M的結果,因爲我們希望醜數是按從小到大順序生成的,其他更大的結果我們以後再說。我們把得到的第一個乘以2後大於M的結果,記爲M2。
同樣我們把已有的每一個醜數乘以3和5,能得到第一個大於M的結果M3和M5。那麼下一個醜數應該是M2、M3和M5三個數的最小者。
<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左右。