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

            Fly me to the moon

            the beauty of C++

            位運算之美——用+,-和位運算實現正整數除法和取模(一)

            9月21日,對本文從格式到部分內容上都進行了修改
            另外,鑒于某些轉載沒有注明出處,考慮到版權問題,特聲明如下:
            作者:翼帆@cppblog 
            原文地址:http://www.shnenglu.com/xiaoyisnail/archive/2009/09/19/96707.html
            本文版權歸作者和cppblog共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

                今天看了一位師兄去年的筆經總結,其中有一題是“不許用%和/來實現求任意數除以3的余數”,我想考官的目的應該是想考察學生對位運算的熟悉程度吧,于是我把題目擴展成“只能用+,-和位運算實現正整數除法(/)和取模(%)”,注意:這里不能使用其它的庫例程來輔助計算,如log,log10等。在思考這道題目的過程中,我又涉及到了許多二進制相關的題目,如:
                判斷給定的整數是不是2的整數次冪
                判斷給定的整數是不是4的整數次冪
                求給定整數的二進制表示中1的個數
                求給定整數的二進制表示中0的個數
                求給定整數的二進制表示中最高位1的位置
                求大于等于給定整數的最小的2的整數次冪
                求給定整數的二進制表示的有效位數
                ...
                9月21日補充:這里只考慮值為正整數的情況。
                這些題目都是經典老題,頻繁出現于各類筆試面試題中,除了能考察位運算外,還能考察應聘者能否給出創新的算法來更好地解決問題。可以說這些題目都不難,如果使用32位的int來表示整數的話,蠻力法都可以比較好地完成任務,但是如果想盡可能地提高效率,那就需要動一番腦經了。下面給出我對這些問題的整理和C++實現,并在下次的文章中給出只用+,-和位運算實現的正整數除法和取模。
                從某種意義上講,特別是從充分利用底層硬件的計算能力(利用特殊的cpu指令)來看,這些解法肯定不是最優的,所以還希望大俠們多多指點。
               
                判斷給定的整數是不是2的整數次冪
                這應該是最簡單的,利用最高位是1,其后所有位為0的特性,常數時間解決問題:

            1 //判斷n是否是2的正整數冪
            2 inline bool is_2exp(unsigned int n)
            3 {
            4     return !(n&(n-1));
            5 }

                求給定整數的二進制表示中1的個數
                考慮到n-1會把n的二進制表示中最低位的1置0并把其后的所有0置1,同時不改變此位置前的所有位,那么n&(n-1)即可消除這個最低位的1。這樣便有了比順序枚舉所有位更快的算法:循環消除最低位的1,循環次數即所求1的個數。此算法的時間復雜度為O(n的二進制表示中的1的個數),最壞情況下的復雜度O(n的二進制表示的總位數)。
             1//計算n的二進制表示中1的個數
             2inline int count1(unsigned int n)
             3{
             4    int r = 0;
             5    while(n)
             6    {
             7        n &= n-1;
             8        r++;
             9    }

            10    return r;
            11}
                既然有了求給定整數的二進制表示中1的個數的辦法,那么想要求給定整數的二進制表示中0的個數就很簡單了。事實上,在二進制中,完全可以把0和1看作是對稱的兩個對象,取反操作(~)可以任意的切換這兩個對象,只要先對n進行一次取反,然后再用上述算法即能得到二進制表示中0的個數。首先看下面的代碼:
             1//計算n的二進制表示中0的個數
             2inline int count0_wrong(unsigned int n)
             3{
             4    int r = 0;
             5    n &= ~n;
             6    while(n)
             7    {
             8        n &= n-1;
             9        r++;
            10    }

            11    return r;
            12}
                不知大家有沒有看出問題來?是的,~操作符會把所有高位的都取反,而不是只把有效位取反,所以我們需要一個能保持高位不變的位取反操作,下面是我的實現,時間復雜度和求二進制表示中1的個數的算法相同,都與二進制表示中1的個數有關:
             1//保持高位取反
             2inline unsigned int negate_bits(unsigned int n)
             3{
             4    if(n==0return 1;
             5    unsigned int r=0, m=~n;
             6    while(n)
             7    {
             8        r |= (n^(n-1))&m;
             9        n &= n-1;
            10    }

            11
            12    return r;
            13}
                有了這個特殊的取反操作,求給定整數的二進制表示中0的個數的辦法就簡單了:
             1//計算n的二進制表示中0的個數
             2inline int count0( unsigned int n)
             3{
             4    int r = 0;
             5    n = negate_bits(n);
             6    while(n)
             7    {
             8        n &= n-1;
             9        r++;
            10    }

            11    return r;
            12}
                看到這里,聰明的讀者肯定看出問題來了,其實我干了一件很蠢的事情。看看上述算法的時間復雜度,negate_bits花了O(n的二進制表示中1的個數),while循環計算取反后的n的二進制表示中1的個數,事實上就是O(n的二進制表示中0的個數),兩部分加起來其實就是二進制表示總的有效位數,換句話說,這個算法是線性的,而事實上,我們完全可以先線性地求出這個總的有效位數,然后減去1的位數,即得到0的位數,根本不用費那么大勁去整個保持高位的取反操作,兩者的時間復雜度在漸進意義上也是相同的。所以,我犯傻了,但是這里又引出另一個問題:

                求給定整數的二進制表示的有效位數
               
            上面提到了線性地求這個位數(下文記為m),即“循環右移1位,記錄右移次數”,時間復雜度O(m)。但是我想,一看到這個題目,所有人的第一反應應該是floor(log2(n))+1吧,但是請注意,本文在一開始就規定了“不能使用庫例程”,那么在這個限制下該怎么做呢?有沒有比線性時間更好的算法呢?其實到目前為止我也沒有什么特別好的算法,希望誰有什么精妙的算法能指點一下,不要打我。。。
             1//求給定整數的二進制表示的位數,線性算法
             2int count_bit(unsigned int n)
             3{
             4    int r = 0;
             5    while(n)
             6    {
             7        n>>=1;
             8        r++;
             9    }

            10    return r;
            11}

                求大于等于給定整數的最小的2的整數次冪
                首先是最簡單的思路:求出n的二進制表示的總位數m,于是1<<m即為所求值,當然這里要排除n自身就是2的整數次冪的情況,復雜度O(m),實現如下:
             1//求大于等于n的最小的2的正整數冪,方法1
             2//時間復雜度O(n的二進制位長度)
             3unsigned int high_2exp_1(unsigned int n)
             4{
             5    if(n<=1return 1;
             6    if(is_2exp(n)) return n;
             7
             8    unsigned int r = 1;
             9    while(n)
            10    {
            11        n >>= 1;
            12        r <<= 1;
            13    }

            14
            15    return r;
            16}
                事實上這就涉及到上面求二進制表示位數的問題,所以目前為止在此基礎上的算法都是線性時間的。   
                那有沒有不用計算位數m,從而效率更好的算法呢,能不能像在計算二進制表示中1的個數時那樣根據1的個數來設計算法呢?回到那一題中,“n-1會把n的二進制表示中最低位的1置0并把其后的所有0置1”,那么n|=n-1就把n的二進制表示中最低位1后的所有0置1,再加上1,那么就把最低位1左移了一位。于是,便有了更好的算法:循環左移最低位的1,直到n是2的整數次冪。該算法跟二進制表示中的1的個數和位置有關,最壞時間復雜度還是O(二進制表示位數),但是比起上一個實現,這個算法在多數情況下都比上一個算法快。實現如下:
             1//求大于等于n的最小的2的正整數冪,方法2
             2//計算時間與n的二進制表示中1的個數和位置有關,比方法1效率高
             3//最壞情況下的時間復雜度與方法1相同
             4unsigned int high_2exp_2(unsigned int n)
             5{
             6    if(n<=1return 1;
             7
             8    while(!is_2exp(n))
             9    {
            10        n |= n-1;
            11        n++;
            12    }

            13
            14    return n;
            15}
                
                最后來一個簡單的擴充題目:
                判斷給定的整數是不是4的整數次冪
                觀察4的整數次冪的特征,容易發現除了滿足n&(n-1)==0外,唯一的1位后的0的個數是偶數,這從4x=22k也能簡單地得到。這就很直觀地衍生出一個簡單的算法:
             1//判斷n是否是4的整數次冪
             2bool is_4exp(unsigned int n)
             3{
             4    if(!is_2exp(n)) return false;
             5
             6    int bit_len = count_bit(n)-1;//線性時間求二進制位數
             7    if((bit_len&0x1)!=1)
             8        return true;
             9    else
            10        return false;
            11}
                算法很直觀,但是比起is_2exp的常數時間is_4exp的線性時間總讓我覺得不能接受,不過無奈還是沒有想出好辦法來,哎。。。求大牛指點啊
               
                說明:寫這篇文章,已經三次丟失全文了,把我快搞瘋了,firefox下好像有點問題,先把文章發上來,過會兒換到IE下繼續。。。
                再說明:換了IE后就沒再出問題了,不過寫著寫著發現寫了好久,先歇會兒,得看書補習功課了
                最后的說明:下次會給出本文最初提出的問題(只能用+,-和位運算實現正整數除法(/)和取模(%))的實現。

            posted on 2009-09-19 13:58 翼帆 閱讀(8295) 評論(3)  編輯 收藏 引用 所屬分類: 算法

            評論

            # re: 位運算之美——用+,-和位運算實現正整數除法和取模(一)[未登錄] 2010-06-22 16:55 xxx

            那么n|=n-1就把n的二進制表示中最低位1后的所有0置1,再加上1,那么就把最低位1左移了一位。

            假設n=0x1100,那么n-1 = 0x1011, n | (n - 1) = 0x1111.跟你上面描述不一致.  回復  更多評論   

            # re: 位運算之美——用+,-和位運算實現正整數除法和取模(一) 2010-07-31 15:00 myway

            總結的不錯  回復  更多評論   

            # re: 位運算之美——用+,-和位運算實現正整數除法和取模(一) 2010-11-24 08:07 jingairpi

            求給定整數的二進制表示的有效位數

            可以binary search最高位置1的位置

            int get_leftmost_set_bit(unsigned int n)
            {
            int l, u, m, t1, t2;

            l = 0;
            u = sizeof(int) * 8 - 1;
            while (l <= u) {
            m = l + (u - l)/2;

            t1 = n & (~((1 << m) - 1));
            t2 = n & (~((1 << (m + 1)) - 1));

            if (t1 && !t2) {
            return m;
            } else if (t1 && t2) {
            l = m + 1;
            } else {
            u = m - 1;
            }
            }

            return -1;
            }  回復  更多評論   

            導航

            <2009年9月>
            303112345
            6789101112
            13141516171819
            20212223242526
            27282930123
            45678910

            統計

            常用鏈接

            留言簿

            隨筆分類

            隨筆檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            日韩精品久久久久久久电影蜜臀 | 久久久久久亚洲精品无码| 97久久久精品综合88久久| 久久美女网站免费| 99久久www免费人成精品| 欧美精品九九99久久在观看| 亚洲精品蜜桃久久久久久| 久久国产高清字幕中文| 欧美午夜A∨大片久久 | 国产精品青草久久久久福利99| 久久久久久国产精品无码下载| 久久SE精品一区二区| 国产成人久久久精品二区三区| 精品国产乱码久久久久软件| 久久这里只有精品首页| 久久精品国产欧美日韩99热| 久久免费视频网站| 久久久久久毛片免费播放| 久久久久久国产a免费观看不卡| 91久久精品91久久性色| 久久精品国产清自在天天线| 久久九九久精品国产免费直播| 久久久免费精品re6| 国内精品伊人久久久久妇| 香蕉久久夜色精品国产小说| 久久久久亚洲AV成人片| 伊人久久大香线蕉综合网站| 久久精品无码一区二区日韩AV| 久久久国产精品福利免费| 7777久久亚洲中文字幕| 亚洲综合熟女久久久30p| 伊人久久无码精品中文字幕| 久久夜色撩人精品国产| Xx性欧美肥妇精品久久久久久| 久久91精品国产91久久小草| 久久精品国产清高在天天线| 日日噜噜夜夜狠狠久久丁香五月| 久久只这里是精品66| 国产A三级久久精品| 综合人妻久久一区二区精品| 综合久久给合久久狠狠狠97色|