考察
a,b を互いに素とします。整数 x,y が x3≡x(moda),y3≡y(modb) を満たすとき、中国剰余定理より n≡x(moda),n≡y(modb) を満たす n が mod ab で一意に存在します。
この n に対して、 n3≡x3≡x(moda),n3≡y3≡y(modb) が成り立つため、 n3−n≡0(moda),n3−n≡0(modb) が従います。同じく中国剰余定理より、 n3−n≡0(modab) すなわち n3≡n(modab) が成り立ちます。
逆に 0 以上 ab 未満の n3≡n(modab) を満たす整数 n を指定したとき、n から n≡x(moda),n≡y(modb) を満たす (x,y) が存在し、これは x3≡x(moda),y3≡y(modb) を満たします。
今までの議論により、次のような解法が正当です。
- M を素因数分解して M=p1a1p2a2⋯pkak とおく。
- 各 i=1,2,⋯,k に対して、mod piai での x3≡x の解をすべて求める。
- Garner のアルゴリズムを用いて、解をすべて復元する。
ここの計算量は、解の個数を K 個として、 O(KlogM) 時間かかります。しかし、問題点として、まだ 2. の部分のステップに時間をかけすぎてしまうことがあります。そこで、素数べき pa に対してその解の特徴を考察します。
p≥3 のとき
p≥3 とします。 x3−x=(x−1)x(x+1)≡0(modpa) の解は 0,1,pa−1 があります。しかし、実はそれだけであることが示されます。
もし他に解 x があるとしたら、 (x−1)x(x+1) は pa の倍数になります。よって、 x−1,x,x+1 のどれか 1 つは pk の倍数になります。ここで、 x=0,1,pa−1 なので、 1≤k≤a−1 です。
しかし、 p≥3 であるので、 (x−1)x(x+1) であって p の倍数であるものは 1 つしかなく、 (x−1)x(x+1) はよって pa の倍数になりません。
よって、解は 0,1,pa−1 の 3 つだけです。
p=2 のとき
a=1 のとき、明らかに解は x=0,1 の 2 つです。
a=2 のとき、明らかに解は x=0,1,3 の 3 つです。
a≥3 のとき、解は x=0,1,2a−1−1,2a−1+1,2a−1 の 5 つです。 v(t) を、 t を 2 で割ることができる回数とします。いま、解であるための必要十分条件は v((x−1)x(x+1))≥a であることです。
x=0,1,2a−1 として、先ほどの議論をなぞると、 x−1,x+1 がともに 2 の倍数である必要があります。ここで、たとえば f(x−1)=k≥2 とすると、必ず f(x+1)=1 となります。もし f(x+1)≥2 とすると、連続する 3 つの整数の中に 4 の倍数が 2 個現れてしまい矛盾します。
k≤a−1 であったので、 f(x−1)=a−1 すなわち x=2a−1+1 が必要です。そしてこれは解になります。同様にして、 f(x+1)=a−1 の場合、 x=2a−1−1 が導かれます。これで p=2,a≥3 のとき解は x=0,1,2a−1−1,2a−1+1,2a−1 の 5 つであることが示されました。
まとめ
- p≥3 のとき、mod pa での解は x=0,1,pa−1 である
- p=2,a=1 のとき、mod pa での解は x=0,1 である
- p=2,a=2 のとき、mod pa での解は x=0,1,3 である
- p=2,a≥3 のとき、mod pa での解は x=0,1,2a−1−1,2a−1+1,2a−1 の 5 つである
これで mod pa での x3≡x の解が O(1) で求まり、この問題に答えることができます。
実装について
解の復元をする際、集合の直積の要素をすべて列挙する必要がありますが、これは再帰で求めることができます。また、Python を使用する場合は itertools.product が便利です。