Project Eulerの問題の多くは、base10 と base2 の両方で、整数とその数字を操作する必要があります。数字のリストで整数を変換したり、base10 を base2 (またはその数字のリスト) に変換したりすることに問題はありませんが、このような変換を繰り返し行うとパフォーマンスが低下することがよくあります。
次に例を示します。
まず、私の典型的な変換は次のとおりです。
#lang racket
(define (10->bin num)
(define (10->bin-help num count)
(define sq
(expt 2 count))
(cond
[(zero? count) (list num)]
[else (cons (quotient num sq) (10->bin-help (remainder num sq) (sub1 count)))]
)
)
(member 1 (10->bin-help num 19)))
(define (integer->lon int)
(cond
[(zero? int) empty]
[else (append (integer->lon (quotient int 10)) (list (remainder int 10)))]
)
)
次に、数字のリストが回文かどうかをテストする関数が必要です
(define (is-palindrome? lon)
(equal? lon (reverse lon)))
最後に、base2 と base10 のパリンドロームである max 以下のすべての base10 整数を合計する必要があります。アキュムレータ スタイルの関数は次のとおりです。
(define (sum-them max)
(define (sum-acc count acc)
(define base10
(integer->lon count))
(define base2
(10->bin count))
(cond
[(= count max) acc]
[(and
(is-palindrome? base10)
(is-palindrome? base2))
(sum-acc (add1 count) (+ acc count))]
[else (sum-acc (add1 count) acc)]))
(sum-acc 1 0))
そして、通常の再帰バージョン:
(define (sum-them* max)
(define base10
(integer->lon max))
(define base2
(10->bin max))
(cond
[(zero? max) 0]
[(and
(is-palindrome? base10)
(is-palindrome? base2))
(+ (sum-them* (sub1 max)) max)]
[else (sum-them* (sub1 max))]
)
)
これら 2 つの最後の関数のいずれかを 1000000 に適用すると、完了するまでに 10 秒以上かかります。再帰バージョンは、アキュムレータ バージョンよりも少し高速に見えますが、違いはごくわずかです。
このコードを改善する方法はありますか、それとも、これが Racket が特に適していない数計算のスタイルであることを受け入れる必要がありますか?
これまでのところ、integer->lon を同様の integer->vector に置き換える可能性を検討してきました。vector-append は append よりも高速であると予想されますが、後で逆を適用する必要があることに行き詰まっています。