使用Clojure生成钟形曲线数据



如何使用Clojure生成值的正态分布?实际上不一定是真正的正态分布,但可能是偏态分布。

作为一个例子,我想创建一个函数,输出一个生成的(伪随机)数字,用于按体积计算空气中的氧气浓度。最小可能输出值为19.5%,最大可能输出值为23.5%,而模态值应为20.95%。该函数应该适用于这种"倾斜正态"分布,其中尾部的较低部分的范围为1.45%,较高部分的范围为2.55%。

您可以使用Incanter的sample-normal来生成(非偏斜)正态分布的数字。例如,这将生成20个正态分布的值,平均值为2,标准差为5:

(ns foo 
  (:use [incanter.stats]))
(sample-normal 20 :mean 2 :sd 5)

您也可以使用Java的Random或Java中更好的随机数生成器,例如Sean Luke的MersenneTwisterFast。例如,要使用MersenneTwisterFast,请下载java源文件并将其放在Leiningen项目树中的src/java中。然后添加像:java-source-paths ["src/java"]这样的一行到project.clj。在Clojure源文件中:

(ns foo
  (:import [ec.util MersenneTwisterFast]))
(defn make-rng
  "Make an instance of a MersenneTwisterFast RNG and flush out its initial
  minimal lack of entropy."
  [seed]
  (let [rng (MersenneTwisterFast. seed)]
    (dotimes [_ 1500] (.nextInt rng)) ; needed because of a quirk of Mersenne Twisters
    rng))
(def my-rng (make-rng 42))
;; Now call this as many times as you want to generate standard Normal data:
(.nextGaussian my-rng)

使用Java的Random是类似的,没有下载和移动到src/Java等,但我不认为你需要冲洗Java的Random与1500 .nextInt s(尽管你的随机数可能不像那些随机从梅森扭)。

作为这个答案在stats。SE解释说,你可以通过将上面的值乘以你想要的标准差,然后加上想要的平均值,从标准正态分布得到任意正态分布。

Alan Thompson的答案解释了如何从正态分布产生截断分布。

这个答案在stats。SE给出了从正态分布产生偏态正态分布的相关建议。

EDIT (2021): SciCloj社区项目在这里列出了一些值得探索的其他库。我没有使用过这些库中的大多数,所以我不能给出更详细的建议,但似乎值得一提。

如果您可以绘制描述概率密度函数的函数y=f(x),则有一种简单的方法可以获得您想要的任何分布。

对于高斯函数,这个函数是f(x)=exp(-(x-m)^2/(2 * s^2))/sqrt(2pi s^2)(参见https://en.wikipedia.org/wiki/Gaussian_function)

其中m为x的均值,s为x的标准差

For "normal"当m=0且s=1时,几乎不存在+/-3之外的值(留给读者练习的确切数量)。给定这个近似值,得到高斯距离的最简单方法是在区间[-3..]内生成一个x浮点值。3]和区间[0..1]内的y值。然后像上面那样计算f(x): exp(…)等等。然后,IFF y<=f(x),使用值x作为您的随机值。否则,丢弃x和y并重新开始。

虽然这种技术会丢弃一些(或许多)值,但它非常简单且无懈可击。

您可以使用类似的方法来处理您的"偏斜高斯分布"。近似,只要定义你自己的f(x)就可以了。对于一个非常简单的近似,你甚至可以使用从(19.5,0)到(20.95,1)到(23.5,0)的直线近似,这样f(x)就形成了一个三角形。在此例中,在区间[19.5..23.5]内画出x,计算左侧& &的直线公式;f(x)的右二分之一。在[0..]

我刚发现维基百科对此有更详细的描述:https://en.wikipedia.org/wiki/Rejection_sampling


更新:

如果你只想要高斯随机变量(或其他常见分布),你可以使用Apache Commons Math库。

这个实现的灵感来自Alan Thompson回答中的两个想法:"拒绝抽样"和使用三角形,而不是钟形曲线的形状,即缩放。

(defn generator [modal-val low-val high-val]
  (fn []
    (let [gen-val (fn []
                    (let [diff-range (- high-val low-val)
                          picked-in-range (rand diff-range)
                          perhaps-res (+ low-val picked-in-range)
                          ;; partial distance left or right, that will be negative if to left
                          modal-delta (- perhaps-res modal-val)
                          extremity (if (neg? modal-delta) low-val high-val)
                          ;; full distance left or right, that will be negative if to left
                          total-dist (- extremity modal-val)
                          closeness-to-modal (- 1 (/ modal-delta total-dist))
                          ]
                      (when (<= (rand) closeness-to-modal)
                        perhaps-res)))]
      (first (drop-while nil? (repeatedly gen-val)))))) 

可以这样使用:

((generator 20.95 19.5 23.5))

最新更新