Ich habe ein unerwartetes Ergebnis beim Lösen Problem 75 in Project Euler bekommen. Mein Code findet die richtige Lösung, aber es verhält sich merkwürdig.Arrays vs. Listen in Lisp: Warum sind Listen im folgenden Code so viel schneller?
Meine Lösung besteht darin, einen Pythagoreischen Baum (Barning's matrices) zu durchlaufen, bis das Perimeterlimit erreicht ist, die Anzahl der Male gezählt, die der Umfang jeden Wert angenommen hat, und schließlich die Perimeterlängen, die nur einmal aufgetreten sind. Mein zugegebenermaßen unordentlicher aber funktionierenden Code ist:
(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*)
ich den Baum Expansion erwartete in wenigen Millisekunden laufen, aber die expand-Knotenfunktion nahm 8,65 Sekunden - viel mehr als erwartet - ein nicht sehr zu durchqueren großer Baum.
aber ich war überrascht, als ich den Code, um die Vektoren zu entfernen gezwickt ...
(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*)
... und die Verfahrgeschwindigkeit ging schneller enorm, wobei nur 35 ms. Ich bin fasziniert von diesem gewaltigen Unterschied und hoffe, dass jemand da draußen erklären kann, warum es passiert ist.
Danke, Paulo
PS: Ich verwende CCL für das alles.
Ein Punkt, der hilfreich sein kann, um zu beachten, ist, dass viele der Sequenzfunktionen Argumente für Anfang und Ende haben. ZB können Sie '(reduzieren ...: start s: end e)', was bedeutet, dass Sie oft bei der rekursiven Verarbeitung von Sequenzen vermeiden können, 'subliveq' zu verwenden und stattdessen einfach auf einen anderen Teil des * same * zeigen Sequenz. Das kann helfen, eine Menge Speicherzuweisung zu vermeiden. –
@Joshua Tatsächlich ist das Schneiden und Schneiden von Listen in meinem innersten Verschluss ziemlich unordentlich. aber ich es fügt ein paar Millisekunden "ziehen" zur Rekursion hinzu. –
Ich meinte nicht in der innersten Schließung; Ich meinte in den drei anrufe nach unten am endeq. Sie haben eine Liste erstellt und nehmen dann drei Teilsequenzen davon, was bedeutet, dass Sie diese Liste erneut erstellen (und die ursprüngliche Liste mehrmals durchlaufen). Wenn expand-node einen Start- und einen Endparameter verwenden würde (und der nächste Knoten wäre ein Array), könnten Sie mit viel weniger Kopieren viel mehr tun. –