HACKER'S DELIGHT[算法心得]筆記

第一章  概述
略.算法

第二章  基礎知識ide

2.1 操做最右側的位元
x & (x - 1) 將最右側置位的比特位置零, 該表達式可用來判斷x是否爲2的冪.
x | (x + 1) 將最右側置零的比特位置位.
x & (x + 1) 將最右位起始的連續的1比特位置零, 若是最右位非1則不變, 該表達式可用來判斷x是否爲2^n - 1.
x | (x - 1) 將最右位起始的連續的0比特位置位, 若是最右位非0則不變.
~x & (x + 1) 將最右側置零的比特位置位, 並將其他位置零.
~x | (x - 1) 將最右側置位的比特位置零, 並將其他位置位.
~x & (x - 1) 將最右位起始的連續的0比特位置位, 並將其他位置零.
~x | (x + 1) 將最右位起始的連續的1比特位置零, 並將其他位置位.
x & (-x) 保留最右側位置的比特位, 並將其他位置零.
x ^ (x - 1) 將最右側置位的比特位及其右側全部置零的比特位置位, 並將左側的比特位置零.
x ^ (x + 1) 將最右側置零的比特位及其右側全部置位的比特位置位, 並將左側的比特位置零.
德摩根定律的推論
~(x & y) = ~x | ~y
~(x | y) = ~x & ~y
~(x + y) = ~x - y (特例y = 1時有~(x + 1) = ~x - 1)
~(x - y) = ~x + y (特例y = 1時有~(x - 1) = ~x + 1)
~(x - y) = ~x + y (特例x = 0時有~(0 - y) = y - 1)函數

2.2 結合邏輯操做的加減運算
-x = ~x + 1 = ~(x - 1)
~x = -x - 1
-(~x) = x + 1
~(-x) = x - 1
x + y = x - (~y) - 1 = (x ^ y) + 2(x & y) = (x | y) + (x & y) = 2(x | y) - (x ^ y)
x - y = x + (~y) + 1 = (x ^ y) - 2((~x) & y) = (x & (~y)) - ((~x) & y) = 2(x & (-y)) - (x ^ y)
x ^ y = (x | y) - (x & y)
x & (~y) = (x | y) - y = x - (x & y)
~(x - y) = y - x - 1 = ~x + y
x @ y = (x & y) - (x | y) - 1 = (x & y) + ~(x | y) (@爲同或, 打不出對應符號用@代替, 下同)
x | y = (x & ~y) + y
x & y = ((~x) | y) - (~x)優化

2.3 邏輯與算術表達式中的不等式
若是將x, y視爲無符號整數則必然有如下不等式:
(x ^ y) <= (x | y)
(x & y) <= (x @ y)
(x | y) >= max(x, y)
(x & y) <= min(x, y)
(x | y) <= x + y (若右值未溢出)
(x | y) > x + y (若右值溢出)
|x - y| <= (x ^ y)編碼

2.4 絕對值函數
設y = x >> 31(符號擴展), 則x的絕對值(abs)可經過如下表達式計算:
(x ^ y) - y
(x + y) ^ y
x - (2x & y)
nabs可經過如下表達式計算:
y - (x ^ y)
(y - x) ^ y
(2x & y) - x
若CPU能快速計算一個數與(+/-)1的乘積, 可使用((x >> 30) | 1) * xspa

2.5 計算兩無符號數的平均值(保證無溢出)
(x & y) + ((x ^ y) >> 1) (x & y爲二者相同部分, x ^ y爲二者差別部分)
(x | y) - ((x ^ y) >> 1) (x | y即x & y加上x ^ y)code

2.6 符號擴展
符號擴展(sign extension)的含義是在比特流中肯定某個位元做爲符號位並將其值向左傳播覆蓋左側位元的原有值.
以第七位做爲符號位爲例:
((x + 0x80) & 0xFF) - 0x80
((x & 0xFF) ^ 0x80) - 0x80
(x & 0x7F) - (x & 0x80)blog

2.11 將值爲0的位段解碼爲2的n次方
有些時候值爲0是無心義的, 此時能夠將0表示爲2^n, 解碼時需將其轉換爲2^n, i.e. ((x - 1) & 0xFF) + 1 (使用0表示256).原型

2.20 互換寄存器中的值
不使用額外空間互換兩個寄存器的值
x = x ^ y
y = x ^ y
x = x ^ y
更進一步, 根據掩碼m交換兩個寄存器中對應的位
x = x ^ y
y = y ^ (x & m)
x = x ^ y數學

2.22 布爾函數分解式
本文的布爾函數是指自變量爲布爾值且函數值亦爲布爾值的函數.
定理: 若f(x, y, z)爲有三個自變量的布爾函數, 則其可分解爲g(x, y) ^ zh(x, y)的形式, 其中g(x, y)與h(x, y)分別爲帶兩個自變量的布爾函數.

第三章  2的冪邊界

3.1 將x上調/下調爲2的已知次冪的倍數
下調: x & (-2^n)
上調: (x + 2^n - 1) & (-2^n)

3.2 將x上調/下調到2的冪
下調: 核心思想是將最左的置位的位元向右傳播.

1 unsigned floor(unsigned x) 2 { 3     x = x | (x >> 1); 4     x = x | (x >> 2); 5     x = x | (x >> 4); 6     x = x | (x >> 8); 7     x = x | (x >> 16); 8     return x - (x >> 1); 9 }

 


上調: 相似下調算法.

 1 unsigned ceiling(unsigned x)  2 {  3     x = x - 1;  4     x = x | (x >> 1);  5     x = x | (x >> 2);  6     x = x | (x >> 4);  7     x = x | (x >> 8);  8     x = x | (x >> 16);  9     return x + 1; 10 }

 

第四章  算術邊界
略.

第五章  位計數

5.1 統計值爲1的位元個數
分治法(Divide&Conquer):
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
x = (x & 0x0000FFFF) + ((x >> 16) & 0x0000FFFF);
上式爲原型式, 還有如下寫法:

1 int pop(unsigned x) 2 { 3     x = x - ((x >> 1) & 0x55555555); 4     x = (x & 0x33333333) + ((x >> 2) & 0x33333333); 5     x = (x + (x >> 4)) & 0x0F0F0F0F; 6     x = x + (x >> 8); 7     x = x + (x >> 16); 8     return x & 0x0000003F; 9 }

 

5.2 字組的奇偶性(統計字組中值爲1的位元個數的奇偶)
y = x ^ (x >> 1);
y = y ^ (y >> 2);
y = y ^ (y >> 4);
y = y ^ (y >> 8);
y = y ^ (y >> 16);

5.3 前導零的計數(nlz, number of leading zero)
太多了, 循環, 二分, 查表, 不記了. 還有一種利用浮點數計算前導零個數:

 1 int nlz(unsigned k)  2 {  3     union {  4         unsigned asInt[2];  5         double asDouble;  6     };  7     int n;  8     asDouble = (double)k + 0.5;  9     n = 1054 - (asInt[LE] >> 20); //小端字節序LE爲1, 大端字節序LE爲0
10     return n; 11 }


比較字組前導零個數
一種無需計算nlz的方式:
nlz(x) == nlz(y)當且僅當(x ^ y) <= (x & y)
nlz(x) < nlz(y)當且僅當(x & ~y) > y
nlz(x) <= nlz(y)當且僅當(y & ~x) <= x

5.4 後綴零(ntz, number of tail zero)的計數
32 - nlz(~x & (x - 1))
還有一種[Gaud]算法:

 1 int ntz(unsigned x)  2 {  3     unsigned y, bz, b4, b3, b2, b1, b0;  4     y = x & -x; //找到最後側非零比特並將其左側全部比特置零
 5     bz = y ? 0 : 1;  6     b4 = (y & 0x0000FFFF) ? 0 : 16;  7     b3 = (y & 0x00FF00FF) ? 0 : 8;  8     b2 = (y & 0x0F0F0F0F) ? 0 : 4;  9     b1 = (y & 0x33333333) ? 0 : 2; 10     b0 = (y & 0x55555555) ? 0 : 1; 11     return bz + b4 + b3 + b2 + b1 + b0; 12 }

 


理解上面的算法後能夠更進一步[Seal]算法: 核心思想是將x的可能性從2 ^ 32壓縮到一個稠密集合(dense set), 而後查表.

 1 int ntz(unsigned x)  2 {  3     static char table[64] =
 4     {  5         32, 0, 1, 12, 2, 6, u, 13,  6         3, u, 7, u, u, u, u, 14,  7         10, 4, u, u, 8, u, u, 25,  8         u, u, u, u, u, 21, 27, 15,  9         31, 11, 5, u, u, u, u, u, 10         9, u, u, 24, u, u, 20, 26, 11         30, u, u, u, u, 23, u, 19, 12         29, u, 22, 18, 28, 17, 16, u 13     }; //u爲未定義值 14     //(x & -x)後必然是2的冪, 所以至關於對0x0450FBAF執行移位操做 15     //選取0x0450FBAF是由於可使用移位操做(0x0450FBAF = 17 * 65 * 65535)代替大數乘法, 更進一步優化算法
16     x = (x & -x) * 0x0450FBAF; 17     return table[x >> 26]; 18 }

 


使用德布魯因循環序列的算法:
德布魯因序列爲0000 0100 1101 0111 0110 0101 0001 1111

 1 int ntz(unsigned x)  2 {  3     static char table[32] =
 4     {  5         0, 1, 2, 24, 3, 19, 6, 25,  6         22, 4, 20, 10, 16, 7, 12, 26,  7         31, 23, 18, 5, 21, 9, 15, 11,  8         30, 17, 8, 14, 29, 13, 28, 27
 9     }; 10     if (x == 0) 11         return 32; 12     x = (x & -x) * 0x04D7651F; //0x04D7651F正好爲((2047 * 5 * 256 + 1) * 31)
13     return table[x >> 27]; 14 }

 

第六章  在字組中搜索位串
略.

 

第七章  重排位元與字節

7.1 反轉位元與字節
反轉位元: 可利用位計數章節中的種羣算法
x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;

7.2 亂序排列位元
x = (x & 0x0000FF00) << 8 | (x >> 8) & 0x0000FF00 | x & 0xFF0000FF;
x = (x & 0x00F000F0) << 4 | (x >> 4) & 0x00F000F0 | x & 0xF00FF00F;
x = (x & 0x0C0C0C0C) << 2 | (x >> 2) & 0x0C0C0C0C | x & 0xC3C3C3C3;
x = (x & 0x22222222) << 1 | (x >> 1) & 0x22222222 | x & 0x99999999;

第八章  乘法
略.

第九章  整數除法
略.

第十章  除數爲常量的整數除法
略.

第十一章  初等函數

11.1 整數平方根
牛頓法開平方

 1 int isqrt(unsigned x)  2 {  3     unsigned x1;  4     int s, g0, g1;  5      6     if (x <= 1)  7         return x;  8     s = 1;  9     x1 = x - 1; 10     if (x1 > 65535) 11     { 12         s = s + 8; 13         x1 = x1 >> 16; 14     } 15     if (x1 > 255) 16     { 17         s = s + 4; 18         x1 = x1 >> 8; 19     } 20     if (x1 > 15) 21     { 22         s = s + 2; 23         x1 = x1 >> 4; 24     } 25     if (x1 > 3) 26     { 27         s = s + 1; 28     } 29     g0 = 1 << s;j 30     g1 = (g0 + (x >> s)) >> 1; 31     32     while (g1 < g0) 33     { 34         g0 = g1; 35         g1 = (g0 + (x / g0)) >> 1; 36     } 37     return g0; 38 }

 


二分查找

 1 int isqrt(unsigned x)  2 {  3     unsigned a, b, m;  4     a = 1;  5     b = (x >> 5) + 8;  6     is (b > 65535)  7         b = 65535;  8     do
 9     { 10         m = (a + b) >> 1; 11         if (m * m > x) 12             b = m - 1; 13         else
14             a = m + 1; 15     } while (b >= a); 16     return a - 1; 17 }

 

11.2 整數立方根

 1 int icbrt(unsigned x)  2 {  3     int s;  4     unsigned y, b;  5     y = 0;  6     for (s = 30; s >= 0; s = s - 3)  7     {  8         y = 2 * y;  9         b = (3 * y * (y + 1) + 1) << s; 10         if (x >= b) 11         { 12             x = x - b; 13             y = y + 1; 14         } 15     } 16     return y; 17 }

 

11.4 整數對數
以2爲底的整數對數:
log2(x) = 31 -nlz(x)
注意當x = 0時定義爲-1(雖然數學意義上不成立), 方便運算.
以10爲底的整數對數:

 1 int ilog10(unsigned x)  2 {  3     int i;  4     static unsigned table[11] = 
 5     {  6         0, 9, 99, 999, 9999,  7         99999, 999999, 9999999, 99999999, 99999999, 0xFFFFFFFF
 8     };  9     for (i = -1; ; i++) 10     { 11         if (x <= table[i + 1]) 12             return i; 13     } 14 }

 

第十二章  以特殊值爲底的數制
略.

第十三章  格雷碼
略.

第十四章  循環冗餘校驗

原理: 多項式整除

 1 unsigned crc32(unsigned char *msg)  2 {  3     int i, j;  4     unsigned byte, crc;  5     i = 0;  6     crc = 0xFFFFFFFF;  7     while (msg[i] != 0)  8     {  9         byte = msg[i]; 10         byte = reverse(byte); //reverse byte order
11         for (j = 0; j <= 7; j++) 12         { 13             if ((int)(crc ^ byte) < 0) 14                 crc = (crc << 1) ^ 0x04C11DB7; //CRC32
15             else
16                 crc = crc << 1; 17             byte = byte << 1; 18         } 19         i++; 20     } 21     return reverse(-crc); 22 }


查表法

 1 unsigned crc32(unsigned char *msg)  2 {  3     int i, j;  4     unsigned byte, crc, mask;  5     static unsigned table[256];  6     /* set up table if necessary */
 7     if (table[1] == 0)  8     {  9         for (byte = 0; byte <= 255; byte++) 10         { 11             crc = byte; 12             for (j = 7; j >= 0; j--) 13             { 14                 mask = -(crc & 1); 15                 crc = (crc >> 1) ^ (0xEDB88320 & mask); 16             } 17             table[byte] = crc; 18         } 19     } 20     /* calculate CRC */
21     i = 0; 22     crc = 0xFFFFFFFF; 23     while ((byte = msg[i]) != 0) 24     { 25         crc = (crc >> 8) ^ table[(crc ^ byte) & 0xFF]; 26         i++; 27     } 28     return -crc; 29 }

 

第十五章  糾錯碼
略.

第十六章  希爾伯特曲線
略.

第十七章  浮點數

17.1 IEEE格式
單精度(single)格式: s(1 bit) e(8 bit) f(23bit).
從高到低分別爲符號位, 指數位, 小數位. 當e與f取不一樣值時有:
e = 0, f = 0, 數值(+/-)0.
e = 0, f != 0, 數值(+/-)(2 ^ -126)(0, f).
0 < e < 255, 數值(+/-)(2 ^ (e - 127))(1, f).
e = 255, f = 0, 數值(+/-)無窮.
e = 255, f != 0, 數值NaN(not a number).
i.e. 將pi編碼爲單精度浮點格式: pi = 11.001010000111111011010101000100010000101b.
符號位爲0, 指數位爲128, 小數位爲10010010000111111011011, 即0 1000 0000 1001 0010 0001 1111 1011 011, 十六進制爲0x40490FDB.
對於單精度浮點數而言, 最後一位(unit in the last position, ulp)在1 / (2 ^ 24)到1 / (2 ^ 23)之間, 這也是單精度浮點數的精度.
所以單精度浮點數可精確表示的整數範圍是-2 ^ 24到+2 ^ 24, 大於此範圍的部分整數也能夠精確表示.
雙精度(double)格式相似單精度, 只是位數區別: s(1 bit) e(11 bit) f(52bit).

17.4 估算平方根倒數

 1 float rsqrt(float x0)  2 {  3     union  4     {  5         int ix;  6         float x;  7     };  8     x = x0;  9     float xhalf = 0.5f * x; //或直接指數減1: int ihalf = ix - 0x00800000;
10     ix = 0x5F375A82 - (ix >> 1); //初始猜想值
11     x = x * (1.5f - xhalf * x * x); //牛頓法
12     return x; 13 }


原理: 設x = 2 ^n * (1 + f), 則平方根倒數y = 2 ^ (-n / 2) * (1 + f) ^ (-1 / 2).
因x的指數位127 + n, y的指數爲127 - n / 2 = 190.5 - (127 + n) / 2, 即將x右移一位後從190中減去就可估算y值.
求得第一次猜想值後再用牛頓法求解.

第十八章  素數公式徹底看不懂, 略...

相關文章
相關標籤/搜索