Mandelbrot 集函数未按预期执行
Posted
技术标签:
【中文标题】Mandelbrot 集函数未按预期执行【英文标题】:Mandelbrot set function does not perform as expected 【发布时间】:2015-05-12 21:42:15 【问题描述】:这是来自Clojure Programming Paperback by Chas Emerick:的示例
(import 'java.awt.image.BufferedImage
'(java.awt Color RenderingHints))
(defn- escape
[^double a0 ^double b0 ^long depth]
(loop [a a0, b b0, iteration 0]
(cond
(< 4 (+ (* a a) (* b b))) iteration
(>= iteration depth) -1
:else (recur (+ a0 (- (* a a) (* b b)))
(+ b0 (apply * [2 a b]))
(inc iteration)))))
(defn mandelbrot [rmin rmax imin imax
& :keys [width height depth]
:or width 80 height 40 depth 1000]
(let [mandelbrot-help
(fn [^double rmin ^double rmax
^double imin ^double imax
]
(let [stride-w (/ (- rmax rmin) width)
stride-h (/ (- imax imin) height)]
(loop [x 0
y (dec height)
escapes []]
(if (== x width)
(if (zero? y)
(partition width escapes)
(recur 0 (dec y) escapes))
(recur (inc x) y (conj escapes
(escape (+ rmin (* x stride-w))
(+ imin (* y stride-h))
depth)))))))]
(mandelbrot-help rmin rmax imin imax)))
(defn render-text
[mandelbrot-grid]
(doseq [row mandelbrot-grid]
(doseq [escape-iter row]
(print (if (neg? escape-iter)
\*
\space)))
(println)))
(defn render-image
[mandelbrot-grid]
(let [palette
(vec (for
[c (range 500)]
(Color/getHSBColor 0.0 0.0 (/ (Math/log c) (Math/log 500)))))
height (count mandelbrot-grid)
width (count (first mandelbrot-grid))
img (BufferedImage. width height BufferedImage/TYPE_INT_RGB)
^java.awt.Graphics2D g (.getGraphics img)]
(doseq [[y row] (map-indexed vector mandelbrot-grid)
[x escape-iter] (map-indexed vector row)]
(.setColor g (if (neg? escape-iter)
(palette 0)
(palette (mod (dec (count palette)) (inc escape-iter)))))
(.drawRect g x y 1 1))
(.dispose g)
img))
(do (time (mandelbrot -2.25 0.75 -1.5 1.5
:width 1600 :height 1200 :depth 1000))
nil)
一切正常,除了在我的机器上需要 60 秒,而根据书本只需要 8 秒(我的笔记本电脑上的结果在其他示例中始终更好)。
是不是我做错了什么?
【问题讨论】:
还有其他使用 java.awt 的例子吗?在图形方面可能是系统特定的。另外,也许一些分析会带来一些启示:***.com/questions/2974916/profiling-tool-for-clojure 在我的 i7 上运行了大约一分半钟。也许查斯有一个真正的机器野兽?书中是否还有其他与您的结果不符的类似时间报告? 不是真的,这是第一个。 【参考方案1】:您从哪里获得该代码?这绝对不是书中出现的内容(基于我的 PDF 副本,第 449-452 页)或code sample on github。特别是escape
中的(apply * [2 a b])
是疯狂;这永远不会很快(至少在没有任何程度的 [微不足道的] 源代码级优化的情况下,很遗憾,Clojure 并不适用)。更奇怪的是,该特定的 sn-p 并没有出现在本书的任何地方,我也无法在我们曾经合作编写本书的 git repo 的历史中找到它。
也许您只是在修改示例?如果不是,我真的很想知道您的样本来自哪里,因为它绝对不能代表我们的意图或最佳实践(显然,鉴于您所看到的时间安排)。
无论如何,这是本书/github 示例 repo 中的“快速escape
”函数:
(defn- escape
[^double a0 ^double b0 depth]
(loop [a a0
b b0
iteration 0]
(cond
(< 4 (+ (* a a) (* b b))) iteration
(>= iteration depth) -1
:else (recur (+ a0 (- (* a a) (* b b)))
(+ b0 (* 2 (* a b)))
(inc iteration)))))
user> (do (time (mandelbrot -2.25 0.75 -1.5 1.5
:width 1600 :height 1200 :depth 1000))
nil)
"Elapsed time: 1987.460104 msecs"
将depth
arg 提示为^long
(我应该在本书示例中包含的内容)在我的笔记本电脑上将其降至 1450 毫秒。
【讨论】:
啊,我没有复制那些sn-ps,而是手动输入的,我认为(apply * [2 a b])
应该和你的版本相当。这会让我在使用 apply
之前三思而后行...谢谢!
顺便说一句,微不足道的源级优化是什么意思?
噢,好吧,这真是一种解脱。很高兴知道有一个无害的解释。 apply
是 FP 的基础,当 fn 的元数已知时,它只是无用的开销。
许多编译器对源代码(或近源代码)应用优化,将 fn 应用到数据结构文字转换为直接调用,例如(apply * [a b c])
=> (* a b c)
。类似的优化是消除直接应用的 lambda,例如(#(+ 5 %) 8)
=> (+ 5 8)
.以上是关于Mandelbrot 集函数未按预期执行的主要内容,如果未能解决你的问题,请参考以下文章