最左側1問題

2021-07-10 07:26:16 字數 2141 閱讀 2218

今天給大家介紹乙個有趣的位運算題目,叫最左側1問題,英文名叫count leading zeros或most significant bits problem。問題比較好理解,就是給定乙個整數,然後看最左側的1出現在什麼位置,等價於尋找整數的最高有效位或者前置零的個數。比如,0x128只有乙個1,從右側遍歷1出現在第7位(從0開始計數)。我們給出幾種不同的解法。以下解法返回的均是從右側遍歷的位置。

針對該問題,乙個很容易想到的解法就是移位操作,右移原數直到0為止。這種方法很容易想到,實現也比較簡單:

int left_most_one_1(int n)

return pos;

}

這種方法的執行時間和1的位置相關,整體而言複雜度較高。我們對其進行優化。乙個整數的各個位可以看成是排好序的,尋找最左側1就類似於在有序陣列中查詢特定元素,所以第二種方法即是二分法:

int left_most_one_2(int n)

int exp=4;

int pos=(1<0)

二分的標準是通過右移判斷結果是否為0,如果為0說明1在低位部分,否則就是在高位部分,然後通過不停地二分即可找到所在位置。事實上,由於迴圈次數較少,我們還可以迴圈展開,使其效能更高。這個任務就交給大家了。

後面兩種方法就不屬於常規方法了,有些投機取巧的成分。第三種方法用到了我們之前介紹的《型別強轉和位址強轉》。我們知道,在計算機中乙個浮點數是按照ieee標準實現的。給定乙個浮點數,其結構如下:

上述格式的符號部分為1位,階碼部分為8位,尾數部分為23位;如果是雙精度(double)型別,符號部分為1位,階碼部分為11位,尾數部分為52位。

int left_most_one_3(int n)

上述**首先將整數提公升成浮點數,然後獲得浮點數的位址,對該位址進行強轉,轉成int型位址。獲得int型位址之後再獲得該位址對應的int值。最後根據浮點數的結構解析階碼減去127即是最左側1的位置。是不是有些匪夷所思,計算機就是這麼奇妙。

(注:上述方法有缺陷,在極少數例子下會返回錯誤的結果,原因出在浮點數的精度上。給定乙個32位整數,將其轉換成32位浮點數的時候會存在精度缺失問題。這是因為32位浮點數的尾數只有23位,加上隱含的1也只有24位的精度。當原始整數的有效位超過24位時,整數轉浮點數就會丟棄末尾的乙個或多個1,從而產生誤差。誤差是不可避免的,另乙個嚴重問題是在丟棄末尾1的同時,可能會產生進製以減少誤差,這是這個原因導致上述方法不正確。舉乙個簡單的例子,上述方法第乙個報錯的整數33554431,它等於2^25-1,二進位制表示是25個1。由於浮點數的尾數最大只能包含24個有效位,所以將它轉換成浮點數就會存在精度缺失的問題。編譯器在處理這種情況的時候可能會直接丟棄末尾的1也可能在末尾加1丟棄0。在我的電腦上是加1丟0,原始整數就被轉換成了2^25=33554432.0,進而產生乙個進製,從而返回錯誤結果。這個例子可能還不直觀,給大家換個數2147483584,這個數在被轉換成浮點數之後變為2147483648.0,和原始整數相差了64!大家能明白一些了嗎?最大的整數2^31-1包含31個1,在轉換成浮點數的時候最多可能會丟棄7位有效數字。7位有效數字的上限是128,根據丟棄值的大小是否大於等於64,編譯器會選擇對結果加1,從而產生進製和誤差。有什麼解決方案嗎?有!float不行,那就換double吧。double的尾數有52位,總以承載整個int整數。但是對64位整數,它也無能為力了。換用double之後,**可以修改為:

int left_most_one_3(int n)

double b=n;

return ((*(long long*)&b)>>52&2047)-1023;

}

感謝王博讓我對浮點數的認識更近一步!)

第四種方法更加偷懶,直接呼叫編譯器的內建函式。針對gcc編譯器,其內建了很多有用函式,比如__builtin_popcount可以用來統計乙個二進位制數中的1個數。再比如,__builtin_clz可以用來獲得前置零的個數,這個函式就是我們需要的。在vs下,對應的兩個函式是:__popcnt和__lzcnt。所以**就很簡單咯:

#include int left_most_one_4(int n)

針對該問題還有很多其他解法,但是上述四種方法足以讓你開啟眼界。我決定了,下次面試就問這個題目,寫出二分法者即通過面試~

mysql最左側原則的深入理解

一直以來,博主對最左側原則的理解都是,比如給a,b,c加上索引,那麼a,b可以用到索引,a,c也可以用到索引,但b,c是用不到的。包括這個a必須要在用到的第乙個索引處。但是如果乙個表中只有a,b,c三個字段,給他們加上聯合索引 a,b,c 此時 select from test where c 2 ...

mysql n 1問題 mybatis n 1問題

mybatis的一對多或者多對多的時候,2中方式解決,一種是巢狀select,但是會有n 1問題,不推薦 另外一種是使用一條sql,在該sql裡面使用子查詢的方式來完成。比如 select from clazz m left join student mm on m.id mm.clazz id w...

執行緒問題1

func setlistview2info mesgp 0 重新整理listview2資訊,0 單執行緒 if guictrlread checkbox1 1 or guictrlread checkbox1 1 and guictrlread combo2 1 then 單執行緒準備資料 guic...