Skip to content

Instantly share code, notes, and snippets.

@intsuc
Last active August 11, 2024 03:01
Show Gist options
  • Save intsuc/1540b95ecc616b951ab64ce4c1ddce58 to your computer and use it in GitHub Desktop.
Save intsuc/1540b95ecc616b951ab64ce4c1ddce58 to your computer and use it in GitHub Desktop.
素数積による疑似ビットベクトル表現とその演算

素数積による疑似ビットベクトル表現とその演算

コマンドには整数のビット演算が存在しないため、ビット演算を行いたい場合には1ビットずつ処理を行う必要があります。 素数積による疑似ビットベクトル表現を用いることで、ビットの密度を犠牲にして一部のビット演算を高速に行うことができます。

素数積による疑似ビットベクトル表現

以下のように、素数の積を用いて長さ8のビットベクトルを表現することができます。 下位 $i$ 番目のビットを $b_i$$3$ から数えて $i$ 番目の素数を $p_i$ とします。

$$ \begin{align*} &\prod_{i=0}^{7} (b_i \ ? \ p_i : 1) \\\ = \ & (b_7 \ ? \ p_7 : 1) (b_6 \ ? \ p_6 : 1) (b_5 \ ? \ p_5 : 1) (b_4 \ ? \ p_4 : 1) (b_3 \ ? \ p_3 : 1) (b_2 \ ? \ p_2 : 1) (b_1 \ ? \ p_1 : 1) (b_0 \ ? \ p_0 : 1) 1 \\\ = \ & (b_7 \ ? \ 23 : 1) (b_6 \ ? \ 19 : 1) (b_5 \ ? \ 17 : 1) (b_4 \ ? \ 13 : 1) (b_3 \ ? \ 11 : 1) (b_2 \ ? \ 7 : 1) (b_1 \ ? \ 5 : 1) (b_0 \ ? \ 3 : 1) 1 \end{align*} $$

例えば、ビットベクトル $\texttt{10101011}$

$$ \begin{align*} & (1 \ ? \ 23 : 1) (0 \ ? \ 19 : 1) (1 \ ? \ 17 : 1) (0 \ ? \ 13 : 1) (1 \ ? \ 11 : 1) (0 \ ? \ 7 : 1) (1 \ ? \ 5 : 1) (1 \ ? \ 3 : 1) 1 \\\ = \ & 23 * 17 * 11 * 5 * 3 * 1 \\\ = \ & 64515 \end{align*} $$

と表現されます。

最大のビットベクトルは $\texttt{11111111}$ でその表現は $23 * 19 * 17 * 13 * 11 * 7 * 5 * 3 * 1 = 111546435$ です。 32ビット符号付き整数の最大値は $2^{31} - 1 = 2147483647 \ (\ge 111546435)$ なので、全てのビットベクトルは32ビット符号付き整数で表現できます。

Note

9番目のビットとして素数 $29$ を追加すると最大値が $29 * 23 * 19 * 17 * 13 * 11 * 7 * 5 * 3 * 1 = 3234846615 \ (\ge 2147483647)$ になり、32ビット符号付き整数では表現できなくなります。

NOT(~a)

素数積による疑似ビットベクトル表現 $a$$\texttt{11111111}$ を割ることで~aを計算することができます。

例えば、 $a = \texttt{10101011}$ とすると、

$$ \begin{align*} & \texttt{11111111} \ / \ \texttt{10101011} \\\ = \ & p_7 p_6 p_5 p_4 p_3 p_2 p_1 p_0 \ / \ p_7 p_5 p_3 p_1 p_0 \\\ = \ & p_6 p_4 p_2 \\\ = \ & \texttt{01010100} \end{align*} $$

AND(a & b)

素数積による疑似ビットベクトル表現 $a$$b$ について、 $a$$b$ の最大公約数 $gcd(a, b)$ を計算することでa & bを計算することができます。

例えば、 $a = \texttt{10101011}$$b = \texttt{01110101}$ とすると、

$$ \begin{align*} & \mathit{gcd}(\texttt{10101011}, \texttt{01110101}) \\\ = \ & \mathit{gcd}(p_7 p_5 p_3 p_1 p_0, p_6 p_5 p_4 p_2 p_0) \\\ = \ & p_5 p_0 \\\ = \ & \texttt{00100001} \end{align*} $$

OR(a | b)

素数積による疑似ビットベクトル表現 $a$$b$ について、 $a$$b$ の最小公倍数 $lcm(a, b)$ を計算することでa | bを計算することができます。

例えば、 $a = \texttt{10101011}$$b = \texttt{01110000}$ とすると、

$$ \begin{align*} & \mathit{lcm}(\texttt{10101011}, \texttt{01110000}) \\\ = \ & \mathit{lcm}(p_7 p_5 p_3 p_1 p_0, p_6 p_5 p_4) \\\ = \ & p_7 p_6 p_5 p_4 p_3 p_1 p_0 \\\ = \ & \texttt{11111011} \end{align*} $$

XOR(a ^ b)

素数積による疑似ビットベクトル表現 $a$$b$ について、 $a$$b$ の最小公倍数 $lcm(a, b)$$a$$b$ の最大公約数 $gcd(a, b)$ で割ることでa ^ bを計算することができます。

例えば、 $a = \texttt{00000011}$$b = \texttt{00000101}$ とすると、

$$ \begin{align*} & \mathit{lcm}(\texttt{00000011}, \texttt{00000101}) / \mathit{gcd}(\texttt{00000011}, \texttt{00000101}) \\\ = \ & \mathit{lcm}(p_1 p_0, p_2 p_0) / \mathit{gcd}(p_1 p_0, p_2 p_0) \\\ = \ & p_2 p_1 p_0 / p_0 \\\ = \ & p_2 p_1 \\\ = \ & \texttt{00000110} \end{align*} $$

ビットマスクテスト(a & b == b)

素数積による疑似ビットベクトル表現 $a$$b$ について、 $a$$b$ で割り切れるかどうか調べることでa & b == bを計算することができます。

例えば、 $a = \texttt{10101011}$$b = \texttt{00001011}$ とすると、

$$ \begin{align*} & \texttt{10101011} \ \% \ \texttt{00001011} \\\ = \ & p_7 p_5 p_3 p_1 p_0 \ \% \ p_3 p_1 p_0 \\\ = \ & 0 \end{align*} $$

結果が $0$ になったので、a & b == bです。

一方、 $a = \texttt{10101011}$$b = \texttt{00001111}$ とすると、

$$ \begin{align*} & \texttt{10101011} \ \% \ \texttt{00001111} \\\ = \ & p_7 p_5 p_3 p_1 p_0 \ \% \ p_3 p_2 p_1 p_0 \\\ = \ & 990 \end{align*} $$

結果が $0$ 以外になったので、a & b == bではありません。

定数によるビットマスクテスト(a & B == B)

先述したビットマスクテストでは $a$ に対して%=演算を行う必要があり、 $a$ の値が変化してしまいます。 そのため、 $a$ の値を保持したい場合、一旦別のスコアに $a$ をコピーする必要があります。 ビットマスク $B$ が定数である場合、 $B$ の逆数を用いて $a$ を保持したままビットマスクテストを行うことができます。

$B$ の逆数とは $B B^{-1} = 1$ を満たす $B^{-1}$ のことです。 32ビット整数の場合、以下のようにニュートン法を用いて $B^{-1}$ を事前計算することができます。1 (全てのビットベクトルと逆数の対応表)

const fn inverse(x: i32) -> i32 {
    const fn f(x: i32, y: i32) -> i32 {
        y.wrapping_mul(2.wrapping_sub(y.wrapping_mul(x)))
    }
    f(x, f(x, f(x, 3i32.wrapping_mul(x) ^ 2)))
}

$a$$B$ で割り切れる場合かつその場合に限り、 $a B^{-1} \in [1, p_7p_6p_5p_4p_3p_2p_1p_0 / B]$ が成立します。 また、 $a B^{-1} B = a$ が成立します。 そのため、以下のような疑似コードで $a$ を保持したままビットマスクテストを行うことができます。

$$ \begin{align*} & a \ \texttt{*=} \ B^{-1} \\\ & \texttt{if} \ a \in [1, p_7p_6p_5p_4p_3p_2p_1p_0 / B] \\\ & \quad \texttt{then} \ \text{\{\texttt{a \& B == B} の場合の処理\}} \\\ & \quad \texttt{else} \ \text{\{\texttt{a \& B != B} の場合の処理\}} \\\ & a \ \texttt{*=} \ B \end{align*} $$

$B^{-1}, p_7p_6p_5p_4p_3p_2p_1p_0 / B, B$ は全て定数です。

$a$ の特定のビット $p_k$$\texttt{1}$ であることが分かれば、 $a$$p_k$ で割ることでそれを $\texttt{0}$ にすることができます。 同様に、 $a$ の特定のビット $p_k$$\texttt{0}$ であることが分かれば、 $a$$p_k$ を掛けることでそれを $\texttt{1}$ にすることができます。

ビット数の拡張

素数の $2$ を用いることで、疑似ビットベクトル表現のビット数を1増やすことができます。 ただし、 $2$ には逆数が存在しないため定数によるビットマスクテストにおいて $2$ に対応するビットを同様に調べることはできなくなります。

また、 $-1$ を用いることでも疑似ビットベクトル表現のビット数を1増やすことができますが、符号の取り扱いには注意が必要です。

Footnotes

  1. https://lemire.me/blog/2017/09/18/computing-the-inverse-of-odd-integers/

00000000 | 1
00000001 | -1431655765
00000010 | -858993459
00000011 | -286331153
00000100 | -1227133513
00000101 | 1022611261
00000110 | -1963413621
00000111 | -654471207
00001000 | -1171354717
00001001 | 1041204193
00001010 | 1483715975
00001011 | 1926227757
00001100 | 1059797125
00001101 | -1078390057
00001110 | 211959425
00001111 | 1502308907
00010000 | -991146299
00010001 | -1762037865
00010010 | -1057222719
00010011 | -352407573
00010100 | -755159085
00010101 | -251719695
00010110 | -151031817
00010111 | -50343939
00011000 | -90104209
00011001 | 1401621029
00011010 | -877014301
00011011 | 1139317665
00011100 | -1240005543
00011101 | -413335181
00011110 | -1965988027
00011111 | 776326423
00100000 | -252645135
00100001 | -84215045
00100010 | -50529027
00100011 | -16843009
00100100 | 1191041351
00100101 | 1828669549
00100110 | -620785189
00100111 | 1224727369
00101000 | -1584774029
00101001 | -1959913775
00101010 | -1175948265
00101011 | -391982755
00101100 | -1453529803
00101101 | 947145831
00101110 | -2008692879
00101111 | -669564293
00110000 | 1962858357
00110001 | 654286119
00110010 | -1325415247
00110011 | 989850683
00110100 | -1560291933
00110101 | -520097311
00110110 | -2030045305
00110111 | 754973997
00111000 | -1773816193
00111001 | 840383701
00111010 | -2072750157
00111011 | -690916719
00111100 | -2094102583
00111101 | 733621571
00111110 | -2136807435
00111111 | -712269145
01000000 | 678152731
01000001 | -1205604855
01000010 | -723362913
01000011 | -241120971
01000100 | -516687795
01000101 | -172229265
01000110 | -103337559
01000111 | -34445853
01001000 | 842553393
01001001 | 280851131
01001010 | 1886497597
01001011 | -802823233
01001100 | 733931527
01001101 | -1187011923
01001110 | -1571200613
01001111 | -1955389303
01010000 | -278216505
01010001 | -92738835
01010010 | -55643301
01010011 | -18547767
01010100 | -39745215
01010101 | -13248405
01010110 | -7949043
01010111 | -2649681
01011000 | -1587098699
01011001 | -1960688665
01011010 | -1176413199
01011011 | -392137733
01011100 | 386838371
01011101 | 1560601889
01011110 | -781625785
01011111 | 1171113837
01100000 | -465398933
01100001 | -1586788743
01100010 | -1811066705
01100011 | -2035344667
01100100 | -1293619075
01100101 | 1000449407
01100110 | -258723815
01100111 | -1517897037
01101000 | -1213663711
01101001 | 1027101195
01101010 | 616260717
01101011 | 205420239
01101100 | 1053752983
01101101 | -1080404771
01101110 | 1928737515
01101111 | 642912505
01110000 | -1026946217
01110001 | -1773971171
01110010 | 1512597675
01110011 | 504199225
01110100 | -760273359
01110101 | -253424453
01110110 | -1011048131
01110111 | -1768671809
01111000 | -93358747
01111001 | 1400536183
01111010 | 1699315169
01111011 | 1998094155
01111100 | -1240470477
01111101 | -413490159
01111110 | 1469892823
01111111 | -941691491
10000000 | -373475417
10000001 | -1556147571
10000010 | 1643291835
10000011 | 547763945
10000100 | -53353631
10000101 | -1449440309
10000110 | 848322733
10000111 | -1148881521
10001000 | -424403883
10001001 | -141467961
10001010 | -1802867695
10001011 | 830699867
10001100 | 1166504387
10001101 | 1820490561
10001110 | -1484686041
10001111 | -494895347
10010000 | -1350257277
10010001 | -450085759
10010010 | 1447935463
10010011 | -949010611
10010100 | 1647806373
10010101 | 549268791
10010110 | 2047548193
10010111 | -749139701
10011000 | -1684556951
10011001 | -1993174749
10011010 | 522082069
10011011 | -1257628409
10011100 | -240650993
10011101 | -1511872763
10011110 | -1766117117
10011111 | -2020361471
10100000 | 735966263
10100001 | 1676977853
10100010 | 1865180171
10100011 | 2053382489
10100100 | -508428719
10100101 | -1601132005
10100110 | -960679203
10100111 | -320226401
10101000 | 1238260741
10101001 | -1018902185
10101010 | -611341311
10101011 | -203780437
10101100 | -436672365
10101101 | -145557455
10101110 | -87334473
10101111 | -29111491
10110000 | -1595297709
10110001 | -531765903
10110010 | -1178053001
10110011 | -1824340099
10110100 | 1612800597
10110101 | 537600199
10110110 | -1395426799
10110111 | 966513499
10111000 | 1416779225
10111001 | 1903915507
10111010 | 283355845
10111011 | -1337203817
10111100 | -1024736481
10111101 | -341578827
10111110 | 654046163
10111111 | 1649671153
11000000 | 1336648861
11000001 | -986106145
11000010 | -591663687
11000011 | -197221229
11000100 | 2031650107
11000101 | -754439063
11000110 | -1311656897
11000111 | -1868874731
11001000 | -1830744329
11001001 | -2041903875
11001010 | -1225142325
11001011 | -408380775
11001100 | 965598609
11001101 | 321866203
11001110 | 1052113181
11001111 | 1782360159
11010000 | -1879473455
11010001 | -2058146917
11010010 | -375894691
11010011 | 1306357535
11010100 | -1495629721
11010101 | 933112525
11010110 | 559867515
11010111 | 186622505
11011000 | -2123119085
11011001 | -2139362127
11011010 | -424623817
11011011 | 1290114493
11011100 | -916869483
11011101 | -305623161
11011110 | -1901360815
11011111 | 797868827
11100000 | 1847142349
11100001 | -815941649
11100010 | 1228421929
11100011 | -1022181789
11100100 | 877444235
11100101 | 1724137177
11100110 | 175488847
11100111 | -1373159483
11101000 | -612981113
11101001 | -1635982803
11101010 | -1840583141
11101011 | -2045183479
11101100 | -701135487
11101101 | -233711829
11101110 | 1577759821
11101111 | -905735825
11110000 | 142087873
11110001 | -1384293141
11110010 | 1746404493
11110011 | 582134831
11110100 | -593268489
11110101 | -197756163
11110110 | -977647157
11110111 | -1757538151
11111000 | -377534493
11111001 | -125844831
11111010 | -1793493817
11111011 | 833824493
11111100 | -53933499
11111101 | -17977833
11111110 | -869780159
11111111 | -1721582485
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment