有趣的勾股数及其计算

前言

勾股数又称毕达哥拉斯三元组,即可以构成直角三角形边长的三个整数,例如:(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)
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值