青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

大龍的博客

常用鏈接

統計

最新評論

從一道筆試題談算法優化(上)

聲明:本文最初發表于《電腦編程技巧與維護》2006年第5期,版本所有,如蒙轉載,敬請連此聲明一起轉載,否則追究侵權責任。
從一道筆試題談算法優化(上)

作者:賴勇浩(http://blog.csdn.net/lanphaday
引子

每年十一月各大IT公司都不約而同、爭后恐后地到各大高校進行全國巡回招聘。與此同時,網上也開始出現大量筆試面試題;網上流傳的題目往往都很精巧,既能讓考查基礎知識,又在平淡中隱含了廣闊的天地供優秀學生馳騁。

這兩天在網上淘到一道筆試題目(注1),雖然真假未知,但的確是道好題,題目如下:

       從10億個浮點數中找出最大的1萬個。

這是一道似易實難的題目,一般同學最容易中的陷阱就是沒有重視這個“億”字。因為有10億個單精度浮點數元素的數組在32位平臺上已經達到3.7GB之巨,在常見計算機平臺(如Win32)上聲明一個這樣的數組將導致堆棧溢出。正確的解決方法是分治法,比如每次處理100萬個數,然后再綜合起來。不過這不是本文要討論的主旨,所以本文把上題的10億改為1億,把浮點數改為整數,這樣可以直接地完成這個問題,有利于清晰地討論相關算法的優化(注2)。
不假思索

拿到這道題,馬上就會想到的方法是建立一個數組把1億個數裝起來,然后用for循環遍歷這個數組,找出最大的1萬個數來。原因很簡單,因為如果要找出最大的那個數,就是這樣解決的;而找最大的1萬個數,只是重復1萬遍而已。

template< class T >

void solution_1( T BigArr[], T ResArr[] )

{

       for( int i = 0; i < RES_ARR_SIZE; ++i )

       {

              int idx = i;

              for( int j = i+1; j < BIG_ARR_SIZE; ++j )

              {

                     if( BigArr[j] > BigArr[idx] )

                            idx = j;

              }

              ResArr[i] = BigArr[idx];

              std::swap( BigArr[idx], BigArr[i] );

       }

}

設BIG_ARR_SIZE = 1億,RES_ARR_SIZE = 1萬,運行以上算法已經超過40分鐘(注3),遠遠超過我們的可接受范圍。
稍作思考

從上面的代碼可以看出跟SelectSort算法的核心代碼是一樣的。因為SelectSort是一個O(n^2)的算法(solution_1的時間復雜度為O(n*m),因為solution_1沒有將整個大數組全部排序),而我們又知道排序算法可以優化到O(nlogn),那們是否可以從這方面入手使用更快的排序算法如MergeSor、QuickSort呢?但這些算法都不具備從大至小選擇最大的N個數的功能,因此只有將1億個數按從大到小用QuickSort排序,然后提取最前面的1萬個。

template< class T, class I >

void solution_2( T BigArr[], T ResArr[] )

{

       std::sort( BigArr, BigArr + BIG_ARR_SIZE, std::greater_equal() );

       memcpy( ResArr, BigArr, sizeof(T) * RES_ARR_SIZE );

}

因為STL里的sort算法使用的是QuickSort,在這里直接拿來用了,是因為不想寫一個寫一個眾人皆知的QuickSort代碼來占篇幅(而且STL的sort高度優化、速度快)。

對solution_2進行測試,運行時間是32秒,約為solution_1的1.5%的時間,已經取得了幾何數量級的進展。
深入思考

壓抑住興奮回頭再仔細看看solution_2,你將發現一個大問題,那就是在solution_2里所有的元素都排序了!而事實上只需找出最大的1萬個即可,我們不是做了很多無用功嗎?應該怎么樣來消除這些無用功?

如果你一時沒有頭緒,那就讓我慢慢引導你。首先,發掘一個事實:如果這個大數組本身已經按從大到小有序,那么數組的前1萬個元素就是結果;然后,可以假設這個大數組已經從大到小有序,并將前1萬個元素放到結果數組;再次,事實上這結果數組里放的未必是最大的一萬個,因此需要將前1萬個數字后續的元素跟結果數組的最小的元素比較,如果所有后續的元素都比結果數組的最小元素還小,那結果數組就是想要的結果,如果某一后續的元素比結果數組的最小元素大,那就用它替換結果數組里最小的數字;最后,遍歷完大數組,得到的結果數組就是想要的結果了。

template< class T >

void solution_3( T BigArr[], T ResArr[] )

{

       //取最前面的一萬個

       memcpy( ResArr, BigArr, sizeof(T) * RES_ARR_SIZE );

       //標記是否發生過交換

       bool bExchanged = true;

       //遍歷后續的元素

       for( int i = RES_ARR_SIZE; i < BIG_ARR_SIZE; ++i )

       {

              int idx;

              //如果上一輪發生過交換

              if( bExchanged )

              {

                     //找出ResArr中最小的元素

                     int j;

                     for( idx = 0, j = 1; j < RES_ARR_SIZE; ++j )

                     {

                            if( ResArr[idx] > ResArr[j] )

                                   idx = j;

                     }

              }

              //這個后續元素比ResArr中最小的元素大,則替換。

              if( BigArr[i] > ResArr[idx] )

              {

                     bExchanged = true;

                     ResArr[idx] = BigArr[i];

              }

              else

                     bExchanged = false;

       }

}

上面的代碼使用了一個布爾變量bExchanged標記是否發生過交換,這是一個前文沒有談到的優化手段——用以標記元素交換的狀態,可以大大減少查找ResArr中最小元素的次數。也對solution_3進行測試一下,結果用時2.0秒左右(不使用bExchanged則高達32分鐘),遠小于solution_2的用時。
深思熟慮

在進入下一步優化之前,分析一下solution_3的成功之處。第一、solution_3的算法只遍歷大數組一次,即它是一個O(n)的算法,而solution_1是O(n*m)的算法,solution_2是O(nlogn)的算法,可見它在本質上有著天然的優越性;第二、在solution_3中引入了bExchanged這一標志變量,從測試數據可見引入bExchanged減少了約99.99%的時間,這是一個非常大的成功。

上面這段話絕非僅僅說明了solution_3的優點,更重要的是把solution_3的主要矛盾擺上了桌面——為什么一個O(n)的算法效率會跟O(n*m)的算法差不多(不使用bExchanged)?為什么使用了bExchanged能夠減少99.99%的時間?帶著這兩個問題再次審視solution_3的代碼,發現bExchanged的引入實際上減少了如下代碼段的執行次數:

for( idx = 0, j = 1; j < RES_ARR_SIZE; ++j )

{

       if( ResArr[idx] > ResArr[j] )

              idx = j;

}

上面的代碼段即是查找ResArr中最小元素的算法,分析它可知這是一個O(n)的算法,到此時就水落石出了!原來雖然solution_3是一個O(n)的算法,但因為內部使用的查找最小元素的算法也是O(n)的算法,所以就退化為O(n*m)的算法了。難怪不使用bExchanged使用的時間跟solution_1差不多;這也從反面證明了solution_3被上面的這一代碼段導致性能退化。使用了bExchanged之后因為減少了很多查找最小元素的代碼段執行,所以能夠節省99.99%的時間!

至此可知元兇就是查找最小元素的代碼段,但查找最小元素是必不可少的操作,在這個兩難的情況下該怎么去優化呢?答案就是保持結果數組(即ResArr)有序,那樣的話最小的元素總是最后一個,從而省去查找最小元素的時間,解決上面的問題。但這也引入了一個新的問題:保持數組有序的插入算法的時間復雜度是O(n)的,雖然在這個問題里插入的數次比例較小,但因為基數太大(1億),這一開銷仍然會令本方案得不償失。

難道就沒有辦法了嗎?記得小學解應用題時老師教導過我們如果解題沒有思路,那就多讀幾遍題目。再次審題,注意到題目并沒有要求找到的最大的1萬個數要有序(注4),這意味著可以通過如下算法來解決:

1)            將BigArr的前1萬個元素復制到ResArr并用QuickSort使ResArr有序,并定義變量MinElemIdx保存最小元素的索引,并定義變量ZoneBeginIdx保存可能發生交換的區域的最小索引;

2)            遍歷BigArr其它的元素,如果某一元素比ResArr最小元素小,則將ResArr中MinElemIdx指向的元素替換,如果ZoneBeginIdx == MinElemIdx則擴展ZoneBeginIdx;

3)            重新在ZoneBeginIdx至RES_ARR_SIZE元素段中尋找最小元素,并用MinElemIdx保存其它索引;

4)            重復2)直至遍歷完所有BigArr的元素。

依上算法,寫代碼如下:

template< class T, class I >

void solution_4( T BigArr[], T ResArr[] )

{

       //取最前面的一萬個

       memcpy( ResArr, BigArr, sizeof(T) * RES_ARR_SIZE );

       //排序

       std::sort( ResArr, ResArr + RES_ARR_SIZE, std::greater_equal() );

       //最小元素索引

       unsigned int MinElemIdx = RES_ARR_SIZE - 1;

       //可能產生交換的區域的最小索引

       unsigned int ZoneBeginIdx = MinElemIdx;

       //遍歷后續的元素

       for( unsigned int i = RES_ARR_SIZE; i < BIG_ARR_SIZE; ++i )

       {    

              //這個后續元素比ResArr中最小的元素大,則替換。

              if( BigArr[i] > ResArr[MinElemIdx] )

              {

                     ResArr[MinElemIdx] = BigArr[i];

                     if( MinElemIdx == ZoneBeginIdx )

                            --ZoneBeginIdx;

                     //查找最小元素

                     unsigned int idx = ZoneBeginIdx;

                     unsigned int j = idx + 1;

                     for( ; j < RES_ARR_SIZE; ++j )

                     {

                            if( ResArr[idx] > ResArr[j] )

                                   idx = j;

                     }

                     MinElemIdx = idx;

              }

       }

}

經過測試,同樣情況下solution_4用時約1.8秒,較solution_3效率略高,總算不負一番努力。

 

待續……

posted on 2010-04-05 22:59 大龍 閱讀(293) 評論(1)  編輯 收藏 引用

評論

# re: 從一道筆試題談算法優化(上) 2010-05-11 09:32 小時候可靚了

看了一下,大概算法就是,抽出1W個數,然后搜索最小值和接下來的數比較,如果最小值比這個數小,則用這個數替換原來較小的數。 并不需要排序,因為結果沒要求有序。   回復  更多評論   


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美精品一区二区高清在线观看| 亚洲欧洲日韩在线| 亚洲人永久免费| 亚洲综合第一页| 9国产精品视频| 在线日韩中文| 日韩一本二本av| 日韩亚洲精品视频| 亚洲精品乱码久久久久久黑人| 国产在线观看精品一区二区三区| 一本色道久久综合亚洲精品不| 亚洲国产欧美在线| 亚洲毛片在线看| 99精品免费网| 亚洲欧美另类在线| 久久久国产精品一区二区三区| 欧美中在线观看| 男人插女人欧美| 欧美激情区在线播放| 欧美粗暴jizz性欧美20| 欧美特黄一级| 国产精品jizz在线观看美国| 免费毛片一区二区三区久久久| 久久精品国产视频| 欧美激情综合在线| 国产欧美日韩综合精品二区| 国产午夜精品全部视频播放 | 国产精品久久久久高潮| 欧美精品18videos性欧美| 欧美视频官网| 国产精品一二三四区| 国产日韩欧美在线观看| 欧美一级专区免费大片| 午夜精品视频一区| 久久午夜色播影院免费高清| 久久先锋资源| 国产精品久久福利| 亚洲精品一区二区三区av| 性色av香蕉一区二区| 欧美激情亚洲激情| 欧美在线视频一区| 国产精品爽爽ⅴa在线观看| 亚洲精品国产精品国自产观看| 久久aⅴ国产欧美74aaa| 在线日本欧美| 欧美一级视频精品观看| 亚洲日本激情| 免费欧美视频| 亚洲高清资源综合久久精品| 欧美呦呦网站| 亚洲午夜高清视频| 国产精品a久久久久| 亚洲精品一区二区三区樱花| 老牛影视一区二区三区| 亚洲性夜色噜噜噜7777| 欧美午夜电影一区| 亚洲一卡久久| 在线视频你懂得一区二区三区| 欧美激情精品久久久久久黑人| 亚洲黄色影院| 亚洲国产精品久久久久婷婷884 | 欧美电影在线观看| 国产午夜亚洲精品不卡| 亚洲一区二区日本| 亚洲美女视频网| 久久综合成人精品亚洲另类欧美 | 在线免费观看欧美| 久久久久久国产精品一区| 亚洲欧美综合v| 国产农村妇女毛片精品久久莱园子 | 久久久亚洲人| 久久伊人精品天天| 亚洲美女视频在线免费观看| 91久久久久久| 欧美婷婷久久| 午夜在线播放视频欧美| 亚洲欧美日韩在线不卡| 国产欧美日韩亚洲精品| 久久青青草原一区二区| 久久精品亚洲| 亚洲欧洲在线播放| 99精品99久久久久久宅男| 欧美日韩国产一区精品一区 | 国产欧美日韩另类视频免费观看| 亚洲女性裸体视频| 亚洲欧美精品在线| 国产一级一区二区| 久久久青草青青国产亚洲免观| 久久免费视频一区| 一区二区三区免费网站| 亚洲欧美激情一区二区| 在线日韩av片| 亚洲在线视频观看| 亚洲人成人77777线观看| 夜夜嗨av一区二区三区| 国产一区三区三区| 日韩午夜激情| 亚洲大胆美女视频| 亚洲免费视频观看| 亚洲精品女av网站| 欧美一区三区三区高中清蜜桃 | 亚洲激情亚洲| 在线视频一区观看| 在线看日韩av| 亚洲女同同性videoxma| 亚洲精品久久久久久久久| 一区二区三区精品在线| 国模吧视频一区| 亚洲高清激情| 国产一区在线看| 一本大道久久a久久综合婷婷| 国产一区二区三区在线观看免费视频 | 亚洲综合精品自拍| 欧美一区二区三区日韩视频| 亚洲免费高清视频| 久久人人看视频| 亚洲一二三区在线| 欧美精品一区二区蜜臀亚洲| 亚洲一区美女视频在线观看免费| 久久女同互慰一区二区三区| 亚洲欧美在线aaa| 欧美啪啪一区| 亚洲高清一区二区三区| 在线电影国产精品| 性欧美1819性猛交| 欧美一区=区| 欧美性大战久久久久久久蜜臀| 欧美激情精品久久久久久久变态| 国产无一区二区| 亚洲综合视频网| 久久xxxx精品视频| 国产欧美亚洲一区| 亚洲欧美综合精品久久成人| 亚洲欧美精品在线观看| 欧美天堂亚洲电影院在线观看| 亚洲精品网站在线播放gif| 日韩一区二区精品葵司在线| 免费成人黄色片| 欧美成在线视频| 亚洲激情黄色| 欧美精品粉嫩高潮一区二区| 欧美福利视频网站| 久久精品欧美| 久久亚洲国产精品一区二区| 国产一区视频在线观看免费| 久久久高清一区二区三区| 香蕉成人久久| 国产一二精品视频| 久久久久久久综合| 免费高清在线一区| 亚洲国产欧美国产综合一区| 美女精品在线观看| 亚洲高清在线观看| 亚洲小视频在线| 国产精品专区一| 先锋影音久久| 欧美激情一区在线观看| 一区二区三区高清不卡| 国产精品久久久久久五月尺| 先锋影音久久| 亚洲电影在线看| 亚洲一区二区三区精品在线观看| 国产精品免费看久久久香蕉| 久久se精品一区精品二区| 欧美激情精品久久久| 亚洲一级黄色av| 有码中文亚洲精品| 欧美视频免费在线观看| 久久国产精品72免费观看| 亚洲国产欧美日韩精品| 午夜欧美理论片| 91久久精品一区| 国产精品日本一区二区| 久久综合九色99| 亚洲午夜av电影| 亚洲国产日韩一区| 久久国内精品视频| 亚洲精品日产精品乱码不卡| 国产精品啊v在线| 久热精品视频在线观看| 一区二区欧美在线观看| 嫩模写真一区二区三区三州| 亚洲午夜激情网站| 亚洲高清免费视频| 国产日韩一区二区三区在线| 欧美激情按摩在线| 久久久久国内| 午夜天堂精品久久久久| 亚洲免费观看高清在线观看 | 一区二区三区欧美| 欧美激情2020午夜免费观看| 香蕉av福利精品导航| 在线亚洲美日韩| 亚洲欧洲在线一区| 在线播放中文一区| 国产精品一区视频| 国产精品久久久久久久久免费 | 亚洲国产三级网| 国产原创一区二区| 国产精品一二一区|