漢明權重
漢明權重是一串符號中非零符號的個數。因此它等同於同樣長度的全零符號串的漢明距離。在最為常見的數據位符號串中,它是1的個數。
字符 | 字符串 | 漢明權重 |
0,1 | 11101 | 4 |
0,1 | 11101000 | 4 |
0,1 | 00000000 | 0 |
' ',a-z | hello world | 11 |
歷史及應用
漢明權重是以理查德·衛斯里·漢明的名字命名的,它在包括信息論、編碼理論、密碼學等多個領域都有應用。
高效實現
在密碼學以及其它應用中經常需要計算數據位中1的個數,針對如何高效地實現人們已經廣泛地進行了研究。一些處理器使用單個的命令進行計算,另外一些根據數據位向量使用並行運算進行處理。對於沒有這些特性的處理器來說,已知的最好解決辦法是按照樹狀進行相加。例如,要計算二進制數A=0110110010111010中1的個數,這些運算可以表示為:
符號 | 二進制 | 十進制 | 註釋 |
A | 01 10 11 00 10 11 10 10 | 原始數據 | |
B = A & 01 01 01 01 01 01 01 01 | 01 00 01 00 00 01 00 00 | 1,0,1,0,0,1,0,0 | A隔一位檢驗 |
C = (A >> 1) & 01 01 01 01 01 01 01 01 | 00 01 01 00 01 01 01 01 | 0,1,1,0,1,1,1,1 | A中剩餘的數據位 |
D = B + C | 01 01 10 00 01 10 01 01 | 1,1,2,0,1,2,1,1 | A中每個雙位段中1的個數列表 |
E = D & 0011 0011 0011 0011 | 00 01 00 00 00 10 00 01 | 1,0,2,1 | D中數據隔一位檢驗 |
F = (D >> 2) & 0011 0011 0011 0011 | 00 01 00 10 00 01 00 01 | 1,2,1,1 | D中剩餘數據的計算 |
G = E + F | 00 10 00 10 00 11 00 10 | 2,2,3,2 | A中4位數據段中1的個數列表 |
H = G & 00001111 00001111 | 00 00 00 10 00 00 00 10 | 2,2 | G中數據隔一位檢驗 |
I = (G >> 4) & 00001111 00001111 | 00 00 00 10 00 00 00 11 | 2,3 | G中剩餘數據的計算 |
J = H + I | 00 00 01 00 00 00 01 01 | 4,5 | A中8位數據段中1的個數列表 |
K = J & 0000000011111111 | 00 00 00 00 00 00 01 01 | 5 | J中隔一位檢驗 |
L = (J >> 8) & 0000000011111111 | 00 00 00 00 00 00 01 00 | 4 | J中剩餘數據的檢驗 |
M = K + L | 00 00 00 00 00 00 10 01 | 9 | 最終答案 |
這裏的運算是用C語言表示的,所以X >> Y表示X右移Y位,X & Y表示X與Y的位與,+表示普通的加法。基於上面所討論的思想的這個問題的最好算法列在這裏:
//types and constants used in the functions below
typedef unsigned __int64 uint64; //assume this gives 64-bits
const uint64 m1 = 0x5555555555555555; //binary: 0101...
const uint64 m2 = 0x3333333333333333; //binary: 00110011..
const uint64 m4 = 0x0f0f0f0f0f0f0f0f; //binary: 4 zeros, 4 ones ...
const uint64 m8 = 0x00ff00ff00ff00ff; //binary: 8 zeros, 8 ones ...
const uint64 m16 = 0x0000ffff0000ffff; //binary: 16 zeros, 16 ones ...
const uint64 m32 = 0x00000000ffffffff; //binary: 32 zeros, 32 ones ...
const uint64 hff = 0xffffffffffffffff; //binary: all ones
const uint64 h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3...
//This is a naive implementation, shown for comparison,
//and to help in understanding the better functions.
//It uses 24 arithmetic operations (shift, add, and).
int popcount_1(uint64 x) {
x = (x & m1 ) + ((x >> 1) & m1 ); //put count of each 2 bits into those 2 bits
x = (x & m2 ) + ((x >> 2) & m2 ); //put count of each 4 bits into those 4 bits
x = (x & m4 ) + ((x >> 4) & m4 ); //put count of each 8 bits into those 8 bits
x = (x & m8 ) + ((x >> 8) & m8 ); //put count of each 16 bits into those 16 bits
x = (x & m16) + ((x >> 16) & m16); //put count of each 32 bits into those 32 bits
x = (x & m32) + ((x >> 32) & m32); //put count of each 64 bits into those 64 bits
return x;
}
//This uses fewer arithmetic operations than any other known
//implementation on machines with slow multiplication.
//It uses 17 arithmetic operations.
int popcount_2(uint64 x) {
x -= (x >> 1) & m1; //put count of each 2 bits into those 2 bits
x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits
x += x >> 8; //put count of each 16 bits into their lowest 8 bits
x += x >> 16; //put count of each 32 bits into their lowest 8 bits
x += x >> 32; //put count of each 64 bits into their lowest 8 bits
return x &0xff;
}
//This uses fewer arithmetic operations than any other known
//implementation on machines with fast multiplication.
//It uses 12 arithmetic operations, one of which is a multiply.
int popcount_3(uint64 x) {
x -= (x >> 1) & m1; //put count of each 2 bits into those 2 bits
x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits
return (x * h01)>>56; //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
}
在最壞的情況下,上面的實現是所有已知算法中表現最好的。但是,如果已知大多數數據位是0的話,那麼還有更快的算法。這些更快的算法是基於這樣一種事實即X與X-1相與得到的最低位永遠是0。例如:
Expression | Value |
X | 0 1 0 0 0 1 0 0 0 1 0 0 0 0 |
X-1 | 0 1 0 0 0 1 0 0 0 0 1 1 1 1 |
X & (X-1) | 0 1 0 0 0 1 0 0 0 0 0 0 0 0 |
減1操作將最右邊的符號從0變到1,從1變到0,與操作將會移除最右端的1。如果最初X有N個1,那麼經過N次這樣的迭代運算,X將減到0。下面的算法就是根據這個原理實現的。
//This is better when most bits in x are 0
//It uses 3 arithmetic operations and one comparison/branch per "1" bit in x.
int popcount_4(uint64 x) {
uint64 count;
for (count=0; x; count++)
x &= x-1;
return count;
}
//This is better if most bits in x are 0.
//It uses 2 arithmetic operations and one comparison/branch per "1" bit in x.
//It is the same as the previous function, but with the loop unrolled.
#define f(y) if ((x &= x-1) == 0) return y;
int popcount_5(uint64 x) {
if (x == 0) return 0;
f( 1) f( 2) f( 3) f( 4) f( 5) f( 6) f( 7) f( 8)
f( 9) f(10) f(11) f(12) f(13) f(14) f(15) f(16)
f(17) f(18) f(19) f(20) f(21) f(22) f(23) f(24)
f(25) f(26) f(27) f(28) f(29) f(30) f(31) f(32)
f(33) f(34) f(35) f(36) f(37) f(38) f(39) f(40)
f(41) f(42) f(43) f(44) f(45) f(46) f(47) f(48)
f(49) f(50) f(51) f(52) f(53) f(54) f(55) f(56)
f(57) f(58) f(59) f(60) f(61) f(62) f(63)
return 64;
}
//Use this instead if most bits in x are 1 instead of 0
#define f(y) if ((x |= x+1) == hff) return 64-y;
參見
外部連結
- Aggregate Magic Algorithms (頁面存檔備份,存於互聯網檔案館).優化漢明重量算法及其它算法解釋(附原始碼)。
- HACKMEM item 169 (頁面存檔備份,存於互聯網檔案館). Population count assembly code for the PDP/6-10.
- Bit Twiddling Hacks (頁面存檔備份,存於互聯網檔案館)帶有原始碼的幾種漢明重量計算方法