前言
勾股数又称毕达哥拉斯三元组,即可以构成直角三角形边长的三个整数,例如:(3, 4, 5)、(5, 12, 13)等等。自然,勾股数同乘以相同的倍数依然是勾股数,像:(6, 8, 10)、(30, 40, 50)等等。但这种可以约去公约数的派生的勾股数,没有太大意义,一般不认为是新的勾股数。另外,像(3, 4, 5)和(4, 3, 5),其实是重复的,当然不能认为是不同的勾股数。这里主要研究本源的勾股数及其产生的计算方法,并用C++实现。
暴力计算 —— 穷举法
此算法很简单,即从1开始,三角形三边a、b、c顺序增加1,直到指定的上限n,如果三者符合勾股定理: c 2 = a 2 + b 2 c^2 = a^2 + b^2 c2=a2+b2,则这三个数是勾股数。用C++程序描述如下:
for (int a=1; a<=n; a++) {
for (int b=1; b<=n; b++) {
for (int c=1; c<=n; c++) {
if (c*c == a*a + b*b) {
printf("(%d, %d, %d)\n", a,b,c);
}
}
}
}
此算法可以说没有什么技术含量,时间复杂度为 O ( n 3 ) O(n^3) O(n3),效率低下,而且会产生很多派生及重复的勾股数。稍微改进一下,规定 a < b < c a<b<c a<b<c,避免产生重复的勾股数。C++程序描述如下:
for (int a=1; a<=n; a++) {
for (int b=a+1; b<=n; b++) {
for (int c=b+1; c<=n; c++) {
if (c*c == a*a + b*b) {
printf("(%d, %d, %d)\n", a,b,c);
}
}
}
}
这样虽然不产生像(3, 4, 5)和(4, 3, 5)这样的重复勾股数,但还是会产生像(3, 4, 5)、(6, 8, 10)、(30, 40, 50)这样派生的勾股数。最重要的是算法时间复杂度还是为
O
(
n
3
)
O(n^3)
O(n3),没有效率。这种简单粗暴的算法当然不可取。
我们要寻找计算勾股数快速有效的方法。
一个快速但不完美的算法
容易证明,以下三元组
(
a
,
b
,
c
)
(a, b, c)
(a,b,c)是勾股数:
{
a
=
2
i
+
1
b
=
2
i
(
i
+
1
)
c
=
2
i
(
i
+
1
)
+
1
(
i
是
自
然
数
)
\left\{ \begin{array} {l} a=2i+1 \\ b=2i(i+1) \\ c=2i(i+1)+1 \\ \end{array} \right. (i是自然数)
⎩⎨⎧a=2i+1b=2i(i+1)c=2i(i+1)+1(i是自然数)
完整的C++程序如下:
#include <iostream>
#include<vector>
using namespace std;
struct PythagorasSet {
long a,b,c;
};
class PythagorasNumbers {
friend ostream& operator<<(ostream& output, PythagorasNumbers& pns);
public:
PythagorasNumbers():num(0){}
PythagorasNumbers(long n):num(n){genPythagorasNum();}
void setValue(long n);
void printPythagorasNum();
private:
long num;
vector <PythagorasSet> ps;
void genPythagorasNum();
};
void PythagorasNumbers::setValue(long n)
{
num=n;
genPythagorasNum();
return;
}
void PythagorasNumbers::genPythagorasNum()
{
PythagorasSet pt;
vector<PythagorasSet>().swap(ps);
for (long i=1; i<=num; i++) {
pt.a=2*i+1;
pt.b=2*i*(i+1);
pt.c=2*i*(i+1)+1;
ps.push_back(pt);
}
return;
}
void PythagorasNumbers::printPythagorasNum()
{
for (size_t i=0;i!=ps.size();i++) {
cout<<"["<<i+1<<"]"<<":("<<ps[i].a<<","<<ps[i].b<<","<<ps[i].c<<")"<<endl;
}
return;
}
ostream& operator<<(ostream& output, PythagorasNumbers& pns)
{
for (size_t i=0;i!=pns.ps.size();i++) {
output<<"["<<i+1<<"]"<<":("<<pns.ps[i].a<<","<<pns.ps[i].b<<","<<pns.ps[i].c<<")"<<endl;
}
return output;
}
void printPythagrasNumbers(long n)
{
for (long a=1; a<=n; a++) {
for (long b=a+1; b<=n; b++) {
for (long c=b+1; c<=n; c++) {
if (c*c == a*a +b*b) {
cout << "(" << a << ", " << b <<", " << c << ")" <<endl;
}
}
}
}
}
int main()
{
long n=100;
PythagorasNumbers p(n);
cout<<"输出前"<<n<<"组勾股数"<<endl<<p;
return 0;
}
这个算法效率高很多,为 O ( n ) O(n) O(n)。但它有一个缺点,并不产生所有的勾股数,即有所遗漏。以下是程序输出的前100组勾股数。
输出前100组勾股数
[1]:(3,4,5)
[2]:(5,12,13)
[3]:(7,24,25)
[4]:(9,40,41)
[5]:(11,60,61)
[6]:(13,84,85)
[7]:(15,112,113)
[8]:(17,144,145)
[9]:(19,180,181)
[10]:(21,220,221)
[11]:(23,264,265)
[12]:(25,312,313)
[13]:(27,364,365)
[14]:(29,420,421)
[15]:(31,480,481)
[16]:(33,544,545)
[17]:(35,612,613)
[18]:(37,684,685)
[19]:(39,760,761)
[20]:(41,840,841)
[21]:(43,924,925)
[22]:(45,1012,1013)
[23]:(47,1104,1105)
[24]:(49,1200,1201)
[25]:(51,1300,1301)
[26]:(53,1404,1405)
[27]:(55,1512,1513)
[28]:(57,1624,1625)
[29]:(59,1740,1741)
[30]:(61,1860,1861)
[31]:(63,1984,1985)
[32]:(65,2112,2113)
[33]:(67,2244,2245)
[34]:(69,2380,2381)
[35]:(71,2520,2521)
[36]:(73,2664,2665)
[37]:(75,2812,2813)
[38]:(77,2964,2965)
[39]:(79,3120,3121)
[40]:(81,3280,3281)
[41]:(83,3444,3445)
[42]:(85,3612,3613)
[43]:(87,3784,3785)
[44]:(89,3960,3961)
[45]:(91,4140,4141)
[46]:(93,4324,4325)
[47]:(95,4512,4513)
[48]:(97,4704,4705)
[49]:(99,4900,4901)
[50]:(101,5100,5101)
[51]:(103,5304,5305)
[52]:(105,5512,5513)
[53]:(107,5724,5725)
[54]:(109,5940,5941)
[55]:(111,6160,6161)
[56]:(113,6384,6385)
[57]:(115,6612,6613)
[58]:(117,6844,6845)
[59]:(119,7080,7081)
[60]:(121,7320,7321)
[61]:(123,7564,7565)
[62]:(125,7812,7813)
[63]:(127,8064,8065)
[64]:(129,8320,8321)
[65]:(131,8580,8581)
[66]:(133,8844,8845)
[67]:(135,9112,9113)
[68]:(137,9384,9385)
[69]:(139,9660,9661)
[70]:(141,9940,9941)
[71]:(143,10224,10225)
[72]:(145,10512,10513)
[73]:(147,10804,10805)
[74]:(149,11100,11101)
[75]:(151,11400,11401)
[76]:(153,11704,11705)
[77]:(155,12012,12013)
[78]:(157,12324,12325)
[79]:(159,12640,12641)
[80]:(161,12960,12961)
[81]:(163,13284,13285)
[82]:(165,13612,13613)
[83]:(167,13944,13945)
[84]:(169,14280,14281)
[85]:(171,14620,14621)
[86]:(173,14964,14965)
[87]:(175,15312,15313)
[88]:(177,15664,15665)
[89]:(179,16020,16021)
[90]:(181,16380,16381)
[91]:(183,16744,16745)
[92]:(185,17112,17113)
[93]:(187,17484,17485)
[94]:(189,17860,17861)
[95]:(191,18240,18241)
[96]:(193,18624,18625)
[97]:(195,19012,19013)
[98]:(197,19404,19405)
[99]:(199,19800,19801)
[100]:(201,20200,20201)