成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

java源碼Integer.bitCount算法解析,分析原理(統(tǒng)計(jì)二進(jìn)制bit位)

Caizhenhao / 2992人閱讀

摘要:算法統(tǒng)計(jì)整數(shù)的二進(jìn)制表達(dá)式中的位為的位數(shù)漢明重量普通算法應(yīng)該是最先想到的算法了,從最低位開始,一位一位地統(tǒng)計(jì)是否為,時(shí)間復(fù)雜度為,為總數(shù)。這時(shí)中存儲(chǔ)了每?jī)晌坏慕y(tǒng)計(jì)結(jié)果,可以進(jìn)行兩兩相加,最后求和。

算法:統(tǒng)計(jì)整數(shù)的二進(jìn)制表達(dá)式中的bit位為1的位數(shù)(漢明重量)

普通算法
public int bitCount(int num) {
    int count = 0;
    do {
        if ((num & 1) == 1) {
            count++;
        }
        num>>=1;
    } while (num > 0);
    return count;
}

應(yīng)該是最先想到的算法了,從最低位開始,一位一位地統(tǒng)計(jì)是否為1,時(shí)間復(fù)雜度為O(n),n為總bit數(shù)。

優(yōu)化算法
public int countBit2(int num) {
    int count = 0;
    while (num > 0) {
        num = num & (num - 1);
        count++;
    }
    return count;
}

這個(gè)算法乍看很懵逼,但是仔細(xì)琢磨一下也能發(fā)現(xiàn)原理:n-1后,n的最低位的1被消除了,然后與n位與,n變?yōu)樽畹臀?置為0后的新整數(shù),如:

0b101100  減一  0b101011 最低位的1消除,0b101100 & 0b101011 = 0b101000

如此循環(huán)多少次就有多少個(gè)1,時(shí)間復(fù)雜度也是O(n),但是這個(gè)n表示bit位為1的個(gè)數(shù),總體是要比上一個(gè)優(yōu)一點(diǎn)的。
當(dāng)我們以為這已經(jīng)是最優(yōu)的算法了,事實(shí)卻并非如此

Integer.bitCount
public static int bitCount(int i) {
    // HD, Figure 5-2
    i = i - ((i >>> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
    i = (i + (i >>> 4)) & 0x0f0f0f0f;
    i = i + (i >>> 8);
    i = i + (i >>> 16);
    return i & 0x3f;
}

最后,其實(shí)java的Integer類已經(jīng)提供了一個(gè)方法來統(tǒng)計(jì)bit位(無符號(hào)右移,可以統(tǒng)計(jì)負(fù)數(shù)的),乍看之下,WTF?
原理:想象一下,當(dāng)一列的1擺在我們?nèi)四X的面前,我們會(huì)怎么數(shù)?一個(gè)一個(gè)數(shù),第一個(gè)的算法的原理?;蛘邇蓚€(gè)兩個(gè)地?cái)?shù)?本方法就是如此實(shí)現(xiàn)的。如下圖:

             二進(jìn)制                       十進(jìn)制
 1  0   1  1   1  1   1  1   1  1     10 11 11 11 11
  01     10     10     10     10       1 2  2  2  2
               /            /           /    /
  01       0100           0100         1   4    4
                       /                     /
  01               1000                1      8
                /                          /
          1001                             9
          
              767的二進(jìn)制中的1的位數(shù)計(jì)算過程

每?jī)晌籦it為一組,分別統(tǒng)計(jì)有幾個(gè)1,然后把結(jié)果存到這兩個(gè)bit位上,如:11有2個(gè)1,結(jié)果為1010替代11的存儲(chǔ)到原位置。然后進(jìn)行加法計(jì)算,把所有的結(jié)果加起來。加的過程中呢又可以兩兩相加,減少計(jì)算流程。

兩個(gè)bit計(jì)算1的數(shù)量:0b11: 0b01 + 0b01 = 0b10 = 2, 0b10: 0b01 + 0b00 = 0b01 = 1,這樣就清楚了。

算法實(shí)現(xiàn)如下:

首先整數(shù)i抹除左一位:i & 0x55555555,然后錯(cuò)位相加。(i >>> 1) & 0x55555555表示:左位移到右邊,再把左位抹除,這樣就可以計(jì)算兩個(gè)bit位上1的個(gè)數(shù)了:0b1011=>0b0001 + 0b0101 = 0b0110左兩位有1個(gè)1,右兩位有2個(gè)1。

這時(shí)i中存儲(chǔ)了每?jī)晌坏慕y(tǒng)計(jì)結(jié)果,可以進(jìn)行兩兩相加,最后求和。

過程:

0x55555555  ?0b01010101010101010101010101010101?
0x33333333  ?0b00110011001100110011001100110011?
0x0f0f0f0f  ?0b00001111000011110000111100001111?
0x00ff00ff  0b00000000111111110000000011111111
0x0000ffff  ?0b00000000000000001111111111111111?
0x3f        ?0b00111111?

0b11 11 11 11 11    (i & 0x55555555) + ((i >>> 1) & 0x55555555)  = 0b0101010101? + 0b0101010101 = 0b1010101010
0b10 10 10 10 10    (i & 0x33333333) + ((i >>> 2) & 0x33333333) = 0b1000100010 + 0b00100010 = 0b1001000100
0b10 01 00 01 00    (i & 0x0f0f0f0f) + ((i >>> 4) & 0x0f0f0f0f) = 0b1000000100 + 0b0100 = 0b1000001000
0b10 00 00 10 00    (i & 0x00ff00ff) + ((i >>> 8) & 0x00ff00ff) = 0b1000 + 0b10 = 0b1010
0b00 00 00 10 10    (i & 0x0000ffff) + ((i >>> 16) & 0x0000ffff) = 0b1010 + 0 = 0b1010
dec           10

算法原型:

public static int bitCount(int i) {
    i = (i & 0x55555555) + ((i >>> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
    i = (i & 0x0f0f0f0f) + ((i >>> 4) & 0x0f0f0f0f);
    i = (i & 0x00ff00ff) + ((i >>> 8) & 0x00ff00ff);
    i = (i & 0x0000ffff) + ((i >>> 16) & 0x0000ffff);
    return i;
}

時(shí)間復(fù)雜度O(1),可以,很ok了!但是寫文章都要潤(rùn)色下的,別說算法了,然后優(yōu)化過后的就是Integer中的實(shí)現(xiàn)了。
優(yōu)化:

第一步:兩個(gè)bit計(jì)算1的數(shù)量:0b11: 0b01 + 0b01 = 0b10 = 2, 0b10: 0b00 + 0b01 = 0b01 = 1。研究發(fā)現(xiàn):2=0b11-0b1,1=0b10-0b1,可以減少一次位于計(jì)算:i = i - ((i >>> 1) & 0x55555555)

第二步:暫時(shí)沒有好的優(yōu)化方法

第三步:實(shí)際是計(jì)算每個(gè)byte中的1的數(shù)量,最多8(0b1000)個(gè),占4bit,可以最后進(jìn)行位與運(yùn)算消位,減少一次&運(yùn)算:i = (i + (i >>> 4)) & 0x0f0f0f0f

第四,五步:同上理由,可以最后消位。但是由于int最多32(0b100000)個(gè)1,所以這兩步可以不消位,最后一步把不需要的bit位抹除就可以了:i & 0x3f

感悟:大道至簡(jiǎn),看似復(fù)雜的算法,其實(shí)現(xiàn)原理卻是我們大腦的簡(jiǎn)單思維邏輯

7    0b111
i = 7 - ((7>>>1) & 0x55555555) = 6 = 0b110
i = (6 & 0x33333333) + ((6 >>> 2) & 0x33333333) = 2 + 1 = 3 = 0b11
i = (3 + (i >>> 4)) & 0x0f0f0f0f = 3 & 0x0f0f0f0f = 3 = 0b11
i = 3 + (3 >>> 8) = 3 = 0b11
i = 3 + (3 >>> 16) = 3 = 0b11
i = 3 & 0x3f = 3

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76489.html

相關(guān)文章

  • Java HashMap 源碼解析

    摘要:所以利用哈希表這種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)具體類時(shí),需要設(shè)計(jì)個(gè)好的函數(shù),使沖突盡可能的減少其次是需要解決發(fā)生沖突后如何處理。源碼剖析首先從構(gòu)造函數(shù)開始講,遵循集合框架的約束,提供了一個(gè)參數(shù)為空的構(gòu)造函數(shù)與有一個(gè)參數(shù)且參數(shù)類型為的構(gòu)造函數(shù)。 本文章首發(fā)于個(gè)人博客,鑒于sf博客樣式具有賞心悅目的美感,遂發(fā)表于此,供大家學(xué)習(xí)、批評(píng)。本文還在不斷更新中,最新版可移至個(gè)人博客。? 繼上一篇文章Java集合...

    Aklman 評(píng)論0 收藏0
  • 大廠算法面試之leetcode精講9.運(yùn)算

    摘要:空間復(fù)雜度方法是否為最大的冪的約數(shù)思路最大的的冪為,判斷是否是的約數(shù)即可。復(fù)雜度時(shí)間復(fù)雜度,一個(gè)整數(shù)統(tǒng)計(jì)二進(jìn)制的復(fù)雜度,最壞的情況下是。 大廠算法面試之leetcode精講9.位運(yùn)算視頻教程(高效學(xué)習(xí)):點(diǎn)擊學(xué)習(xí)目錄:1.開篇介紹2.時(shí)間空間復(fù)雜度3.動(dòng)態(tài)規(guī)劃4.貪心5.二分查找6.深度優(yōu)先&廣度優(yōu)先7.雙指針...

    番茄西紅柿 評(píng)論0 收藏2637
  • Java集合之HashMap源碼解析

    摘要:之前,其內(nèi)部是由數(shù)組鏈表來實(shí)現(xiàn)的,而對(duì)于鏈表長(zhǎng)度超過的鏈表將轉(zhuǎn)儲(chǔ)為紅黑樹。非線程安全,即任一時(shí)刻可以有多個(gè)線程同時(shí)寫,可能會(huì)導(dǎo)致數(shù)據(jù)的不一致。有時(shí)兩個(gè)會(huì)定位到相同的位置,表示發(fā)生了碰撞。 原文地址 HashMap HashMap 是 Map 的一個(gè)實(shí)現(xiàn)類,它代表的是一種鍵值對(duì)的數(shù)據(jù)存儲(chǔ)形式。 大多數(shù)情況下可以直接定位到它的值,因而具有很快的訪問速度,但遍歷順序卻是不確定的。 HashM...

    lindroid 評(píng)論0 收藏0
  • 集合源碼學(xué)習(xí)之路---hashMap(jdk1.8)

    摘要:值得位數(shù)有的次方,如果直接拿散列值作為下標(biāo)訪問主數(shù)組的話,只要算法比較均勻,一般是很難出現(xiàn)碰撞的。但是內(nèi)存裝不下這么大的數(shù)組,所以計(jì)算數(shù)組下標(biāo)就采取了一種折中的辦法,就是將得到的散列值與數(shù)組長(zhǎng)度做一個(gè)與操作。 hashMap簡(jiǎn)單介紹 hashMap是面試中的高頻考點(diǎn),或許日常工作中我們只需把hashMap給new出來,調(diào)用put和get方法就完了。但是hashMap給我們提供了一個(gè)絕佳...

    kamushin233 評(píng)論0 收藏0
  • 源碼|jdk源碼之HashMap分析(一)

    摘要:看屬性有一個(gè),所以是紅黑樹的節(jié)點(diǎn)。會(huì)在鏈表過長(zhǎng)的時(shí)候,將其重構(gòu)成紅黑樹,這個(gè)看后面的代碼。如果是紅黑樹的話,調(diào)用紅黑樹的查找函數(shù)來最終找到這個(gè)節(jié)點(diǎn)。該位置為平衡樹。但是這導(dǎo)致鏈表增長(zhǎng),需要觸發(fā)鏈表重構(gòu)成平衡樹的判斷邏輯。 hash表是應(yīng)用最廣泛的數(shù)據(jù)結(jié)構(gòu),是對(duì)鍵值對(duì)數(shù)據(jù)結(jié)構(gòu)的一種重要實(shí)現(xiàn)。 它能夠?qū)㈥P(guān)鍵字key映射到內(nèi)存中的某一位置,查詢和插入都能達(dá)到平均時(shí)間復(fù)雜度為O(1)的性能。 ...

    AndroidTraveler 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Caizhenhao

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<