快速尋找勾股數算法的實現和優化


快速尋找勾股數算法的實現和優化

深夜隔壁寢室的老哥來訪,說他用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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM