2016-11-11 3 views
0

Как создать нормальное распределение значений с использованием Clojure? На самом деле не обязательно истинное нормальное распределение, а одно, которое может быть искажено.Генерировать данные кривой кривой с использованием Clojure

В качестве примера я хотел бы создать функцию, которая выводит сгенерированное (псевдослучайное) число для концентрации кислорода в воздухе по объему. Самый низкий возможный выход должен составлять 19,5% и максимально возможный 23,5%, тогда как модальное значение должно составлять 20,95%. Функция должна работать для этого «перекошенного нормального» распределения, где нижняя часть хвоста имеет диапазон 1,45%, а верхняя часть - 2,55%.

ответ

2

Существует простой способ получить любое распределение, которое вы хотите, если вы можете построить функцию y=f(x), которая описывает функцию плотности вероятности.

для гауссова, эта функция F (X) = ехр (- (Xm)^2/(2 * S^2))/SQRT (2 * пи * с^2) (см https://en.wikipedia.org/wiki/Gaussian_function)

Где m - среднее значение x и s - std dev of x.

Для «нормального» гаусса, где m = 0 и s = 1, значения «почти никогда» не превышают +/- 3 (точное количество осталось в виде упражнения для читателя). Учитывая это приближение, самый простой способ получить гауссовский dist - создать значение с плавающей запятой x в интервале [-3..3] И значение y в интервале [0..1]. Затем вычислите f (x), как указано выше: exp (...) и т. Д. Затем IFF y < = f (x) используйте значение x в качестве вашего случайного значения. В противном случае отбросьте как x, так и y и начните сначала.

Хотя этот метод выбрасывает некоторые (или многие) значения, он очень прост и пуленепробиваемый.

Вы можете использовать подобный метод для вашего «искаженного гауссовского» приближения, просто определите свой собственный f (x), как вы описали. Для действительно простого приближения вы могли бы даже использовать прямолинейное приближение от (19,5,0) до (20,95,1) до (23,5,0), где это делает треугольную форму для f (x). В этом случае нарисуйте x в интервале [19.5..23.5] и вычислите прямолинейную формулу для левых & правых половин для f (x). Нарисуйте y в [0..1], как и раньше.

Я просто нашел, что Википедия описывает это более подробно: https://en.wikipedia.org/wiki/Rejection_sampling

0

Эта реализация вдохновленный двух идей в ответ Алана Томпсона: «отказ выборки» и с использованием треугольника, а не кривой колокол рода формы, т.е. масштабирования.

(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)) 
+0

fwiw, «rand» Clojure основан на Java «Random», поэтому, если последний не является достаточно высококачественным генератором случайных чисел для данного использования, тогда и rand' тоже не будет. Однако для многих ситуаций это достаточно хорошо. – Mars

2

Вы можете использовать Ведун sample-normal для генерации (без перекошенных) нормально распределенных чисел. Например, это будет генерировать 20 нормально распределенных значений со средним значением 2 и стандартное отклонение 5:

(ns foo 
    (:use [incanter.stats])) 

(sample-normal 20 :mean 2 :sd 5) 

Можно также использовать Random или генератора в Java лучше случайных чисел в Java, такие как Шон Люк MersenneTwisterFast. Например, для использования MersenneTwisterFast загрузите исходный файл java и поместите его, например. в src/java в дереве проекта Leiningen. Затем добавьте строку типа :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 (хотя ваши случайные числа могут быть не такими случайными, как у Mersenne Twister).

Как объясняется this answer в stats.SE, вы можете получить произвольное нормальное распределение из стандартного нормального распределения, умножив значение выше на требуемое стандартное отклонение и затем добавив желаемое среднее значение.

Ответ Алана Томпсона объясняет, как производить усеченное распределение из обычного нормального распределения.

This answer at stats.SE дает рекомендации относительно создания перекошенного нормального распределения из нормального распределения.

+0

Спасибо @ChrisMurphy. Ты прав. Исправлено. (Фактически, версия, используемая мной, имеет две функции и автоматически вызывает функцию генерации семени, если нет аргументов. Я упростил 'make-rng' для ясности здесь.) – Mars

Смежные вопросы