尤拉計劃748:倒丟蕃多方程
對於丟蕃多方程的倒寫
1/x2 +1/y2=13/z2
設(x,y,z)是一組正整數解,其中gcd(x,y,z)=1,為原始解,
設S(n)是滿足1<=x,y,z<=n,x<=y的原始解之和。
對於n=100,有2組原始解,各是(2,3,6)和(5,90,18),所以s(100)=124,
已知S(1000)=1470,S(100000)=2340084
求S(1016)的最後9位數
測試程式碼,在pypy3上算到104需要3秒,算到105需要600秒。
import math
import time
def f(n,m):
s=0
for x in range(2,n//3):
for y in range(x+1,n+1,2):
gxy=math.gcd(x,y)
if (x*x+y*y)%m==0:
zz=m*(x*x*y*y)/(x*x+y*y)
if zz % 1==0:
z=int(pow(zz,0.5))
if z<n and z*z==zz and math.gcd(gxy,z)==1:s+=x+y+z;print(x,y,z);break
return s
t=time.time();print(f(10000,13));print(time.time()-t)
2 3 6
...
2997 3922 8586
70677
3.3921940326690674
把對y的迴圈改為對w=x*y/z的迴圈,快了一倍。 觀察w的平方根,都是質數或者質數的積(25)。
def f2(n,m):
s=0
for x in range(2,n//3):
w0=int(x*pow(2/m,0.5))+1
if w0%2==0:w0+=1
for w in range(w0,int(pow((x*x+n*n)/m,0.5)+1),2):
y0=pow(m*w*w-x*x,0.5)
if y0%1 ==0:
y=int(y0)
gxy=math.gcd(x,y)
z1=y*x/w
if z1 % 1==0:
z=int(z1)
if z<n and math.gcd(gxy,z)==1:s+=x+y+z;print(x,y,z,int(w**0.5));break
return s
t=time.time();print(f2(10000,13));print(time.time()-t)
2 3 6 1
5 90 18 5
30 85 102 5
102 1037 366 17
117 598 414 13
493 918 1566 17
522 2987 1854 29
667 2958 2346 29
675 2150 2322 25
1258 4773 4386 37
1450 1725 4002 25
2173 5658 7314 41
2997 3922 8586 37
70677
1.4536311626434326
如果w不迴圈,而是隻羅列質數和質數之積,有了數量級的提高。算到105縮短到1秒多。
def prime(n):
sieve = [True] * (n//2+1)
for number in range(3, int(n ** 0.5) + 1,2):
if sieve[(number-1)//2]:
for index in range(number * number, n, number*2):
sieve[(index-1)//2] = False
return [x for x in range(1,n,2) if sieve[(x-1)//2]]
def f3(n,m):
s=0
maxp=int((n/(m**0.5))**0.5)
p=prime(maxp)
pp=[i*j for i in p for j in p if i<=j and i*j<=maxp]
for x in range(2,n//3):
w0=int(x*pow(2/m,0.5))+1
if w0%2==0:w0+=1
for w in [j*j for j in pp]:
if not(w>=w0 and w<int(pow((x*x+n*n)/m,0.5)+1)):continue
y0=pow(m*w*w-x*x,0.5)
if y0%1 ==0:
y=int(y0)
gxy=math.gcd(x,y)
z1=y*x/w
if z1 % 1==0:
z=int(z1)
if z<n and math.gcd(gxy,z)==1:s+=x+y+z;print(x,y,z,w**0.5);break
return s
t=time.time();print(f3(10000,13));print(time.time()-t)
70677
0.09000492095947266
t=time.time();print(f3(100000,13));print(time.time()-t)
2052798
1.3252480030059814
這與題目公佈的正確值有差距,經過檢查,遺漏了w=125的結果。在函式f4中加上,將來隨著n的增加,還有可以加上更多的質數之積。 考慮到x,y都是w的倍數,所以x的迴圈可以放到w的迴圈內部,又有數量級的提高,可以在1秒鐘內計算106,而上一種演算法需要42秒(質數表不全)。
def f4(n,m):
s=0
maxp=int((n/(m**0.5))**0.5)
p=prime(maxp)
pp=[i*j for i in p for j in p if i<=j and i*j<=maxp]
ppp=[i*j*k for i in p for j in p for k in p if i>1 and i<=j and j<=k and i*j*k<=maxp]
pppp=[i*j*i*j for i in p for j in p if i>1 and i<=j and i*j*i*j<=maxp]
pp+=ppp+pppp
for w in [j*j for j in pp]:
for k in range(1,n//int(w**0.5)+1):
x=k*int(w**0.5)
w0=int(x*pow(2/m,0.5))+1
if not(w>=w0 and w<int(pow((x*x+n*n)/m,0.5)+1)):break
y0=pow(m*w*w-x*x,0.5)
if y0%1 ==0:
y=int(y0)
gxy=math.gcd(x,y)
z1=y*x/w
if z1 % 1==0:
z=int(z1)
if z<n and math.gcd(gxy,z)==1:s+=x+y+z;print(x,y,z,int(w**0.5))
return s
t=time.time();print(f4(100000,13));print(time.time()-t)
2340084
0.1716001033782959
t=time.time();print(f4(10**6,13));print(time.time()-t)
85126107
0.48520898818969727
t=time.time();print(f3(10**6,13));print(time.time()-t)
77588781
42.492141008377075
觀察n很大的數,以及把m=13換成其他的平方和,似乎w中不存在3 7 11,而存在1 5 13 17,這些數共同的特徵是可以表示成4*n+1的形式。
把程式碼中p=prime(maxp)
改為p=[i for i in prime(maxp) if i%4==1]
,結果完全不變,效能又有提高。
import random as rd
import time
import math
def ran(n):
if n==1:return 1
if (n%4!=1):return 0
while True:
a=rd.randint(1,10**6)%(n-1)+1 #1~ n-1
b=pow(a,(n-1)//4,n);
if(b*b%n==n-1):
if b<=(n-1)/2 :return b
else: return n-b
def solve(p):
global c1
c1+=1
if(p==2): return [1,1];
if(p%4!=1): return mp[-1,-1];
A=ran(p); B=1
tmp=1;
M=(tmp*A*A+tmp*B*B)//p
while M>1:
u=(A%M+M)%M;v=(B%M+M)%M
if(2*u>M): u-=M
if(2*v>M): v-=M
ta=A
A=(tmp*u*A+tmp*v*B)//M
B=(tmp*v*ta-tmp*u*B)//M
M=(tmp*u*u+tmp*v*v)//M
return [min(abs(A),abs(B)),max(abs(A),abs(B))]
def e(l,s2):
l1=[]
for s1 in l:
u,v=s1[0],s1[1]
a,b=s2[0],s2[1]
l1.append([abs(u*a+v*b),abs(v*a-u*b)])
l1.append([abs(u*a-v*b),abs(v*a+u*b)])
l2=[]
for x in l1:
if x not in l2 and 0 not in x:
l2.append(x)
return l2
def test2(sol,pl,m):
sl=[sol[(p-1)//4] for p in pl]
sl+=sl
sl+=sl
l1=[solve(m)]
for i in range(0,len(sl)):
l1=e(l1,sl[i])
l2=[]
for x in l1:
if x not in l2 and 0 not in x:
l2.append(x)
return l2
def f7(n,m):
s=0
maxp=int((n/(m**0.5))**0.5)
p=[i for i in prime(maxp) if i%4==1]
#sol=[solve(px) for px in p]
sol=[1 for i in range(maxp//4+1)]
for px in p:sol[(px-1)//4]=solve(px)
pp=[(i*j,[i,j]) for i in p for j in p if i<=j and i*j<=maxp]
ppp=[(i*j*k,[i,j,k]) for i in p for j in p for k in p if i>1 and i<=j and j<=k and i*j*k<=maxp]
pppp=[(i*j*i*j,[i,j,i,j]) for i in p for j in p if i>1 and i<=j and i*j*i*j<=maxp]
pp+=ppp+pppp
for w,lp in [(j[0]*j[0],j[1]) for j in pp]:
r=test2(sol,lp,m)
for i in r:
x=i[0]
y=i[1]
gxy=math.gcd(x,y)
z1=y*x/w
if z1 % 1==0:
z=int(z1)
if z<n and math.gcd(gxy,z)==1:s+=x+y+z;#print(x,y,z,int(w**0.5))
return s
c1=0
t=time.time();print(f7(10**7,13));print(time.time()-t,c1)
上述是採用費馬降階法求整數化為平方和的程式碼
相關文章
- 尤拉方程
- 尤拉計劃700:尤拉幣
- 尤拉計劃622:洗牌
- 尤拉計劃698:123數
- 尤拉計劃696:麻將
- 尤拉計劃719:拆分數
- 尤拉計劃699:三腳數
- 尤拉計劃706:三象數
- 尤拉計劃718:不可達數
- 尤拉計劃686:2的冪
- 尤拉計劃715:六元組
- 尤拉計劃709:偶數袋
- 尤拉計劃708:你只要2
- 尤拉計劃712:指數差
- 尤拉計劃749:近似冪和
- 尤拉計劃739:和的和
- 尤拉計劃751:串聯重合
- 尤拉計劃745:平方和
- 尤拉計劃717:取模公式之和公式
- 尤拉計劃722:慢收斂系列
- 尤拉計劃695:隨機長方形隨機
- 尤拉計劃694:立方滿因數
- 尤拉計劃725:數位之和數
- 尤拉計劃710:1百萬會員
- 尤拉計劃723:畢達哥拉斯四邊形
- 尤拉計劃697:隨機衰減序列隨機
- 尤拉計劃713:圖蘭熱水系統
- 尤拉計劃711:二進位制黑板
- 尤拉計劃747:三角披薩
- 尤拉計劃657:不完整的單詞
- 尤拉計劃721:無理數高次冪
- 尤拉計劃701:隨機連線區域隨機
- 尤拉計劃714:兩種數位的數
- 尤拉計劃658:不完整的單詞(2)
- 尤拉計劃735:2n^2的因數
- 尤拉計劃705:除數序列的逆轉次數
- P1082 [NOIP2012 提高組] 同餘方程 尤拉定理
- 尤拉計劃621:把整數表示為三角數之和