快速寻找勾股数算法的实现和优化

快速寻找勾股数算法的实现和优化

深夜隔壁寝室的老哥来访,说他用python实现的寻找2000以内勾股数的算法跑了20秒钟。邀请我一起讨论优化思路,完成后记录如下:

朴素探数法寻找勾股数

首先实现那个需要20秒钟的朴素算法,思路非常简单,三重for循环遍历,利用了勾股数的以下性质:

a2 + b2 == c2

python代码实现:

def gcd(m,n):
    return m if n == 0 else gcd(n,m%n)
def nfgg(max):
    results = []
    for a in range(1,max+1):
        for b in range(a,max+1):
            for c in range(b,max+1):
                if a*a + b*b == c*c and gcd(a,b) == 1:
                    results.append([a,b,c])
    for l in results:
        print(l)
    print("total : " + str(len(results)))
    return results
    

我已经对这种朴素算法进行了简单优化,以下是注意事项:
1*,gcd()方法用于递归求公因数,此处用于除去派生勾股数,如6,8,10
2,b,c两变量无需从1开始遍历
3*,max是遍历边界,注意range()方法左闭右开的性质参数需填写(1,max+1)

算法优化
尝试了以上算法,发现确实慢的离谱,我通过查阅资料发现一种数学上的优化思路:

1. 定义:凡符合a2+b2=c2的正整数a,b,c我们称之为一组勾股数。a和b是直角边,c是斜边。
2. 凡有公约数的勾股数我们称之为派生勾股数,例[30,40,50] 等;
3. 无公约数的勾股数,例[3,4,5];[8,15,17]等,我们称之为勾股数。
有:全是偶数的勾股数必是派生勾股数,三个奇数不可能符合定义公式。两偶一奇和两奇一偶都可以被证明不符合公式条件,因此,勾股数唯一的可能性是:
           a和b分别是奇数和偶数(偶数和奇数),斜边c只能是奇数。
4. 勾股数具有以下特性:
斜边与偶数边之差是奇数,这个奇数只能是某奇数的平方数, 例1,9,25,49,……
斜边与奇数边之差是偶数,这个偶数只能是某偶数平方数的一半, 2,8,18,32,……
5. 由以上定义我们推导出勾股公式:
           a = p2 + q2
           b = q2/ 2 + pq
           c =p2+ q2/ 2 + pq
6,此公式涵盖了自然界的全部勾股数,包括派生勾股数。
以任意奇数代入P ,任意偶数代入Q ,即可得到唯一一组勾股数。
例如P = 5 ,Q = 8 ,得到
X = 25 + 5×8 = 65
Y = 32 + 5×8 = 72
Z = 25 + 32 + 5×8 = 97
当P与Q有公约数时,例如9与12 ,再例如21与28等,推导出来的是派生勾股数;
当P与Q无公约数时,例如9与8 ,再例如21与16等,推导出来的是勾股数;

(引用来自百度,有修改)
根据以上数学方法,可以得到一种代码实现思路:
双重for循环遍历max内的奇数a和偶数b,
如果gcd(a,b) == 1:
如果t = aa + bb <= max :
寻找到一组勾股数(a,b,c=pow(t,0.5))
优化:当 aa + bb > max时,跳出第二重循环
最后对结果进行简单处理
python实现如下:

def gcd(m,n):
    return m if n == 0 else gcd(n,m%n)
def fgg(max):
    # find numbers
    results = []
    for i in range(1,max+1,2):
        for j in range(2,max+1,2):
            if gcd(i,j) == 1 :
                t = pow(i*i+j*j,0.5)
                if t > max:
                    break
                elif int(t) == t:
                    t = int(t)
                    results.append([i,j,t])
    # handle the resulta
    for l in results:
        l.sort()
    results.sort()
    for l in results:
        print(l)
    print("total : " + str(len(results)))
    return results

效率提升非常明显,如果没有特殊需要,代码中的列表排序完全可以去除。

https://tieba.baidu.com/p/6435191370  http://blog.sina.com.cn/s/blog_184e9f38b0102yyi5.html  https://www.douban.com/group/topic/162687100/

posted @ 2020-01-09 12:36  胡马  阅读(1605)  评论(0编辑  收藏  举报