Home
Blog
Products
Profile
Study
Collatz
© 2024 Oizumi Yuta

p - 1 法について考える

2025-1-5

フェルマー数 F10=2210+1F_{10} = 2^{2^{10}} + 1F10​=2210+1 が素数でないことを示すには、p−1p - 1p−1 法と試し割り法ではどちらが早いか。

  • p−1p - 1p−1 法
n = 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137217
非自明な約数: 6487031809
実行時間: 0.015044 秒
  • 試し割り法
n = 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137217
最も小さな素因数: 45592577
実行時間: 4.262128 秒

圧倒的に p−1p - 1p−1 法が早い。これはどういうことなのだろう。

p−1p - 1p−1 法とは

適当な a, k∈Na,\ k \in \mathbb{N}a, k∈N を与えて

gcd⁡(ak−1, n)\gcd(a^k - 1,\ n)gcd(ak−1, n)

を計算する。

1<gcd⁡(ak−1, n)<n1 \lt \gcd(a^k - 1,\ n) \lt n1<gcd(ak−1, n)<n

のとき、nnn の非自明な約数を得る。

計算例

n=F10n = F_{10}n=F10​ に対して次のように a, ka,\ ka, k をとると

a=3,k=lcm(1, 2, ⋯ , 4100)a = 3, \\ k = \textrm{lcm}(1,\ 2,\ \cdots,\ 4100)a=3,k=lcm(1, 2, ⋯, 4100)

gcd⁡(ak−1, n)=6487031809\gcd(a^k - 1,\ n) = 6487031809gcd(ak−1, n)=6487031809 となった。ここで 648703180964870318096487031809 は素数であり、

6487031809−1=214×32×29×37×416487031809 - 1 = 2^{14} \times 3^2 \times 29 \times 37 \times 416487031809−1=214×32×29×37×41

と素因数分解される。

疑問

なぜ a, ka,\ ka, k を適当にとって gcd⁡(ak−1, n)=6487031809\gcd(a^k - 1,\ n) = 6487031809gcd(ak−1, n)=6487031809 のように素因数が現れるのだろうか。

考察

速度比較のとき、最初から a, ka,\ ka, k を与えてしまっていた。a, ka,\ ka, k は探索しなければならないパラメータである。そこで a, ka,\ ka, k をパラメータ化して計測した結果:

| a = 3 | b = 4100 | d = 6487031809 成功
実行時間: 3.109131 秒

a は小さい範囲 2≤a≤102 \le a \le 102≤a≤10 を動き、bbb は p−1p - 1p−1 が 500050005000 までの素因数を含むことを許容した。

def find_factor():
    isSuccess = False

    for a in range(2, 10):
        for b in range(10, 5000, 10):
            d = p_minus_1(n, a, b)
            # 判定
            if 1 < d < n:
                print(f'| a = {a} | b = {b} | d = {d} 成功')
                isSuccess = True
                break
            elif d == 1:
                # b を大きくする必要あり
                print(f'| a = {a} | b = {b} | d = 1 失敗')
            elif d == n:
                # これ以上 b を大きくしても無駄なので a をインクリメントする必要あり
                print(f'| a = {a} | b = {b} | d = n 失敗')
                break
        else:
            continue
        if isSuccess:
            break

試し割り法より早かった。

いろいろな整数で速度比較

比較的小さな数

8226679=127×211×3078226679 = 127 \times 211 \times 3078226679=127×211×307

  • p−1p - 1p−1 法
n = 8226679
| a = 2 | b = 10 | d = 26797 成功
実行時間: 0.000306 秒

※ 267972679726797 は合成数。

  • 試し割り法
n = 8226679
最も小さな素因数: 127
実行時間: 0.000010 秒

比較的大きな数

99230305792491364911811=814432939613×12183975004799230305792491364911811 = 814432939613 \times 12183975004799230305792491364911811=814432939613×121839750047

両者ともに 10 秒以内に終わらない。