GitHub上的這篇文章寫了求解親和數的問題,解釋的還是蠻詳細的,具體鏈接 https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/06.0.md
作者介紹了算法,也給出瞭解釋,其思想與埃拉托色尼選篩法是一致的,埃拉托色尼選篩法是尋找一定範圍內的質數,用一個數組裏的元素表示判斷結果,用下標來表示所檢測的數字,這裏求親和數,作者也是採樣了這樣的思路,第一遍遍歷整個數組把所有數的因子和求出來保存到對應的下標中,第二遍就判斷是否是親和數,這個解題的思想很巧妙,充分利用了數組的下標。這裏我不討論作者的程序,作者用C和JAVA實現了算法,下午我用scheme語言實現了這個算法,這裏就拿出來看看:
#!/usr/bin/guile -s
!#
;vector initialized with 1
(define SIZE 5000000)
(define sum (make-vector SIZE 1))
(define (all-sum i)
(define (estra j)
(if (< j SIZE)
(begin (vector-set! sum j (+ (vector-ref sum j) i))
(estra (+ j i)))))
(if (< (+ i i) SIZE)
(begin
(estra (+ i i))
(all-sum (+ i 1)))
))
;
(all-sum 2)
(define (amiable i)
(if (< i SIZE)
(begin
(if (and (> (vector-ref sum i) i) (< (vector-ref sum i) SIZE) (= (vector-ref sum (vector-ref sum i)) i)) (begin (display i) (display " ") (display (vector-ref sum i)) (newline)))
(amiable (+ i 1)))))
(amiable 220)
這裏解釋一下程序,(all-sum i) 求解所有數的因子和,結果保存在全局變量sum中,all-sum本身是採用遞歸實現的,因爲Scheme中很少使用循環,循環一般都使用遞歸的寫法,由於Scheme對於爲尾遞歸做了優化,所以性能上不會差。在(all-sum)中又定義了一個潛逃的函數,這個函數本身也是遞歸實現的,這兩個函數實現的效果就相當於C語言中嵌套循環效果是一樣的,由於兩個都是尾遞歸,所以都會被優化,內層的函數使用了外層函數的變量i,這在Scheme中叫做閉包性質,也就是內層的自由變量綁定外層函數的限定變量。
最後一個函數amiable就直接判斷是否是親和數,也是一個遞歸實現,可見使用Scheme編程程序中會大量出現遞歸的使用,並且Scheme也提倡使用尾遞歸,因爲編譯器已經做了特殊的優化,會展開爲迭代。
第一次用Scheme實現了真正的功能,感覺還是隻掌握了皮毛,對Scheme理解還是很淺,希望以後隨着學習的深入能掌握的更好。