日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

面试题之丑数的C++实现求解(孤陋寡闻了,才知道丑数这么high的东东)

發布時間:2023/12/20 c/c++ 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面试题之丑数的C++实现求解(孤陋寡闻了,才知道丑数这么high的东东) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

問題描述:

?? ? 我們把只包含因子2、35的數稱作丑數(Ugly Number)。例如68都是丑數,但14不是,因為它包含因子7。習慣上我們把1當做是第一個丑數。求按從小到大的順序的第1500個丑數。(昨天突然發現個不錯的博客:http://blog.csdn.net/v_JULY_v,突然知道丑數這個題,于是搜之)

?? ? 當然,最簡單的肯定是遍歷啊,想當年初學的時候,什么水仙花數,完數,質數,都遍歷搞定。遍歷存在的問題就是效率太低,如同暴力破密碼似的,以前用bt4破一個wep的有時候都要10多分鐘,破個WAP加密的半個小時,這不蛋疼嗎,破了就為蹭個網。像這個吧,到第1500個丑數的時候,用時就要42s多(win7+vc6),效率上肯定是有折扣的了,下面是代碼:

  • #include <iostream>
  • #include <climits>
  • using namespace std;
  • //遍歷法找丑數
  • int 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 1;
  • else
  • return 0;//not an ugly number
  • }
  • void GetUglyNumber(int index)
  • {//找到第index個丑數
  • int i , time =0 ;
  • if (index < 1)
  • {
  • cout << "error input " << endl;
  • exit(EXIT_FAILURE);
  • }
  • for (i=1 ; i< INT_MAX && time < index ; i++)
  • {
  • if ( IsUgly(i) )
  • {
  • time ++ ;
  • // cout << i << " " ;
  • }
  • }
  • cout << i-1 << endl;
  • }
  • int main()
  • {
  • int Number;
  • cout << "Input a number : " ;
  • cin >> Number ;
  • GetUglyNumber(Number);
  • return 0;
  • }
  • ?? ? ? ?遍歷法很大的問題在于對每個數都進行判斷,進行取余和除的運算了,如果換種思路的話,只對丑數進行計算呢?根據 http://www.cnblogs.com/mingzi/archive/2009/08/04/1538491.html 的思路,雖然從代碼上來看 http://www.cppblog.com/zenliang/articles/131094.html 的更簡潔易懂,不過第一個鏈接的變量命名會好很多,而且思路交代更清晰。

    ?? ? ? ?根據丑數的定義,丑數應該是另一個丑數乘以23或者5的結果(1除外)。因此我們可以創建一個數組,里面的數字是排好序的丑數。里面的每一個丑數是前面的丑數乘以23或者5得到的。那關鍵就是確保數組里的丑數是有序的了。我們假設數組中已經有若干個丑數,排好序后存在數組中。我們把現有的最大丑數記做M

    現在我們來生成下一個丑數,該丑數肯定是前面某一個丑數乘以2、3或者5的結果。

    我們首先考慮把已有的每個丑數乘以2。在乘以2的時候,能得到若干個結果小于或等于M的。

    由于我們是按照順序生成的,小于或者等于M肯定已經在數組中了,我們不需再次考慮;

    我們還會得到若干個大于M的結果,但我們只需要第一個大于M的結果,因為我們希望丑數是按從小到大順序生成的,其他更大的結果我們以后再說。

    我們把得到的第一個乘以2后大于M的結果,記為M2

    同樣我們把已有的每一個丑數乘以35,能得到第一個大于M的結果M3M5。那么下一個丑數應該是M2、M3M5三個數的最小者。(來自http://www.cnblogs.com/mingzi/archive/2009/08/04/1538491.html),則可以得到以下代碼:

  • #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;
  • }
  • /*
  • for (int i = 0; i < n; ++i)
  • cout << ugly[i] << endl;
  • //*/
  • 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;
  • }
  • ?? ? 代碼來自: http://www.cppblog.com/zenliang/articles/131094.html 。看到他的new,才想起,以前寫排序的時候,由于數組大小可變,直接用了vector,讓它直接去vector的size()就知道大小了,而沒有想到還有更初級的new,對于不定大小,new就好了啊,雖說new出來的是是在堆上,直接定義的是在棧上,不過用起來也是毫無影響的,果然自己還是太菜了點。
    ?? ? ?另外還可以采用的方法很多,參考 http://www.iteye.com/topic/832545 。本帖子列出了5種方法:

  • *?1.method1是最基礎的遍歷,唯一的優點估計就是簡單易懂。<br/>?
  • ?*?2.method2,method3的思想是先人工估算范圍值,將一定范圍內的值乘2,3,5排重增加,不同的地方在于method2重新遍歷,?
  • ?*?method3排序求下標<br/>?
  • ?*?3.method4的思想是將已經獲取的值分別遍歷,乘以2,3,5,當比最大值大就停止,比較這3個數的最小值,增加到定義的有序數組中。<br/>?
  • ?*?4.method5的思想是將數進行評估,評估出該數包含丑數的數量,當超過丑數要求數量時,進行2分法進行縮小范圍,直至求出解。?
  • ?? ? ?代碼直接參考,實際上,搜到的C++的代碼就是method1和method4,其實吧,method2和method3的精髓在于i?<?Integer.MAX_VALUE / 5 ,也是利用了所有丑數肯定是由丑數產生這一思想,雖然不同之處在于遍歷和求下標,不過總體是產生足夠大的丑數集合,再直接取需要的位置。C++實現如下: ???

  • #include <set>
  • #include <iostream>
  • #include <climits>
  • using namespace std;
  • const int MAX = INT_MAX/5;
  • void GetUgly(int Index)
  • {
  • int i;
  • set<int,less<int> > s;
  • set<int, less<int> >::iterator It;
  • s.insert(1);
  • for (i=1 ; i<MAX ; i++)
  • {
  • if (s.find(i) != s.end() )
  • {
  • s.insert(2*i) ;
  • s.insert(3*i) ;
  • s.insert(5*i) ;
  • }
  • }
  • for (It = s.begin() ,i=1 ; It != s.end() && i < Index; It++)
  • i++;
  • cout << *It << endl;
  • }
  • int main(int argc,char *argv[])
  • {
  • int Number;
  • cout << "Input a number : " ;
  • cin >> Number ;
  • GetUgly(Number);
  • return 0;
  • }
  • ?? ?說到這個,本打算用vector的,還用到了algorithm頭文件的find和sort。不過問題在于vector怎么刪除重復元素呢?哪怕加入是否在vector中的判斷,仍然難以阻止,效率不高。不過一不小心找到了STL的 set ,高級貨啊, set自動刪除重復元素 這一特性,還是很給力的。和Java的set一樣,不過這個算法的問題在于,直接將所有的丑數都找出來了,再取下標,在vc6和gcc測試下,速度著實很慢,莫非是C++STL的set不如Java的set高效么?這個方法讓我想到對于1000個數,找出其中最小的5個,但是將這1000個數都進行排序了再直接取前5個,雖然可行,但未免開銷太大,不經濟。運行的時候,等的時間太長,以至于直接關掉,將MAX換為2w,隨便測試了下對于100等數是否正確來判斷程序是否大致準確。

    ?? ?下面來改寫Java的method5為C++版本,代碼如下:

  • #include <iostream>
  • using namespace std;
  • int nums5(int val)
  • {
  • int n=0 ;
  • while (val >= 5)
  • {
  • n++ ;
  • val /= 5;
  • }
  • return n;
  • }
  • int nums35(int val)
  • {
  • int n=0 ;
  • while (val >= 3)
  • {
  • n += 1+nums5(val);
  • val /= 3;
  • }
  • return n;
  • }
  • //基于因數分解求出val以內有多少個丑數(不包含1)
  • int nums235(int val)
  • {
  • int n=0 ;
  • while (val >= 2)
  • {
  • n += 1+nums35(val);
  • val /= 2 ;
  • }
  • return n;
  • }
  • //用二分法查找第n個丑數
  • //對于X,如果X以內的丑數個數是n,而X-1以內的丑數個數是n-1,那么X就是第n個丑數
  • int numOfIndex(int n)
  • {
  • if(n == 1)
  • return 1;
  • n--;
  • int val1 = 1;
  • int nums1 = 0;
  • int val2 = 2;
  • int nums2 = nums235(val2); //nums2為val2的因數個數
  • while( nums2 < n )
  • {
  • val1 = val2;
  • nums1 = nums2;
  • val2 = val1*2;
  • nums2 = nums235(val2);
  • }
  • if( nums1 == n )
  • return val1;
  • if( nums2 == n )
  • return val2;
  • while(true)
  • {
  • long mid = (val1 + val2)/2;
  • int nums = nums235(mid);
  • if(val2 == mid+1 && nums == n-1 && nums2==n)
  • return val2;
  • if(mid == val1+1 && nums1 == n-1 && nums==n)
  • return mid;
  • if(nums >= n)
  • {
  • val2 = mid;
  • nums2 = nums;
  • }
  • else
  • {
  • val1 = mid;
  • nums1 = nums;
  • }
  • }
  • }
  • int check(int val)
  • {
  • long v = val;
  • while( v%2==0 )
  • v/=2;
  • while( v%3==0 )
  • v/=3;
  • while( v%5==0 )
  • v/=5;
  • if( v != 1 )
  • cout << " v is not an ugly number! " << endl;
  • return val;
  • }
  • void calc(int n)
  • {
  • long val = numOfIndex(n);
  • cout << n << " : " << val << endl;;
  • check(val);
  • }
  • int main(int argc ,char *argv[])
  • {
  • int Number;
  • cout << "Please input a number : " ;
  • cin >> Number ;
  • calc(Number);
  • return 0;
  • }
  • ?? ? ? ? 想不到這算法很是高級貨啊,直接因數分解,其實也是充分利用丑數是由丑數產生這一原理,用nums235統計出val內丑數個數。雖然也是都大量計算,不過比第一種的好很多,加上引入二分查找,效率還是不錯的。經過測試,與method4在1500的時候都能在5ms內完成,各有所長。不過有個不足的地方, http://www.iteye.com/topic/832545 雖然說這方法是最優解(如果在calc中去掉check調用,1500-1545都是1ms或2ms完成,震驚啊),不過在輸入1546開始,會很慢,更不用說在1692這樣會溢出的點,會很慢(沒等,不知道具體時間)不過在1545以內,的確是最優,作者 taolei0628 果然牛。

    ?? ? ? ? ?總結起來,就是最簡陋的遍歷,從小到大的只算丑數,統計全部丑數,計算丑數個數,方法不同,算起來,搞程序還是很有意思的嘛,可惜沒早點發現,就這樣了吧。

    ?? ? ? ? ?菜鳥goes on ~~

    總結

    以上是生活随笔為你收集整理的面试题之丑数的C++实现求解(孤陋寡闻了,才知道丑数这么high的东东)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。