LoginSignup
0
0

2つの平方数の和の組合せを求める(ヤコビの二平方定理)

Last updated at Posted at 2021-11-30

二平方の和の個数

今回はヤコビの二平方定理 (Wiki)という面白い定理を見つけたので、これを使うプログラムを書いてみました。以下のような問題を考えてみます。

問題: $N=10^{12}$、$a^2+b^2=c^f (a,b,cは自然数,0<a<b, f\geq 3,\space c^f<N)$となる(a,b,c)の数を数えよ

これを素直にインプリすると、以下のようになりますが、Python(Google Colab)では約45分かかります。

from time import time

def count2sq(f,N):
  ret = 0
  for c in itertools.count(2):
    c_f = c**f
    if c_f > N: break
    for b in itertools.count(2):
      b2 = b**2
      if (1+b2) > c_f: break
      a2 = (c_f - b2)
      if a2 >= b2: continue
      a = int(a2**(1/2)+0.00001)
      if (a**2 == a2):
        ret += 1
  return ret

N, count = 10**12, 0
start = time()
for f in itertools.count(3):
  if 2**f > N: break
  count += count2sq(f,N)
print(f"**** count = {count},  {time() - start:.2f}sec ****")
# **** count = 15992,  2697.05sec ****

ヤコビの二平方定理

日本語版のヤコビの二平方定理 (Wiki)の説明だと分かりづらいので英語版のSum of squares functionの$k=2$の説明を引用します。

\begin{align}
&a^2+b^2=nを満たす整数のペア(a,b)の個数をr_2(n)を求める \\ \\
&nの素因数分解が以下のように表されるとすると、\\
&\space n=2^gp^{f_1}_1p^{f_2}_2\cdots q^{h_1}_1q^{h_2}_2\cdots\\
&\ \ \ \ ここで \\
&\ \ \ \ p_i\equiv 1\space \pmod 4\\
&\ \ \ \ q_i\equiv 3\space \pmod 4 \\
&r_2(n)は以下の式で求められます\\
\end{align}
\begin{eqnarray}
r_2(n)=\left\{ \begin{array}{ll}
4(f_1+1)(f_2+1)\cdots &(h_1,h_2,\cdotsがすべて偶数の時)\\
0 & (h_iの一つでも奇数の時)
\end{array} \right.
\end{eqnarray}

要は$n$の素因数を求めてmod 4が1になるものの数を数えればいいということになります。元の問題は$n=c^f$なので、$c$の素因数分解をして各々$f$倍すれば良いということでヤコビの二平方定理が効率よく利用できます。
ただしこの$r_2(n)$は$a,b$の各々の$\pm$と$a,b$の入れ替えを含んでいるので問題の数を求めるには$4\times 2=8$で割る必要があります。

Pythonでインプリメントすると以下のようになり、無事、同じ答えが0.10secで求まりました。

from sympy import factorint
import itertools
from time import time

def sumE2f(N, f):
  sum = 0
  for c in itertools.count(2):
    c_f = c**f 
    if c_f > N: break
    cfct = factorint(c)
    rn, p3 = 4, 0
    for p in cfct.keys():
      cfp = cfct[p]*f
      if p%4==3 and cfp % 2 == 1: rn = 0
      if p%4==1: rn *= (cfp+1)
    if rn > 4:
      sum += rn // 8
  return sum

start = time()
np, sum2 = 12, 0
N = 10**np
for f in itertools.count(3):
  if 2**f > N: break
  sm = sumE2f(N,f)
  if sm > 0:
  sum2 += sm 

print(f"**** count = {sum2},  {time() - start:.2f}sec ****")
#**** count = 15992,  0.10sec ****

このアルゴリズムはEuler Project: Problem 678等を解くのに役に立ちます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0