親和數問題--求解500萬以內的親和數之—Scheme語言實現

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理解還是很淺,希望以後隨着學習的深入能掌握的更好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章