uvaoj 106 Fermat vs. Pythagoras 毕达哥拉斯三元组
给定方程x^2+y^2=z^2,在1到1000000范围内找出三元组(x,y,z)的数目,其中三元组要求x<y<z,并且x,y和z两两互质,并且求出不在任何三元组内的数的个数(并不限于两两互质)。
这个方程与勾股定理有密切的关系。
1. 勾股定理和勾股数(毕达哥拉斯数)
勾股定理可以说是全部数学中最古老也最家喻户晓的一个定理,在西方它被称为毕达哥拉斯定理(Pythagorean Theorem or Pythagoras' theorem),因为古希腊的数学家毕达哥拉斯(Pythagoras,572 BC—497 BC)最早证明了这个定理。据说,毕达哥拉斯发现这个定理后欣喜若狂,命人宰杀了一百头牛祭祀庆贺,故它又被戏称为“百牛定理”。
勾股定理的确是一个又简单又很“牛”的定理,它断言任意直角三角形斜边的平方等于两条直角边的平方之和,记两直角边的长为 a 和 b, 斜边的长为 c, 则一定有:
a^2 + b^2 = c^2
满足上述方程的正整数(a, b, c)被称为“勾股数”,也称为“毕达哥拉斯数”(Pythagorean numbers)或“毕达哥拉斯三元数组”(Pythagorean triplet),由勾股定理可知,这样的三个整数恰好能构成直接三角形的三条边。
最为大家熟悉的勾股数是(3, 4, 5),在二千多年前的中国古代数学著作《周髀算经》就有“勾三股四弦五”的记载,说明我们的老祖宗老早就知道了这组勾股数。中国古代把直角三角形的两条直角边分别称为“勾”和“股”,斜边称为“弦”,“勾三股四弦五”等于道出了勾股定理的一个特例,“勾、股各自乘,并而开方除之(则得弦)”,这是《周髀算经》中对勾股定理的一般表述(但书中并没有给出证明,长于计算,疏于推理和逻辑演绎是中国古代数学的一个特征)【注1】。
2.题目的做法
我们注意到如果三元(x,y,z)满足方程,那么(kx,ky,kz)也满足方程,所以我们只需要求出来所有两两互质的三元组,就可以得到所有的解。
首先可以证明x和y必为一奇一偶。因为互质,所以不能同为偶数。用反证法可以证明不同时为奇数。设x=2a+1,y=2b+1,则有z^2=4(a^2+b^2+a+b)+2,因为x^2和y^2为奇数,所以z^2也为奇数,有它为平方数,所以它必须能被四整除,这与前边的式子矛盾,所以x和y不能同时为奇数。
假设x为奇数,y为偶数,则z为奇数,2z与2x的最大公因数为2,2z和2x可分别写作
2z = (z + x) + (z - x)
2x = (z + x) - (z - x)
那么跟据最大公因数性质,z + x和z - x的最大公因数也为2,又因为:
(z + x)(z - x) = y^2,两边同除以4得:
((z + x) / 2)((z - x) / 2) = (y / 2)^2
故可令:
z + x = 2m^2, z - x = 2n^2
其中z = m^2 + n^2, x = m^2 - n^2(m与n互质,并且m和n一奇数一偶数,同理可证明)
则有:
y2 = z^2 - x^2 = 2m^2*2n^2 = 4m^2n^2
即y = 2mn。
综上所述,可得到下式:
x = m^2 - n^2, y = 2mn, z = m^2 + n^2. (m, n为任意自然数)
这里还有一个问题:题目要求统计(x, y, z)三元组的数量时只统计x,y和z两两互质的的情况,这个问题用上面的算法就可以解决了。但对于统计p的数量,题目并不限定三元组是两两互质的。但是上式不能生成所有x, y, z并不是两两互质的情况。然而假设x与y最大公因数w不为1,则z也必能被w整除,因此w为x, y, z三个数的公因数。归纳总结可知,所有非两两互质的x0, y0, z0都可由一组互质的x, y, z乘以系数得到。根据以上理论就可以快速的求解了
代码如下:
/*************************************************************************
> File Name: 106.cpp
> Author: gwq
> Mail: gwq5210@qq.com
> Created Time: 2015年01月06日 星期二 19时20分08秒
************************************************************************/
#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>
#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())
using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;
const double esp = 1e-5;
#define N 1000010
int vis[N];
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
int main(int argc, char *argv[])
{
int n;
while (scanf("%d", &n) != EOF) {
int ans1 = 0;
int ans2 = 0;
clr(vis, 0);
for (int i = 2; i * i < n; ++i) {
for (int j = 1; j < i; ++j) {
int x = i * i - j * j;
int y = 2 * i * j;
int z = i * i + j * j;
// i和j必须互质和一奇一偶
if (i % 2 != j % 2 && x <= n && y <= n
&& z <= n && gcd(i, j) == 1) {
++ans1;
for (int k = 1; k * z <= n; ++k) {
vis[k * x] = 1;
vis[k * y] = 1;
vis[k * z] = 1;
}
}
}
}
for (int i = 1; i <= n; ++i) {
if (!vis[i]) {
++ans2;
}
}
printf("%d %d\n", ans1, ans2);
}
return 0;
}
注:
【注1】但中国古人也并不是完全缺乏证明的意识和才华,三国时吴国的赵爽在给《周髀算经》所作的注中就给出了勾股定理的一个漂亮的证明,他用的方法是面积割补法(即著名的赵爽弦图,2002年在北京召开的国际数学家大会上曾用赵爽弦图作为会徽)。
参考:
1)http://www.cnblogs.com/devymex/archive/2010/08/07/1799713.html
2)http://www.xieguofang.cn/Maths/Number_Theory/Fermats_Last_Theorem_1.htm