Problem 75 in Project Eulerを解決している間に予期しない結果が発生しました。私のコードは正しい解を見つけることができますが、それは不思議です。Lispのリストとリスト:リストはなぜ以下のコードではるかに高速ですか?
私の解決策は、周囲の限界に達するまでピタゴラスの木(Barning's matrices)をトラバースし、周囲がそれぞれの値を取った回数を数え、そして最後に1回だけ起こった周長を数えます。あるマイ確かにだらしないが、コードを機能:
(defparameter *barning-matrixes*
'(#(1 -2 2) #(2 -1 2) #(2 -2 3)
#(1 2 2) #(2 1 2) #(2 2 3)
#(-1 2 2) #(-2 1 2) #(-2 2 3)))
(defparameter *lengths* (make-array 1500001 :initial-element 0))
(defun expand-node (n)
"Takes a primitive Pythagorean triple in a vector and traverses subsequent nodes in the the tree of primitives until perimeter > 1,500,000"
(let ((perimeter (reduce #'+ n)))
(unless (> perimeter 1500000)
(let ((next-nodes (mapcar #'(lambda (x)
(reduce #'+ (map 'vector #'* n x))) *barning-matrixes*)))
(loop for i from perimeter to 1500000 by perimeter
do (incf (aref *lengths* i)))
(expand-node (subseq next-nodes 0 3))
(expand-node (subseq next-nodes 3 6))
(expand-node (subseq next-nodes 6 9))))))
(expand-node #(3 4 5)) ; Takes too darn long :-(
(count 1 *lengths*)
私は数ミリ秒で実行するツリーの拡大を期待しますが、拡張ノード機能は、8.65秒かかった - 予想よりも多くの - ではない非常に横断します大木。私はベクトルを削除するためのコードを微調整するとき
しかし、私は...
(defparameter *barning-matrixes*
'((1 -2 2) (2 -1 2) (2 -2 3)
(1 2 2) (2 1 2) (2 2 3)
(-1 2 2) (-2 1 2) (-2 2 3)))
(defparameter *lengths* (make-array 1500001 :initial-element 0))
(defun expand-node (n)
"Takes a primitive Pythagorean triple in a list and traverses subsequent nodes in the the tree of primitives until perimeter > 1,500,000"
(let ((perimeter (reduce #'+ n)))
(unless (> perimeter 1500000)
(let ((next-nodes (mapcar #'(lambda (x) (reduce #'+ (mapcar #'* n x))) *barning-matrixes*)))
(loop for i from perimeter to 1500000 by perimeter
do (incf (aref *lengths* i)))
(expand-node (subseq next-nodes 0 3))
(expand-node (subseq next-nodes 3 6))
(expand-node (subseq next-nodes 6 9))))))
(expand-node '(3 4 5)) ; Much faster, but why?!
(count 1 *lengths*)
驚いた...とトラバースはわずか35ミリ秒を取って、非常に高速行ってきました。私はこの大きな違いに興味を持っていて、そこにいる誰かがなぜそれが起こったのか説明できると思っています。
おかげで、 パウロ
PS:私はすべてこのためにCCLを使用しています。
注目すべき点は、多くのシーケンス関数が開始引数と終了引数をとることです。例えば、 '(reduce ...:start s:end e)'を使うことができます。これは、 'subseq'を使わないシーケンスの再帰的処理をしばしば行うことを意味し、代わりに*同じ*シーケンス。これはメモリ割り当ての*ロット*を避けるのに役立ちます。 –
@Joshua確かに、私の最も内側の閉鎖のリストのスライスとダイシングはかなり面倒です。しかし、私は再帰に数ミリ秒の "ドラッグ"を追加します。 –
私は一番内側の閉鎖を意味しませんでした。私は最後にsubseqへの3つの呼び出しを意味しました。あなたはリストを作成した後、3つのサブシーケンスを取ります。つまり、そのリストを再作成します(元のリストを複数回横断する)ことを意味します。 expand-nodeがstartおよびendパラメータを取る場合(そして次のノードが配列だった場合)、はるかに少ないコピーでより多くのことを行うことができます)。 –