Day 172-Q5 — 2026-10-08 | 二平方和(Pollard の rho で分解し、4k+1 型の素数を Cornacchia で a²+b² に、ガウス整数の積で組み立てる。表し方の数は 4∏(e+1))

2026-10-08 赤色 Master (Phase 8+) ★★★★★★★★★ 数論(焦点帯 M3)。$n\le10^{18}$ で $a$ を $0$ から $\sqrt n$ まで回すと $10^9$ 回。素因数分解と「素数ごとの二平方和」を掛け合わせる代数(ガウス整数 $\mathbb Z[i]$)に置き換える

問題

$T$ 個の整数 $n_1,\dots,n_T$ が与えられる。各 $n$ について、次を出力せよ。

  • $a^2+b^2=n$ を満たす整数の組 $(a,b)$(符号・順序を区別する)の個数 $r_2(n)$
  • $r_2(n)>0$ なら、$0\le a\le b$ かつ $a^2+b^2=n$ を満たす組を 1 つ

$r_2(n)=0$ なら 0 だけ、そうでなければ r_2(n) a b を 1 行に出力する。組が複数あるときはどれを出力してもよい。

入力形式

T
n_1
...
n_T

制約

#条件
1$1\le T\le100$
2$1\le n_i\le10^{18}$

入出力例

入力例1

5
25
3
9
50
1

出力例1

12 3 4
0
4 0 3
12 1 7
4 0 1

($25=0^2+5^2=3^2+4^2$。符号と順序を区別すると $(\pm5,0),(0,\pm5),(\pm3,\pm4),(\pm4,\pm3)$ の 12 組。$50$ は 1 7 でも 5 5 でもよい。$9=3^2$ は $3$ が 4k+3 型で偶数乗なので表せ、$3$ は奇数乗なので 0)

入力例2

2
999999999999999999
1000000000000000000

出力例2

0
76 472103424 881543168

($999999999999999999=3^4\cdot7\cdot11\cdot13\cdot19\cdot37\cdot52579\cdot333667$ は 4k+3 型の $7,11,19$ が奇数乗。$10^{18}=2^{18}5^{18}$ で $r_2=4\cdot19=76$。2 例とも、出力した組の $a^2+b^2$ を検算)

背景と出典

次はいずれも私の記憶による(本セッションでは原典を参照していない。式は $n\le2\times10^5$ の全数で数え上げと照合した)。

  • Fermat の二平方定理: 奇素数 $p$ が $a^2+b^2$ で表せるのは $p\equiv1\pmod4$ のときに限る
  • Jacobi の二平方の公式: $r_2(n)=4\,(d_1(n)-d_3(n))$($d_j$ は $j\bmod4$ の約数の個数)。分解 $n=2^{e_0}\prod p^{e}\prod q^{f}$($p\equiv1$、$q\equiv3$)で書くと、どれかの $f$ が奇数なら $0$、そうでなければ $4\prod(e+1)$
  • Cornacchia のアルゴリズム(1908): $x^2\equiv-1\pmod p$ の解 $x$ から始めて $(p,x)$ にユークリッドの互除法を行い、最初に $\sqrt p$ 未満になった余り $a$ で $p=a^2+b^2$ が得られる

組の構成は、ガウス整数 $\mathbb Z[i]$ で $p=(a+bi)(a-bi)$ と分かれることを使う。$n$ の各素因数の「$a+bi$」を掛け合わせれば $n=|z|^2$ となる $z$ が得られる。本問は自作。

概念図

a² + b² = 25 の格子点 12 個(r₂(25) = 4 × (2+1) = 12)と、p = 13 の Cornacchia 3+4i = (2+i)² p = 13 ≡ 1 (mod 4) を a² + b² に ① 平方非剰余 c = 2(2⁶ = 64 ≡ −1) ② x = 2³ = 8(8² = 64 ≡ −1 mod 13) ③ 互除法 (13, 8) → (8, 5) → (5, 3)   3 < √13 ≈ 3.6 で止める ④ 13 = 3² + 2² 合成数は素因数ごとの a+bi を掛ける (a+bi)(c+di) = (ac−bd) + (ad+bc)i 4k+3 型が奇数乗 → 表せない(r₂ = 0)

ヒント(クリックで段階的に開示)

ヒント1(方向性)

$a^2+b^2=|a+bi|^2$。$|zw|^2=|z|^2|w|^2$ なので、二平方和で表せる数どうしの積も二平方和で表せる($(a^2+b^2)(c^2+d^2)=(ac-bd)^2+(ad+bc)^2$)。素数ごとに表して掛ければよい。

ヒント2(アプローチ)

素因数ごとに場合分け: $2=1^2+1^2$、$p\equiv1\pmod4$ は Cornacchia、$q\equiv3\pmod4$ は偶数乗なら $q^{f/2}$ をそのまま掛ける($q^f=(q^{f/2})^2+0^2$)、奇数乗なら表せない。$10^{18}$ の素因数分解は Miller–Rabin と Pollard の rho。

ヒント3(誘導)

$x^2\equiv-1\pmod p$ は、平方非剰余 $c$($c^{(p-1)/2}\equiv-1$)を探して $x=c^{(p-1)/4}$。

a, b = p, x
while b > isqrt(p):
    a, b = b, a % b
return b, isqrt(p - b * b)      # p = b^2 + (残り)^2

個数は $4\prod_{p\equiv1}(e+1)$。

模範解答 (Python)

模範解答を表示する(まず自力で解いてから)
import sys
import random
from math import gcd, isqrt


def is_prime(n):
    if n < 2:
        return False
    for p in (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37):
        if n % p == 0:
            return n == p
    d, s = n - 1, 0
    while d % 2 == 0:
        d //= 2
        s += 1
    for a in (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37):   # この 12 個の底で n < 3×10^23 まで決定的(記憶)
        x = pow(a, d, n)
        if x == 1 or x == n - 1:
            continue
        for _ in range(s - 1):
            x = x * x % n
            if x == n - 1:
                break
        else:
            return False
    return True


def rho(n):
    # Pollard の rho(Brent 版、gcd を 128 回分まとめて取る)
    if n % 2 == 0:
        return 2
    while True:
        y, c, m = random.randrange(1, n), random.randrange(1, n), 128
        g = r = q = 1
        while g == 1:
            x = y
            for _ in range(r):
                y = (y * y + c) % n
            k = 0
            while k < r and g == 1:
                ys = y
                for _ in range(min(m, r - k)):
                    y = (y * y + c) % n
                    q = q * abs(x - y) % n
                g = gcd(q, n)
                k += m
            r *= 2
        if g == n:
            g = 1
            while g == 1:
                ys = (ys * ys + c) % n
                g = gcd(abs(x - ys), n)
        if g != n:
            return g


def factor(n, res):
    if n == 1:
        return
    if is_prime(n):
        res[n] = res.get(n, 0) + 1
        return
    r = isqrt(n)
    if r * r == n:                             # 平方数は先に開く(p^2 の rho は実測で遅かった)
        factor(r, res)
        factor(r, res)
        return
    d = rho(n)
    factor(d, res)
    factor(n // d, res)


def two_squares_prime(p):
    # p ≡ 1 (mod 4) を a^2 + b^2 に。x^2 ≡ -1 (mod p) を作り、Cornacchia(ユークリッドの互除法を √p 未満で止める)
    c = 2
    while pow(c, (p - 1) // 2, p) != p - 1:   # 平方非剰余 c を探す(半分の c が当たる)
        c += 1
    x = pow(c, (p - 1) // 4, p)               # x^2 = c^((p-1)/2) = -1
    a, b = p, x
    r = isqrt(p)
    while b > r:
        a, b = b, a % b
    return b, isqrt(p - b * b)


def gmul(x, y):
    # ガウス整数の積 (a+bi)(c+di)
    return (x[0] * y[0] - x[1] * y[1], x[0] * y[1] + x[1] * y[0])


def gpow(x, e):
    r = (1, 0)
    while e:
        if e & 1:
            r = gmul(r, x)
        x = gmul(x, x)
        e >>= 1
    return r


def solve(n):
    f = {}
    small = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47)
    for p in small:                            # 小さい素因数は割り算で先に取る(rho の負担を減らす)
        while n % p == 0:
            f[p] = f.get(p, 0) + 1
            n //= p
    factor(n, f)
    count = 4
    z = (1, 0)
    for p, e in f.items():
        if p % 4 == 3:
            if e % 2:
                return 0, None                 # 4k+3 型の素数が奇数乗 → 表せない
            z = (z[0] * p ** (e // 2), z[1] * p ** (e // 2))
        elif p == 2:
            z = gmul(z, gpow((1, 1), e))       # 2 = (1+i)(1-i)
        else:
            count *= e + 1                     # r2(n) = 4 × Π(e+1)(p ≡ 1 mod 4)
            z = gmul(z, gpow(two_squares_prime(p), e))
    a, b = sorted((abs(z[0]), abs(z[1])))
    return count, (a, b)


def main():
    data = sys.stdin.buffer.read().split()
    t = int(data[0])
    out = []
    for i in range(1, t + 1):
        n = int(data[i])
        cnt, ab = solve(n)
        out.append(f"{cnt} {ab[0]} {ab[1]}" if cnt else "0")
    print("\n".join(out))


if __name__ == "__main__":
    main()

計算量: 分解は Pollard の rho で最小の素因数 $p$ に対し期待 $O(p^{1/2})$ 回の乗算($n\le10^{18}$ なら最悪でも $p\approx10^9$ で数万回)。Cornacchia は互除法なので $O(\log p)$、平方非剰余探しは期待 2 回の累乗。$T=100$、すべて $10^9$ 前後の 2 素数の積という最悪寄りの入力で 0.85〜0.96 s(Python 3.14.7、M1 MacBook Pro、実測)。

Step-by-Step 解説

1積が閉じていることを使う

$(a^2+b^2)(c^2+d^2)=(ac-bd)^2+(ad+bc)^2$。これはガウス整数 $(a+bi)(c+di)$ の絶対値の 2 乗を取っただけ。だから $n$ を素因数ごとに表せれば、掛けて組み立てられる。

2素数ごとの場合分け
  • $2=|1+i|^2$
  • $p\equiv1\pmod4$: $p=|a+bi|^2$ と表せる(Fermat)。Cornacchia で $a,b$ を求める
  • $q\equiv3\pmod4$: $\mathbb Z[i]$ でも素数のまま。偶数乗なら $q^f=|q^{f/2}|^2$、奇数乗なら $n$ は表せない
3Cornacchia($p=13$ の例)

平方非剰余 $c=2$($2^6=64\equiv-1\pmod{13}$)。$x=2^3=8$($8^2=64\equiv-1$)。$\sqrt{13}\approx3.6$。互除法 $(13,8)\to(8,5)\to(5,3)$ で $3<3.6$ になったので止め、$a=3$、$b=\sqrt{13-9}=2$。$13=3^2+2^2$。

4個数 $r_2(n)$

$p\equiv1$ の素因数 $p^e$ は、$\mathbb Z[i]$ で $\pi^j\bar\pi^{e-j}$($j=0..e$)の $e+1$ 通りに分けられる。単数 $\pm1,\pm i$ の 4 倍で $4\prod(e+1)$。$2$ と偶数乗の $q$ は分け方が 1 通りなので掛からない。

5分解の工夫

小さい素数(47 以下)は割り算で先に取る。残りが素数なら終わり、平方数なら平方根を分解($p^2$ を rho にかけると、100 個で 1.37 s かかったものが 0.04 s になった。実測)、それ以外は rho。

迷った他案と採らなかった理由

案計算量採否理由
$a=0..\sqrt n$ のループ$O(\sqrt n)$検算用のみ$\sqrt{10^{14}}=10^7$ 回で 2.07 s(実測)→ $10^{18}$ は 1 問 $10^9$ 回、約 200 s(導出)
試し割りで分解$O(\sqrt n)$不採用$10^9$ 前後の 2 素数の積で $10^9/\ln$ 回程度、同じく間に合わない(導出)
$x^2\equiv-1$ を Tonelli–Shanks で解く$O(\log^2p)$不採用$p\equiv1\pmod4$ なら $c^{(p-1)/4}$ の 1 回の累乗で足りる
Hermite–Serret(連分数で $p=a^2+b^2$)$O(\log p)$不採用Cornacchia と中身は同じ互除法。止める条件が「$\sqrt p$ 未満」と単純な Cornacchia を採用
rho+Cornacchia+ガウス整数の積(本解)期待 $O(n^{1/4})$採用最悪寄りの入力 100 問で 0.96 s

前提が変わったら:

  • すべての組を列挙するなら、各 $p^e$ について $\pi^j\bar\pi^{e-j}$ を全通り掛けて単数倍する。個数は $r_2(n)$ 個で、$n\le10^{18}$ では 4k+1 型の小さい素数 11 個の積($5\cdot13\cdots97\approx9.9\times10^{16}$)だけでも $4\cdot2^{11}=8192$、指数を振れば数万に届く(概算)
  • $a^2+db^2=n$($d\ne1$)なら Cornacchia は一般の形で使えるが、合成数 $n$ への組み立ては $\mathbb Z[\sqrt{-d}]$ が一意分解でないと単純な積では済まない
  • $T=10^5$ なら rho の定数が重く Python では厳しい。$n\le10^{7}$ 程度なら最小素因数の篩で分解する

よくあるミス

ミス原因正しい書き方
$r_2(n)=4\,d(n)$(全約数の個数)とするJacobi の公式の $d_1-d_3$ を忘れる$n\le2\times10^5$ の全数中 181308 個で誤答(実測)
4k+3 型の奇数乗を見落とす$p\equiv1$ の因子だけ数える表せない $n$($2\times10^5$ 以下で 153550 個)すべてで誤答(実測)
互除法を余り 0 まで回す「最大公約数」の癖$\sqrt p$ 未満になった最初の余りで止める。0 まで回すと $\gcd(p,x)=1$ しか残らない
$(a+bi)^e$ の代わりに $a^e,b^e$ を掛ける積の公式の取り違えガウス整数として累乗する($5^2=25$ は $(2+i)^2=3+4i$ で $3^2+4^2$)
$p^2$ をそのまま rho にかける平方数の特別扱いを忘れる正しいが遅い。100 個で 1.37 s → 平方根を先に取って 0.04 s(実測)

次のステップ

発展1: すべての組 $(a,b)$ を列挙する($r_2(n)$ 個)

  • 発展2: 三平方和(Legendre の三平方定理: $4^a(8b+7)$ 型以外は表せる)と、その構成
  • 発展3: 原典(Cornacchia 1908、Jacobi の二平方の公式)を確認する(本セッションでは未確認)

検証ログ(本セッションで実行した数値)

全数照合(実測): $1\le n\le2\times10^5$ のすべてで、$r_2(n)$ が全組の数え上げと一致し、出力した組が $a^2+b^2=n$・$0\le a\le b$ を満たす(表せない $n$ は 153550 個)

  • ランダム照合(実測): $n\le10^{10}$ の 200 個(半数は $a^2+b^2$ の形で作成、表せるもの 113)で、$a$ のループによる個数と一致
  • 大きい $n$(実測): 素因数を指定して作った $n\le10^{18}$ の 300 個で、個数が既知の分解からの値と一致し、組の検算もすべて合格(1 問の最大 0.019 s)。下の計測用 500 個の出力の組もすべて検算合格
  • 誤った方法(実測): $4d(n)$ は 181308 個、4k+3 の奇数乗の見落としは 153550 個で誤答
  • 入力例(実測): 例 1・例 2 とも上記のとおり(組は検算)
  • 実行時間(実測、$T=100$、負荷平均 3〜8): $10^9$ 前後の 4k+1 型 2 素数の積 0.91〜0.96 s、4k+1 型と 4k+3 型の積 0.81〜0.85 s、4k+3 型素数の 2 乗 0.04 s(平方根を先に取らない版は 1.37 s)、$10^{18}$ 前後の 4k+1 型素数 0.05 s、一様乱数 0.06〜0.07 s
  • 扱っていない範囲: 全組の列挙、$a^2+db^2$、$T=10^5$ 規模、原典の確認

自己評価

自分の回答

気づき・メモ