[英]Takeuchi numbers in Clojure (performance)
在计算Takeuchi数字时 ,我们需要计算出函数调用自身的次数。 我很快想出来:
(def number (atom 0))
(defn tak [x y z]
(if (<= x y)
y
(do
(dosync (swap! number inc))
(tak (tak (dec x) y z)
(tak (dec y) z x)
(tak (dec z) x y)))))
(defn takeuchi_number [n]
(dosync (reset! number 0))
(tak n 0 (inc n))
@number)
(time (takeuchi_number 10))
; 1029803
; "Elapsed time: 11155.012266 msecs"
但表现非常糟糕。 如何在Clojure中快速制作它?
正如有人所说,删除dosync似乎可以将事情提高10倍,但这不是全部。 一旦JVM热插入您的代码,它就会更快地获得10倍的因子。 这就是为什么你应该使用标准或类似的测试现实世界的速度...
(def number (atom 0))
(defn tak [x y z]
(if (<= x y)
y
(do
(swap! number inc)
(tak (tak (dec x) y z)
(tak (dec y) z x)
(tak (dec z) x y)))))
(defn takeuchi_number [n]
(reset! number 0)
(tak n 0 (inc n))
@number)
;=> (time (takeuchi_number 10))
; "Elapsed time: 450.028 msecs"
; 1029803
;=> (time (takeuchi_number 10))
; "Elapsed time: 42.008 msecs"
; 1029803
在我的机器上使用dosync的原装约为5s,所以我们已经订购了两个基本10级的订单! 这是我们能做的最好的吗? 让我们重构纯函数并远离计数器。
(defn tak [c x y z]
(if (<= x y)
[c y]
(let [[a- x-] (tak 0 (dec x) y z)
[b- y-] (tak 0 (dec y) z x)
[c- z-] (tak 0 (dec z) x y)]
(recur (+' 1 a- b- c- c) x- y- z-))))
(defn takeuchi_number [n]
(tak 0 n 0 (inc n)))
;=> (time (takeuchi_number 10))
; "Elapsed time: 330.741 msecs"
; [1029803 11]
;=> (time (takeuchi_number 10))
; "Elapsed time: 137.829 msecs"
; [1029803 11]
;=> (time (takeuchi_number 10))
; "Elapsed time: 136.866 msecs"
; [1029803 11]
不太好。 将状态保持在向量中并将其传递的成本可能是开销。 但是,现在我们已经重构了纯度,让我们利用我们的良好行为!
=> (def tak (memoize tak))
#'euler.tak/tak
=> (time (takeuchi_number 10))
"Elapsed time: 1.401 msecs"
[1029803 11]
健康快3000左右。 适合我。
实现这一功能的一种纯函数方式是让你的tak
函数返回一对[result count]
,其中result
是tak
计算的实际结果, count
是函数递归调用自身的次数。 但在这种情况下,我认为这会导致功能体内各种各样的痛苦扭曲,并且不值得。
这里使用atom
,而习惯性的Clojure,会带来不必要的开销; 它真正的目标是将独立更新同步到线程之间的共享状态。 基本上你想要的是一个可变对象,你可以在同一个线程中传递给递归函数调用,不需要同步。 数组应足以满足此目的:
(defn tak [x y z ^longs counter]
(if (<= x y)
y
(do
(aset counter 0 (inc (aget counter 0)))
(tak (tak (dec x) y z counter)
(tak (dec y) z x counter)
(tak (dec z) x y counter)
counter))))
(defn takeuchi_number [n]
(let [counter (long-array [0])]
(tak n 0 (inc n) counter)
(aget counter 0)))
请注意,我已将计数器定义从作为全局常量移动到辅助函数的参数,以确保可变状态仅在该函数内本地使用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.