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 集函数未按预期执行的主要内容,如果未能解决你的问题,请参考以下文章

Azure API 管理集状态代码策略未按预期工作

改进我的 Mandelbrot 集代码

AuthZForce PDP 未按预期运行

未按预期应用约束

LibreOffice 对话框未按预期工作

C# 布尔评估未按预期进行评估