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

Fly me to the moon

the beauty of C++

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

9月21日,對本文從格式到部分內容上都進行了修改
另外,鑒于某些轉載沒有注明出處,考慮到版權問題,特聲明如下:
作者:翼帆@cppblog 
原文地址:http://m.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 翼帆 閱讀(8325) 評論(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;
}  回復  更多評論   

導航

<2010年6月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

統計

常用鏈接

留言簿

隨筆分類

隨筆檔案

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            一区二区三区高清不卡| 午夜精品久久久久影视| av成人福利| 亚洲国产精品综合| 亚洲国产天堂久久综合网| 国内精品久久久久久久影视麻豆| 国产精品国产三级国产专区53| 欧美性猛交xxxx免费看久久久| 欧美电影在线播放| 欧美视频在线观看免费| 国产精品sss| 国产日韩在线视频| 亚洲国产你懂的| 欧美在线免费看| 一区二区三区视频在线| 国产综合香蕉五月婷在线| 狠狠爱综合网| 亚洲精品资源| 欧美一站二站| 欧美国产视频一区二区| 日韩一级精品视频在线观看| 亚洲视频欧洲视频| 久久久中精品2020中文| 欧美日韩中文字幕在线| 国产日韩欧美| 日韩视频不卡| 久久香蕉国产线看观看网| 91久久国产综合久久91精品网站| 欧美11—12娇小xxxx| a91a精品视频在线观看| 久久久久国产精品一区三寸| 欧美精品1区2区3区| 国内外成人免费激情在线视频网站 | 91久久黄色| 久久青青草原一区二区| 午夜精品久久久久久久蜜桃app | 美国成人直播| 欧美激情一区二区三区四区| 亚洲深夜福利在线| 欧美成人午夜免费视在线看片 | 国产日韩欧美在线| 在线性视频日韩欧美| 麻豆成人在线| 性欧美大战久久久久久久免费观看 | 久久久久久久综合| 国产精品毛片大码女人| 亚洲青涩在线| 久久人人超碰| 欧美一区二区啪啪| 国产精品一区一区| 亚洲一区二区三区高清| 亚洲国产99| 欧美一级成年大片在线观看| 国产精品久久久久免费a∨| 在线亚洲欧美视频| 亚洲精品网站在线播放gif| 一区二区三区欧美在线| 女同性一区二区三区人了人一| 午夜精品视频一区| 国产日本欧美一区二区三区在线| 亚洲欧美第一页| 一本久久综合| 国产精品久久久久久久app| 亚洲一区二区在线观看视频| 亚洲精品韩国| 欧美日韩精品免费观看视频| 一区二区三区精密机械公司| 日韩视频在线观看国产| 欧美美女bb生活片| 亚洲私拍自拍| 亚洲一区高清| 国产一级久久| 欧美不卡高清| 欧美精品成人在线| 亚洲日本欧美在线| 欧美不卡高清| 国产欧美日韩| 亚洲午夜羞羞片| 亚洲欧美视频一区| 国产精品www994| 亚洲精品老司机| 亚洲免费电影在线| 在线观看欧美成人| 一区二区三区鲁丝不卡| 欧美黄色aa电影| 日韩亚洲视频| 欧美制服丝袜第一页| 国产欧美一区二区三区另类精品| 亚洲午夜激情网站| 久久精品一区二区| 在线电影院国产精品| 久久精品一二三区| 久热精品在线| 999亚洲国产精| 国产精品二区在线| 欧美一进一出视频| 亚洲国产精品电影| 亚洲欧美日韩在线一区| 韩国av一区二区三区在线观看| 美乳少妇欧美精品| 亚欧成人精品| 亚洲精品久久久久| 久久人人爽人人爽爽久久| 欧美激情一区二区三区在线视频| 欧美激情一区二区三区四区 | 国内外成人免费视频| 欧美日本乱大交xxxxx| 久久精品国产成人| 99成人在线| 亚洲电影下载| 欧美视频在线一区二区三区| 久久久91精品国产一区二区精品| 亚洲精品久久久久久久久久久| 久久久久久网址| 欧美亚洲一区| 午夜精品久久| 亚洲午夜激情网站| 一区二区三区国产在线观看| 日韩视频在线观看一区二区| 亚洲青色在线| 亚洲乱码精品一二三四区日韩在线 | 久久一区亚洲| 久久精品欧美日韩| 久久久蜜桃一区二区人| 老司机一区二区三区| 欧美成人免费观看| 欧美激情一区二区三区蜜桃视频 | 亚洲人精品午夜| 亚洲精品免费在线播放| 亚洲精品国产欧美| 国产精品福利网站| 亚洲视频你懂的| 欧美中文字幕第一页| 欧美在线播放高清精品| 在线观看日韩专区| 欧美国产综合| 久久免费视频网站| 欧美一区午夜精品| 亚洲高清二区| 午夜精品成人在线视频| 国产精品成人一区二区| 樱桃国产成人精品视频| 欧美日韩一区自拍| 欧美日韩日日骚| 亚洲精品亚洲人成人网| 欧美裸体一区二区三区| 欧美黄色影院| 欧美视频在线观看免费| 亚洲欧美日韩一区二区在线| 久久精品国产精品| 国产精品国产福利国产秒拍 | 99热免费精品| 美日韩精品视频| 亚洲伊人网站| 欧美日韩精品在线播放| 亚洲精品美女在线观看| 欧美寡妇偷汉性猛交| 久久精品国产免费看久久精品| 欧美日韩高清一区| 国产精品视频成人| 亚洲免费网址| 亚洲欧美激情视频| 亚洲第一视频| 久久阴道视频| 亚洲靠逼com| 9l视频自拍蝌蚪9l视频成人| 欧美三级精品| 久久久久久久久综合| 久久精品国产亚洲5555| 亚洲人成高清| 亚洲无限av看| 亚洲电影一级黄| 夜夜精品视频一区二区| 国产午夜一区二区三区| 欧美国产综合一区二区| 欧美日本一道本| 久久综合五月| 欧美三级免费| 亚洲国产色一区| 国产一区二区av| 亚洲最黄网站| 亚洲国产精品电影| 欧美亚洲综合久久| 一本色道久久综合亚洲二区三区| 国产亚洲激情在线| 亚洲欧洲精品一区二区三区| 亚洲激情网址| 欧美一级艳片视频免费观看| 亚洲精品视频免费在线观看| 在线亚洲欧美视频| 日韩一级精品| 欧美日韩ab片| 亚洲看片免费| 亚洲午夜精品久久久久久浪潮| 免费观看30秒视频久久| 麻豆成人综合网| 日韩网站在线观看| 欧美精品日韩| 亚洲一区二区三区四区五区午夜| 亚洲一区网站|