Tue, 26th November 2019Edit on Githubalgorithm平方数
2007年6月northwolves在CSDN提问 :
5个不同的自然数, 要求它们中的任意二个之和都是平方数
这是最大数最小的一组解: 7442, 28658, 148583, 177458, 763442
问题:
试列出10000000内的所有满足条件的解
6个自然数有没有解?
gxqcn给出一个预筛选思路。
1、要么全部为偶数,则可同除以最大公约数,必有奇数出现(进入下条款);
2、有奇数,则最多允许出现一个;
2.1 若还有偶数。则奇数个数必为1,且偶数间关于 8 必同余;
2.2 若全为奇数。则奇数个数必为2,且其和被 4 整除。
mathe建议首先对于3个数和4个数的情况给出通解:
可以如下构造通解:
任意给对整数边长的锐角三角形(假设三条边长度为a,b,c)
记, 如果S为整数,那么构成3个数的通解。
对于整数N,如果N的所有奇素因子模4都是1,
而且
N的奇素因子的数目不小于3
或者N的奇素因子数目为2,而且其中一个素因子的次数不小于2
或者N只有1个奇素因子,而且素因子的次数不小于5
那么我们必然可以找到6个整数
使得
(这种分解可能不唯一,这时候,每个分解都可以对应一个4个数情况下的解)
这时,我们记
那么(X,Y,Z,W)是4个数的一组解。
(其中要求S是整数,如果S不是整数,我们将a,b,c,d,e,f全部乘上2就可以得到对于4N情况的一组解的)
mathe说上面方法可以求出4个数情况的所有解。
有了4个数的结论,我们就可以有比较好的方法穷举5个数的情况了。
首先,我们对于在一定范围内的N,找出所有符合条件的N以及对于N的分解。(后面再介绍如何分解N)
我们可以建立一个关系表
(可能还应该将数据\gt ; \lt a,b,d;N\gt$也添加到这个表格中)
然后我们在关系表中找出所有的前三个数字相同,但是最后一项不同的数据对,比如
最后计算
如果这个数是完全平方数,那么我们已经找到一组5个数的情况。
现在我们讨论对于一个给定的整数N,如何将它分解为两个完全平方数的和。
假设
其中,都是模4余1的奇素数。
事先计算出将分解成两个平方数的和,可以证明,这种分解方案是存在而且唯一的。
比如
我们可以将这个表达式写成
(其中)
而
然后对于N的因子分解中,我们将其中每个素因子分别用奇分解中的一项替换
比如
用或来替换
展开后可以得到一个数
那么
根据替换方法的不同,我们可以得到N的不同分解方案。
可以看出,N的素因子越多,不同的分解方案越多。
mathe给出了下面的代码将一个素数p写成两个正整数平方和
#include <stdio.h>
#define abs(x) ((x)>0?(x):-(x))
#define HALFLIMIT 1000
#define LIMIT (HALFLIMIT*HALFLIMIT)
#ifdef WIN32
typedef __int64 longlong;
#else
typedef long long longlong;
#endif
int not_prime[LIMIT];
class cn{
longlong r,i;
public:
cn(longlong r, longlong i){this->r=r;this->i=i;}
cn com()const{cn x(r,-i);return x;}
longlong sqrmod()const{return r*r+i*i;}
longlong rel()const{return r;}
longlong img()const{return i;}
cn operator+(const cn& x)const{cn y(r+x.r,i+x.i);return y;}
cn operator-(const cn& x)const{cn y(r-x.r,i-x.i);return y;}
cn operator*(const cn& x)const{cn y(r*x.r-i*x.i,r*x.i+i*x.r);return y;}
cn div(longlong x)const;
bool iszero()const{return r==0&&i==0;}
};
cn cn::div(longlong x)const{
longlong u,v;
u=r%x;
v=i%x;
if(u<0)u+=x;
if(v<0)v+=x;
if(u>x/2)u-=x;
if(v>x/2)v-=x;
u=(r-u)/x;
v=(i-v)/x;
return cn(u,v);
}
cn round(const cn& x, const cn& y){
cn z=x*y.com();
longlong r=y.sqrmod();
return z.div(r);
}
cn factor(const cn& x, const cn& y){
if(x.iszero())return y;
return factor(y-round(y,x)*x,x);
}
int powmod(longlong a, longlong n, int p){
longlong mul=a%p;
longlong base=1;
while(n){
if(n&1){
base*=mul;
base%=p;
}
mul*=mul;
mul%=p;
n>>=1;
}
return (int)base;
}
int rm1(int p){
int i;
int t=(p-1)/4;
for(i=2;i<p;i++){
int u=powmod(i,t,p);
if(((longlong)u*u+1)%p==0)return u;
}
}
void defactor(int p){
int v=rm1(p);
cn x(v,1);
cn y(p,0);
cn f=factor(x,y);
printf("%d=%d^2+%d^2\n",p,abs((int)f.rel()),abs((int)f.img()));
}
int main(){
int i,j;
not_prime[0]=not_prime[1]=1;
for(i=2;i<HALFLIMIT;i++){
if(!not_prime[i]){
for(j=i*i;j<LIMIT;j+=i)
not_prime[j]=1;
}
}
for(i=2;i<LIMIT;i++){
if(!not_prime[i]&&i%4==1){
defactor(i);
}
}
}
northwolves自己利用平方数末尾二位的特点:
00 01 04 09 16 21 24 25 29 36 41 44 49 56 61 64 69 76 81 84 89 96
做了一个很笨的循环,花了25分钟在 1000000内找到了http://www.iwr.uni-heidelberg.de/groups/ngg/People/winckler/PU/s051.html 提供的
两个解:
7442 28658 148583 177458 763442
32018 104882 188882 559343 956018
mathe最后实现了他的上面介绍方案的代码 ,这个程序可以找到1483组5个数的解。