• <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>

            那誰(shuí)的技術(shù)博客

            感興趣領(lǐng)域:高性能服務(wù)器編程,存儲(chǔ),算法,Linux內(nèi)核
            隨筆 - 210, 文章 - 0, 評(píng)論 - 1183, 引用 - 0
            數(shù)據(jù)加載中……

            二分查找學(xué)習(xí)札記

            我之前寫過兩篇關(guān)于二分查找算法的文章,這篇算是一個(gè)小結(jié).我給這份文檔整理了一份pdf格式的文檔,可以在這里下載.


            二分查找算法學(xué)習(xí)札記
            說明
            作者:那誰(shuí)
            blog: http://www.shnenglu.com/converse
            轉(zhuǎn)載請(qǐng)注明出處.
            二分查找算法基本思想
            二分查找算法的前置條件是,一個(gè)已經(jīng)排序好的序列(在本篇文章中為了說明問題的方便,假設(shè)這個(gè)序列是升序排列的),這樣在查找所要查找的元素時(shí),首先與序列中間的元素進(jìn)行比較,如果大于這個(gè)元素,就在當(dāng)前序列的后半部分繼續(xù)查找,如果小于這個(gè)元素,就在當(dāng)前序列的前半部分繼續(xù)查找,直到找到相同的元素,或者所查找的序列范圍為空為止.

            用偽代碼來表示, 二分查找算法大致是這個(gè)樣子的:
            left = 0, right = n -1
            while (left <= right)
                mid 
            = (left + right) / 2
                
            case
                    x[mid] 
            < t:    left = mid + 1;
                    x[mid] 
            = t:    p = mid; break;
                    x[mid] 
            > t:    right = mid -1;

            return -1;


            第一個(gè)正確的程序
            根據(jù)前面給出的算法思想和偽代碼, 我們給出第一個(gè)正確的程序,但是,它還有一些小的問題,后面會(huì)講到
            int search(int array[], int n, int v)
            {
                
            int left, right, middle;

                left 
            = 0, right = n - 1;

                
            while (left <= right)
                {
                    middle 
            = (left + right) / 2;
                    
            if (array[middle] > v)
                    {
                        right 
            = middle;
                    }
                    
            else if (array[middle] < v)
                    {
                        left 
            = middle;
                    }
                    
            else
                    {
                        
            return middle;
                    }
                }

                
            return -1;
            }


            下面,講講在編寫二分查找算法時(shí)可能出現(xiàn)的一些問題.

            邊界錯(cuò)誤造成的問題
            二分查找算法的邊界,一般來說分兩種情況,一種是左閉右開區(qū)間,類似于[left, right),一種是左閉右閉區(qū)間,類似于[left, right].需要注意的是, 循環(huán)體外的初始化條件,與循環(huán)體內(nèi)的迭代步驟, 都必須遵守一致的區(qū)間規(guī)則,也就是說,如果循環(huán)體初始化時(shí),是以左閉右開區(qū)間為邊界的,那么循環(huán)體內(nèi)部的迭代也應(yīng)該如此.如果兩者不一致,會(huì)造成程序的錯(cuò)誤.比如下面就是錯(cuò)誤的二分查找算法:
            int search_bad(int array[], int n, int v)
            {
                
            int left, right, middle;

                left 
            = 0, right = n;

                
            while (left < right)
                {
                    middle 
            = (left + right) / 2;
                    
            if (array[middle] > v)
                    {
                        right 
            = middle - 1;
                    }
                    
            else if (array[middle] < v)
                    {
                        left 
            = middle + 1;
                    }
                    
            else
                    {
                        
            return middle;
                    }
                }

                
            return -1;
            }

            這個(gè)算法的錯(cuò)誤在于, 在循環(huán)初始化的時(shí)候,初始化right=n,也就是采用的是左閉右開區(qū)間,而當(dāng)滿足array[middle] > v的條件是, v如果存在的話應(yīng)該在[left, middle)區(qū)間中,但是這里卻把right賦值為middle - 1了,這樣,如果恰巧middle-1就是查找的元素,那么就會(huì)找不到這個(gè)元素.
            下面給出兩個(gè)算法, 分別是正確的左閉右閉和左閉右開區(qū)間算法,可以與上面的進(jìn)行比較:
            int search2(int array[], int n, int v)
            {
                
            int left, right, middle;

                left 
            = 0, right = n - 1;

                
            while (left <= right)
                {
                    middle 
            = (left + right) / 2;
                    
            if (array[middle] > v)
                    {
                        right 
            = middle - 1;
                    }
                    
            else if (array[middle] < v)
                    {
                        left 
            = middle + 1;
                    }
                    
            else
                    {
                        
            return middle;
                    }
                }

                
            return -1;
            }

            int search3(int array[], int n, int v)
            {
                
            int left, right, middle;

                left 
            = 0, right = n;

                
            while (left < right)
                {
                    middle 
            = (left + right) / 2;

                    
            if (array[middle] > v)
                    {
                        right 
            = middle;
                    }
                    
            else if (array[middle] < v)
                    {
                        left 
            = middle + 1;
                    }
                    
            else
                    {
                        
            return middle;
                    }
                }

                
            return -1;
            }


            死循環(huán)
            上面的情況還只是把邊界的其中一個(gè)寫錯(cuò), 也就是右邊的邊界值寫錯(cuò), 如果兩者同時(shí)都寫錯(cuò)的話,可能會(huì)造成死循環(huán),比如下面的這個(gè)程序:
            int search_bad2(int array[], int n, int v)
            {
                
            int left, right, middle;

                left 
            = 0, right = n - 1;

                
            while (left <= right)
                {
                    middle 
            = (left + right) / 2;
                    
            if (array[middle] > v)
                    {
                        right 
            = middle;
                    }
                    
            else if (array[middle] < v)
                    {
                        left 
            = middle;
                    }
                    
            else
                    {
                        
            return middle;
                    }
                }

                
            return -1;
            }

            這個(gè)程序采用的是左閉右閉的區(qū)間.但是,當(dāng)array[middle] > v的時(shí)候,那么下一次查找的區(qū)間應(yīng)該為[middle + 1, right], 而這里變成了[middle, right];當(dāng)array[middle] < v的時(shí)候,那么下一次查找的區(qū)間應(yīng)該為[left, middle - 1], 而這里變成了[left, middle].兩個(gè)邊界的選擇都出現(xiàn)了問題, 因此,有可能出現(xiàn)某次查找時(shí)始終在這兩個(gè)范圍中輪換,造成了程序的死循環(huán).

            溢出
            前面解決了邊界選擇時(shí)可能出現(xiàn)的問題, 下面來解決另一個(gè)問題,其實(shí)這個(gè)問題嚴(yán)格的說不屬于算法問題,不過我注意到很多地方都沒有提到,我覺得還是提一下比較好.
            在循環(huán)體內(nèi),計(jì)算中間位置的時(shí)候,使用的是這個(gè)表達(dá)式:
            middle = (left + right) / 2;

            假如,left與right之和超過了所在類型的表示范圍的話,那么middle就不會(huì)得到正確的值.
            所以,更穩(wěn)妥的做法應(yīng)該是這樣的:
            middle = left + (right - left) / 2;

            更完善的算法
            前面我們說了,給出的第一個(gè)算法是一個(gè)"正確"的程序, 但是還有一些小的問題.
            首先, 如果序列中有多個(gè)相同的元素時(shí),查找的時(shí)候不見得每次都會(huì)返回第一個(gè)元素的位置, 比如考慮一種極端情況:序列中都只有一個(gè)相同的元素,那么去查找這個(gè)元素時(shí),顯然返回的是中間元素的位置.
            其次, 前面給出的算法中,每次循環(huán)體中都有三次情況,兩次比較,有沒有辦法減少比較的數(shù)量進(jìn)一步的優(yōu)化程序?
            <<編程珠璣>>中給出了解決這兩個(gè)問題的算法,結(jié)合前面提到溢出問題我對(duì)middle的計(jì)算也做了修改:
            int search4(int array[], int n, int v)
            {
                
            int left, right, middle;

                left 
            = -1, right = n;

                
            while (left + 1 != right)
                {
                    middle 
            = left + (right - left) / 2;

                    
            if (array[middle] < v)
                    {
                        left 
            = middle;
                    }
                    
            else
                    {
                        right 
            = middle;
                    }
                }

                
            if (right >= n || array[right] != v)
                {
                    right
            = -1;
                }

                
            return right;
            }

            這個(gè)算法是所有這里給出的算法中最完善的一個(gè),正確,精確且效率高.
            參考資料
            1.<<編程珠璣>>
            2.wiki上關(guān)于二分查找的說明:http://en.wikipedia.org/wiki/Binary_search




            posted on 2009-10-05 21:53 那誰(shuí) 閱讀(14300) 評(píng)論(22)  編輯 收藏 引用 所屬分類: 算法與數(shù)據(jù)結(jié)構(gòu)

            評(píng)論

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            哈哈,這個(gè)總結(jié)真好~
            2009-10-06 00:35 | 唐僧

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            orz..我就經(jīng)常2分的時(shí)候orz
            2009-10-06 12:08 | Vincent

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            再orz一個(gè)..第一次來到您的blog..超贊啊
            2009-10-06 12:09 | Vincent

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            真這么神奇?我一直以為二分查找很簡(jiǎn)單啊!太打擊我了!我簡(jiǎn)直是菜鳥中再也不能菜的了...........
            2009-10-06 16:52 | 浮生如夢(mèng)

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            受教了 O(∩_∩)O哈!
            2009-10-07 08:19 | 遲到的愛

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            測(cè)試了下。。。。最后的代碼效率不高。
            2009-10-07 10:31 | 飯中淹

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @飯中淹
            哦?怎么測(cè)試的?
            2009-10-07 10:59 | 那誰(shuí)

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @那誰(shuí)
            大量隨機(jī)數(shù)值的查找。
            用我自己的二分算法和最后的算法進(jìn)行比較測(cè)試。

            結(jié)果是最后的算法比我的算法慢個(gè)幾百毫秒。

            是10萬(wàn)int數(shù)組,1萬(wàn)預(yù)生成的待查數(shù)據(jù)。

            重復(fù)1000次后得出的時(shí)間。
            我的算法大概是1.8S
            你文中的算法是2.XS
            RELEASE版,在2.6GHZ的INTEL CPU上。

            為了排除緩沖區(qū)干擾等,我用自己的算法的變種進(jìn)行過測(cè)試,相差不到10毫秒。



            2009-10-08 07:51 | 飯中淹

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @飯中淹
            可以把你的算法貼出來嗎?
            2009-10-08 10:09 | 那誰(shuí)

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @那誰(shuí)
            http://www.shnenglu.com/johndragon/archive/2008/04/17/47345.html
            在這里。
            2009-10-08 14:58 | 飯中淹

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @飯中淹
            我這邊測(cè)試的結(jié)果是我的稍好一些的,測(cè)試的數(shù)據(jù)是這樣的:

            #define LEN 400000

            int main()
            {
            int array[LEN];
            int i;

            for (i = 0; i < LEN; ++i)
            {
            array[i] = i;
            }

            xbinary_search<int, int> search;

            for (i = 0; i < LEN / 1000; ++i)
            {
            search.search_value(array, LEN, i * 10);
            }
            printf("done123\n");

            return 0;
            }
            這個(gè)測(cè)試數(shù)據(jù), 你的表現(xiàn)是0.176s,我的是0.047.

            另外,你的代碼在我的cygwin上面的g++上不能直接編譯過去,我稍作了一些修改.
            2009-10-08 15:51 | 那誰(shuí)

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @飯中淹
            2-way和3-way的對(duì)比,通常和輸入有關(guān)。
            3-way一旦命中,就可以提前退出循環(huán), 但每次循環(huán)要多一次compare。
            2-way總是執(zhí)行l(wèi)gN次比較后才退出循環(huán),再測(cè)試是否命中。
            嚴(yán)格計(jì)算需要概率統(tǒng)計(jì)知識(shí)。
            這種蠻力測(cè)試我也做過, 3-way通常表現(xiàn)得比2-way更優(yōu)秀。


            但2-way有一個(gè)決定性的優(yōu)勢(shì),是3-way無(wú)法相比的。
            "存在相同鍵的有序區(qū)間中查找鍵屬于某個(gè)區(qū)間的所有值",對(duì)這種需求,2-way可以繼續(xù)保持O(lgN)的復(fù)雜度, 而3-way會(huì)退化至O(N)。


            stl中使用2-way而不是3-way,可能就是基于這種考慮吧 —— 即使在最壞情況下也要足夠好, 在最好的情況也不算壞。
            2009-10-08 16:23 | OwnWaterloo

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @OwnWaterloo
            是的,你這么分析有道理.所以最后的那個(gè)算法"精確"(都找到相同元素的第一個(gè))的代價(jià)就是在一些情況下不如3-way高效.
            2009-10-08 16:30 | 那誰(shuí)

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            綜上,我想這篇文章還需要做一些說明.稍后我會(huì)補(bǔ)充上,謝謝幾位.
            2009-10-08 16:35 | 那誰(shuí)

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            好久沒來了,貌似這回的論題是算法效率了。
            2-way/3-way的二分算法都是基于無(wú)冗余的比較運(yùn)算的,理論比較次數(shù)的下限不會(huì)達(dá)到log(2,n)。而
            ”3-way一旦命中,就可以提前退出循環(huán), 但每次循環(huán)要多一次compare。
            2-way總是執(zhí)行l(wèi)gN次比較后才退出循環(huán),再測(cè)試是否命中。“
            考慮到測(cè)試數(shù)據(jù)的生成方法,那么3-way的表現(xiàn)會(huì)好一些,結(jié)果就是大量的數(shù)據(jù)面前差距會(huì)被會(huì)放大。
            但是如果附加大量無(wú)法命中的搜索,那么理論上二者差距會(huì)趨近于零。
            我印象中數(shù)學(xué)方法分析算法效率的時(shí)候大概的過程是:寫出匯編代碼,看單次表現(xiàn)的指令數(shù),數(shù)學(xué)求和。感覺不是很困難的事情,無(wú)非就是給一個(gè)所在位置n所需要搜索次數(shù)s的函數(shù),對(duì)0..max求和看平均,但是運(yùn)算量上看卻是很麻煩的事情,特別是當(dāng)這個(gè)函數(shù)比較復(fù)雜的時(shí)候。。

            ps,在上次討論的時(shí)候那個(gè)uniform二分的表現(xiàn)要好于3-way很多,我當(dāng)時(shí)測(cè)了一萬(wàn)左右的數(shù)據(jù),因?yàn)椤眲?dòng)態(tài)“尋找二分點(diǎn)要比預(yù)先生成靜態(tài)二分點(diǎn)多增加很多運(yùn)算,而且這種增加是隨著搜索空間的增大而增長(zhǎng)的(這句話我不太確定,直覺上一倍左右的差距)。這也從另一方面說明了二分算法本身比較運(yùn)算的運(yùn)算量已經(jīng)沒有提高的空間了,優(yōu)化僅僅是從周邊入手,比如3-way的用一次比較的代價(jià)來減少循環(huán)的次數(shù)。

            再ps,剛才寫上一段的時(shí)候想起來通用指令集cpu的設(shè)計(jì),尤其以intel為首,通過加長(zhǎng)流水線,提高分支預(yù)測(cè)的效率來提高性能,一旦預(yù)測(cè)失敗就會(huì)有比較大的性能損失。這和精簡(jiǎn)指令集的cpu的區(qū)別有點(diǎn)像3-way和2-way的區(qū)別,一個(gè)強(qiáng)調(diào)命中,一個(gè)強(qiáng)調(diào)穩(wěn)定的結(jié)果。理念非常接近。
            2009-10-09 08:11 | 唐僧

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            再補(bǔ)充一點(diǎn),翻了一下編程藝術(shù),mix結(jié)構(gòu)上的匯編
            3-way的每個(gè)循環(huán)的操作數(shù)大概是15個(gè)
            uniform在12個(gè)左右
            3個(gè)的差距在于取二分點(diǎn)的操作上面,占到了1/4。
            沒有2-way的實(shí)現(xiàn),不過可以猜測(cè)每個(gè)循環(huán)的操作在13或者14個(gè)上。
            那么這對(duì)于最終算法的影響確實(shí)是相當(dāng)大的,因?yàn)榧兇庥糜诒容^兩數(shù)大小的操作在一個(gè)循環(huán)中不過1/2左右。
            2009-10-09 08:21 | 唐僧

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @那誰(shuí)
            我是這樣測(cè)試的,不知道有么有錯(cuò)。
            第一步:將你的算法嵌入到我的binarysearch里面
            static inline int _search( const T1 * pTArray, int nArraySize, const T2 & v, int & insertpoint )
            {
            if( pTArray == NULL )
            {
            insertpoint = 0;
            return -1;
            }

            int left, right, middle;

            left = -1, right = nArraySize;

            while (left + 1 != right)
            {
            middle = left + (right-left) / 2;

            if (_cmp( pTArray[middle], v ) < 0)
            {
            left = middle;
            }
            else
            {
            right = middle;
            }
            }

            if (right >= nArraySize || _cmp( pTArray[right], v ) != 0)
            {
            right = -1;
            }

            return right;
            //}

            }
            第二步:測(cè)試函數(shù)
            #define MAX_DATA_COUNT 100000
            int gDatas[MAX_DATA_COUNT];
            #define MAX_FIND_DATA_COUNT 10000
            int gFindDatas[MAX_FIND_DATA_COUNT];
            #define MAX_TEST_TIMES 1000
            void Test1()
            {
            typedef xbinary_search<int, int> INT_SEARCH;
            for( int t = 0;t < MAX_TEST_TIMES; ++t )
            {
            for( int i = 0;i < MAX_FIND_DATA_COUNT; ++i )
            {
            int nPos = INT_SEARCH::search_value( gDatas, MAX_DATA_COUNT, gFindDatas[i] );
            if( nPos != gFindDatas[i] )printf( "error !\n" );
            }
            }
            }

            void Test2()
            {
            typedef xbinary_search2<int, int> INT_SEARCH;
            for( int t = 0;t < MAX_TEST_TIMES; ++t )
            {
            for( int i = 0;i < MAX_FIND_DATA_COUNT; ++i )
            {
            int nPos = INT_SEARCH::search_value( gDatas, MAX_DATA_COUNT, gFindDatas[i] );
            if( nPos != gFindDatas[i] )printf( "error !\n" );
            }
            }
            }

            void Test3()
            {
            typedef xbinary_search3<int, int> INT_SEARCH;
            for( int t = 0;t < MAX_TEST_TIMES; ++t )
            {
            for( int i = 0;i < MAX_FIND_DATA_COUNT; ++i )
            {
            int nPos = INT_SEARCH::search_value( gDatas, MAX_DATA_COUNT, gFindDatas[i] );
            if( nPos != gFindDatas[i] )printf( "error !\n" );
            }
            }
            }

            xbinary_search3就是你的函數(shù)嵌入的

            第三步:數(shù)據(jù)生成和測(cè)試代碼
            srand( 312431 );
            for( int i = 0;i < MAX_DATA_COUNT; ++i )
            {
            gDatas[i] = i;
            }

            for( int i = 0;i < MAX_FIND_DATA_COUNT; ++i )
            {
            gFindDatas[i] = (rand() % 10000) * MAX_DATA_COUNT / 10000;
            }
            timeBeginPeriod(1);
            DWORD dwT1 = timeGetTime();
            Test1();
            dwT1 = timeGetTime() - dwT1;
            printf( "t1 = %u\n", dwT1 );
            DWORD dwT2 = timeGetTime();
            Test2();
            dwT2 = timeGetTime() - dwT2;
            printf( "t2 = %u\n", dwT2 );
            DWORD dwT3 = timeGetTime();
            Test3();
            dwT3 = timeGetTime() - dwT3;
            printf( "t3 = %u\n", dwT3 );
            timeEndPeriod(1);

            最后結(jié)果


            t1 = 1828
            t2 = 1822
            t3 = 2155


            2009-10-09 10:03 | 飯中淹

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            查找i*10的結(jié)果是:

            t1 = 1159
            t2 = 1160
            t3 = 1515

            你的算法的消耗時(shí)間仍然多幾百毫秒

            2009-10-09 10:07 | 飯中淹

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @唐僧

            對(duì)對(duì),我忘了說了,如果查找目標(biāo)不在區(qū)間中,2-way肯定比3-way高效。


            -------- 關(guān)于 uniform --------
            "因?yàn)椤眲?dòng)態(tài)“尋找二分點(diǎn)要比預(yù)先生成靜態(tài)二分點(diǎn)多增加很多運(yùn)算,而且這種增加是隨著搜索空間的增大而增長(zhǎng)的(這句話我不太確定,直覺上一倍左右的差距)。"

            上次我就說了嘛, 關(guān)于uniform這個(gè)詞。
            如果只從源代碼(維基上的那份)入手uniform優(yōu)化的實(shí)質(zhì)就預(yù)處理一個(gè)delta,每次免去除法運(yùn)算。
            不管高爺爺是怎么一步一步想到這里來的(那本書我沒看……), 源代碼中已經(jīng)不怎么能體現(xiàn)出來了。


            但實(shí)際上,效果并不一定很顯著, 我瞎猜的啊。
            因?yàn)槌龜?shù)是2, 實(shí)際上并不需要做除法, 只是移位。

            -------- 注意 --------
            一次移位的寫法有(但不限于)以下2種:
            int m = lower + ( (upper - lower) >> 1);
            int m = lower + (upper - lower) / 2u; /* 這個(gè)u很關(guān)鍵 */

            第1種使用sar,第2種使用shr。 只要upper>=lower, sar,shr都是正確的。

            而樓主和飯中淹使用的代碼
            int m = lower + (upper - lower) /2;
            會(huì)被翻譯為3條指令。
            飯中淹的代碼還沒有考慮溢出的情況。

            -------- 注意完畢 --------


            那么,不使用uniform的方式, 每次計(jì)算middle的消耗就是:
            減法,移位,加法。
            lower, upper都是緩存在寄存器中的。
            具體可以見上一篇文章的評(píng)論中我發(fā)的匯編代碼。


            而uniform的方式:
            i += delta[++d];
            大概就是
            mov r1, delta[r2(d)*4+4]
            add r3(i),r1

            一次加法;一次內(nèi)存訪問;d需要多占用一個(gè)寄存器(否則更慢),++d還需要要一次加法。


            這個(gè)靜態(tài)存儲(chǔ)區(qū)的內(nèi)存訪問很討厭。
            非uniform方式lower,upper所在頁(yè)面肯定是被加載的,因?yàn)樗鼈兪呛瘮?shù)參數(shù),處在棧頂。
            uniform的方式,delta就不一定在頁(yè)面中,有可能被置換出去了。這種情況一旦發(fā)生,說不定效率就是數(shù)量級(jí)的下降,和非uniform完全沒得比。
            并且,這種情況,通過這種小的測(cè)試程序還不一定能測(cè)得出來 —— 怎么模擬一個(gè)大的、長(zhǎng)期運(yùn)行的、偶爾調(diào)用二分查找的程序,它已經(jīng)將delta所在頁(yè)面置換出去了?

            從本質(zhì)上來說, uniform的方式的一個(gè)直接缺陷就是造成數(shù)據(jù)的局部性不如非uniform的方式。而且這缺陷是無(wú)法修正的。 缺陷造成的影響可能會(huì)被uniform的其他優(yōu)勢(shì)所掩蓋, 但缺陷始終存在。


            當(dāng)然, 上面全都是臆測(cè),臆測(cè)。 需要實(shí)際測(cè)試一下。
            我覺得只要非uniform的注意移位問題, 效率應(yīng)該不會(huì)比uniform的差太多。即使不缺頁(yè),內(nèi)存訪問也是比較傷的。
            一旦缺頁(yè),我覺得uniform就沒什么可比性的了。



            ps:
            關(guān)于兩種方式的對(duì)比:
            1. 增加happy path的效率,不管unfortunate的效率
            2. 兼顧所有情況的效率

            除了uniform和非uniform,我還想起一個(gè)……
            不知道hash表和紅黑樹算不算這種對(duì)比的例子之一……
            2009-10-09 15:27 | OwnWaterloo

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @OwnWaterloo
            我確實(shí)理解錯(cuò)了,因?yàn)閙ix結(jié)構(gòu)上不能實(shí)現(xiàn)右移這樣的操作……所以有了uniform。
            而且我也沒有考慮到實(shí)際實(shí)際內(nèi)存的分配情況。如果仔細(xì)考慮一下就能想到,這么多年來保持下來的,成為標(biāo)準(zhǔn)的,大量應(yīng)用的是非uniform的二分,說明了實(shí)際應(yīng)用要好的多。
            stl里面的set/map都應(yīng)該用的是紅黑樹吧,還有2-way的二分,都是兼顧所有情況的。hash表也挺極端,這個(gè)例子也挺像。呵呵,合適最好。先搞實(shí)現(xiàn),在追求優(yōu)化。
            2009-10-09 22:37 | 唐僧

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            @唐僧

            mix沒有右移?
            不過嘛…… 右移是除法的子集…… 沒有也挺合理的…

            嗯,在你介紹之前, 我完全不知道有uniform這回事……
            高爺爺?shù)臅袝r(shí)間一定得去看看……

            sgi的stl中有非公開的紅黑樹實(shí)現(xiàn)。4中關(guān)聯(lián)容器其實(shí)是紅黑樹的adapter...
            就像std::stack,queue,priority_queue一樣…… 尷尬……

            我覺得吧,二分只要寫正確就行了…… 復(fù)雜度肯定是lgN的。
            lgN已經(jīng)很猛了…… 常數(shù)稍微差點(diǎn)也壞不到哪去…… 優(yōu)化是無(wú)底洞……
            不記得在哪本書上看到過這么一句話"只需要讓軟件足夠快"
            2009-10-10 14:16 | OwnWaterloo

            # re: 二分查找學(xué)習(xí)札記  回復(fù)  更多評(píng)論   

            沒有一個(gè)人認(rèn)真看樓主的貼的算法,search()和search_bad2()是一模一樣的錯(cuò)誤算法,我跪了!!!
            2013-04-08 10:21 | quatrejuin
            久久精品国产亚洲AV不卡| 中文字幕精品无码久久久久久3D日动漫 | 狠狠精品久久久无码中文字幕| 亚洲国产精久久久久久久| 久久综合狠狠综合久久激情 | 久久国产成人亚洲精品影院| 热综合一本伊人久久精品| 久久久久久久综合狠狠综合| 亚洲国产欧洲综合997久久| 久久青青草原亚洲av无码app| 久久精品嫩草影院| 久久夜色精品国产| 亚洲精品乱码久久久久66| 色综合久久天天综合| 日韩中文久久| 久久91精品久久91综合| 亚洲国产精品综合久久网络| 久久夜色精品国产噜噜噜亚洲AV| 国产一级持黄大片99久久| 久久99精品免费一区二区| 久久精品国产亚洲av高清漫画 | 99精品国产综合久久久久五月天| 久久精品国产亚洲77777| 久久精品这里只有精99品| 亚洲国产精品无码久久98| 久久99精品久久久久久9蜜桃 | 久久99国产综合精品| 国内精品久久久久久久涩爱 | 精品国产乱码久久久久久1区2区| 久久性精品| 久久狠狠色狠狠色综合| 一本久久a久久精品亚洲| 91超碰碰碰碰久久久久久综合| 久久精品国产99久久久古代| 久久精品国产WWW456C0M| 国内精品久久久久影院优| 久久夜色精品国产亚洲| 激情久久久久久久久久| 久久99国产精一区二区三区| 亚洲伊人久久大香线蕉综合图片| 久久久黄片|