線形合同法であるseedが0からいくつ進めたものかを得る
- 続きの記事: 線形合同法であるseedが0からいくつ進めたものかを得る その2 - oupoの日記
- 続きの続きの記事: 線形合同法であるseedが0からいくつ進めたものかを得る その3 (法が2のべきとは限らない一般の場合) - oupoの日記
sid-searchを作り直してみた - oupoの日記でも使ったアルゴリズムを解説する。
問題
線形合同法の漸化式に使われる関数(x * A + B) mod 2^Nをf(x)とおき、この線形合同法の数列は最大周期であるとする。
与えられたseed、xに対してf^i(0) = xとなるi ∈ 0...2^Nを求めたい。
思考
a_nは偶数、奇数を繰り返すことからxの偶奇からiの偶奇がわかる。iの第0ビットがわかったので、同じことを繰り返して第1ビット、第2ビット...と次々に求めていけないだろうかと考える。
- iが偶数の場合、0にf^2を何回適用すればxになるかを求めて、それを2倍すれば0にfを何回適用すればxになるかが得られる。
- iが奇数の場合、0にf^2を何回適用すればf(x)になるかを求めて、それを2倍して1引けば0にfを何回適用すればxになるかが得られる。
この考えを使って繰り返せばよい。
ここで、f^i(0) = xとなるiの偶奇はxの偶奇から得られたが、fの代わりにf^2を当てはめた場合のiの偶奇は得られるか?
xの第1ビットから分かるのではないかと予想できる。
実際:
n | a_n | a_n mod 4 |
---|---|---|
0 | 0x00000000 | 0b00 |
2 | 0x6c078966 | 0b10 |
4 | 0xdbffe6dc | 0b00 |
6 | 0x992415e2 | 0b10 |
8 | 0x895277f8 | 0b00 |
10 | 0x014d329e | 0b10 |
12 | 0x75ab8f54 | 0b00 |
14 | 0xe0d45b9a | 0b10 |
16 | 0xe69948f0 | 0b00 |
18 | 0x964b4cd6 | 0b10 |
一般に、g=f^{2^k}とおいたとき、g^i(0) = xとなるiを2で割った余りはxの第kビットに一致する。
これは
k=1を例にとって解説してみる。
n | a_n mod 2 | a_n mod 4 |
---|---|---|
0 | 0b0 | 0b00 |
1 | 0b1 | |
2 | 0b0 | |
3 | 0b1 |
a_n mod 2の値からa_n mod 4の第0ビットは分かる。
n | a_n mod 2 | a_n mod 4 |
---|---|---|
0 | 0b0 | 0b00 |
1 | 0b1 | 0b?1 |
2 | 0b0 | 0b?0 |
3 | 0b1 | 0b?1 |
a_2 mod 4が0b00だとすると周期が4なことに反してしまうのでa_2 mod 4 = 0b10である。
n | a_n mod 2 | a_n mod 4 |
---|---|---|
0 | 0b0 | 0b00 |
1 | 0b1 | 0b?1 |
2 | 0b0 | 0b10 |
3 | 0b1 | 0b?1 |
よって0を初項、f^2を漸化式とする数列はmod 4で見たとき0b00, 0b10を繰り返す。
プログラム
# f^m(0) = sとなるm ∈ 0...2^Nを求める def seed_to_index(s) seed_to_index0(s, 0) end # g = f^{2^n}としてg^m(0) = sとなるm ∈ 0...2^{N-n}を求める def seed_to_index0(s, n) if n == N 0 elsif ((s >> n) & 1) != 0 (seed_to_index0(step_seed(s, 2**n), n + 1) * 2 - 1) % 2**(N-n) else seed_to_index0(s, n + 1) * 2 end end
前回の記事では下から5行目がseed_to_index0(step_seed(s, -2**n), n + 1) * 2 + 1となっていたのだが、-2^n進める定数をあらかじめ表にしておくより、2^n進める定数を表にしておく方が自然なので今回の方が好みだ。