計算一個整數的二進制表示有多少個1(別人的最快算法)

最近看了這篇文章 的第一道題

題目本意是怎麼判斷這段代碼的輸出

int func(x)
{
    int countx =0;
    while(x)
    {
          countx ++;
          x = x&(x-1);
     }
    return countx;
}

但深入一想,這不就是求任意整數的二進制表示裏有多少個1嗎?

要想解決上述問題,我能想到的最快方法是按位右移,累計每個移出的數,直到原數爲0

int func2(int x)
{
    int countx =0;
    while (x)
    {
        if (x & 1)
            countx ++;
        x = x >> 1;
    }
    return countx;
}

但是很明顯,我的算法在二進制表示含有較多中間0時,效率很低,而別人的算法就不存在該問題,而且沒有if分支判斷,避免CPU指令Cache miss,方便並行化

總之,該算法求解1個數,類似於輾轉相除法求最大公約數,都是基於某種數學原理,性能自然比愚蠢的枚舉不知道高到哪裏去了


該算法的原理,我晚上思考了下,覺得是對【判斷一個整數是否爲2的冪】算法的擴展應用

【判斷一個整數是否爲2的冪】算法

bool power_of_2(int x)
{
    if (0 == (x & x-1))
        return true;
    else
        return false;
}

該算法的思路是,如果整數i爲2的冪,則其二進制表示必然只有1位爲1,假設是第N位爲1,則0到N-1位必然爲0,且N+1到31位(假設是32位CPU)全零

令j = i-1,則j的二進制表示就是第N位爲00到N-1位全1,N+1到31位還是全零,

因爲減法需要從高位借位,對於2的冪整數,能借位的只有唯一的那個1,所以N位必然變爲0

又因爲減數爲1,所以0到N-1位必然全爲1

這樣i和j的【按位與】運算,必然等於0


將上述思路拓展

對於任意一個整數,都可以看成一系列冪的和,比如

520,就是512+8

1111,就是1024+64+16+4+2+1

當對該整數進行【減一】和【按位與】操作時,最低等級的冪先變爲零

然後再次執行【減一】和【按位與】操作,次低等級的冪也變爲零

以此類推,每次循環都將一個非零冪項清除(爲零的冪項不發生借位,所以自動忽略)

最後變爲全零,循環退出

發佈了159 篇原創文章 · 獲贊 27 · 訪問量 46萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章