Сегодня утром я читал сообщение из блога Toward Data Science о математическом программировании для развития навыков в области науки о данных. Хотя статья была основана на Python, в ней не использовались популярные фреймворки, такие как NumPy или SciPy.

После некоторого перерыва я хотел, чтобы мой мозг работал нормально, поэтому мысль об использовании математики в Clojure мне очень понравилась. И я не говорю, что один лучше другого. Лучший язык для науки о данных - тот, который вы знаете. Главный ключ к науке о данных - это хорошее знание математики, а не фреймворков, чтобы упростить задачу.

Вычисление числа Пи путем имитации случайных бросков дротика

Метод Монте-Карло - это концепция имитации случайного процесса. Если процесс повторяется большое количество раз, это приведет к приближению некоторой математической величины, представляющей интерес.

Представьте себе квадратную доску для дартса ...

Теперь представьте квадратную доску для дартса с кругом внутри квадрата, края круга касаются квадрата ...

Если вы бросите достаточно дротиков в доску, некоторые из них приземлятся внутри круга, а некоторые за его пределами. Как это было наглядно сказано в оригинальной статье:

Это случайные броски, вы можете бросить 10 раз, вы можете бросить 1 миллион раз. В конце броска дротика вы подсчитываете количество дротиков в круге, делите его на количество бросков (10, 1 м и т. Д.), А затем умножаете на 4.

Как указано в исходной статье: вероятность падения дротика внутри круга - это просто отношение площади круга к площади квадратной доски.

Чем больше бросков мы сделаем, тем больше у нас будет шансов найти число рядом с Пи. Закон больших чисел в действии.

Бросок дротика в доску

Я собираюсь создать функцию, имитирующую одиночный бросок дротика. Я хочу разбить свой код Clojure на как можно больше простых функций. На мой взгляд, это значительно упрощает тестирование и поиск ошибок.

(defn throw-dart [] {:x (calc-position 0) :y (calc-position 0)})

Я создаю координаты x, y с центральной точкой 0,0, а затем передаю координаты x и y через другую функцию для вычисления положения (calc-position).

(def side-of-square 2) (defn calc-position [v] (* (/ (+ v side-of-square) 2) (+ (- 1) (* 2 (Math/random)))))

Функция calc-position принимает значение x и y и применяет вычисление, это где-то -side-of-square / 2 и +-side-of-square / 2 вокруг центральной точки.

Запустив эту функцию в REPL, мы можем увидеть позиции x или y.

mathematical.programming.examples.montecarlo> (calc-position 0) 0.4298901518005238

Дротик внутри круга?

Теперь у меня есть координаты x, y в виде карты {:x some random throw value :y some random throw value}. Я хочу подтвердить, что бросок находится внутри круга.

Снова используя значение side-of-square (следовательно, это def), я могу выяснить, попадает ли дротик внутрь. Я передам карту с координатами x, y и извлечу квадратный корень из добавленных квадратов координат.

(defn is-within-circle [m] (let [distance-from-center (Math/sqrt (+ (Math/pow (:x m) 2) (Math/pow (:y m) 2)))] (< distance-from-center (/ side-of-square 2))))

Эта функция вернет true или false. Если я проверю это в REPL, это будет выглядеть так:

mathematical.programming.examples.montecarlo> (throw-dart) {:x 0.22535085231582297, :y 0.04203583357796781} mathematical.programming.examples.montecarlo> (is-within-circle *1) true

Теперь много дартс

Пока что есть функции для имитации броска дротика и подтверждения того, что он находится внутри круга. Теперь мне нужно повторить этот процесс столько раз, сколько потребуется.

Я создаю две функции: compute-pi-throwing-dart для выполнения желаемого количества бросков и throw-range для фактической работы по определению количества истинных попаданий в круге.

(defn throw-range [throws] (filter (fn [t] (is-within-circle (throw-dart))) (range 0 throws))) (defn compute-pi-throwing-dart [throws] (double (* 4 (/ (count (throw-range throws)) throws))))

Функция throw-range выполняет функцию throw-dart, а is-within-circle оценивает карту, чтобы увидеть, является ли значение истинным или ложным. Функции filter вернут список истинных значений. Так, например, если из десяти бросков первый, третий и пятый попадают в круг, я получу (1,3,5) как результат функции.

Вызов функции compute-pi-throwing-dart приводит все это в движение. Как я сказал в начале, если взять количество дротиков в круге и разделить его на количество выполненных бросков, умножив это на четыре, получится число, близкое к Пи.

Чем больше вы сделаете бросков, тем ближе будет результат.

mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 10) 3.2 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 10) 3.2 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 10) 3.6 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 10) 2.4 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 10) 4.0 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 10) 2.8 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 100) 2.92 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 1000) 3.136 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 10000) 3.138 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 100000) 3.15456 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 1000000) 3.13834 mathematical.programming.examples.montecarlo> (compute_pi_throwing_dart 10000000) 3.1419096

Давайте построим моделирование

Через REPL есть доказательство эмерджентного поведения, значение Пи зависит от большого количества бросков, которые мы сделали на доске для дартса.

Последнее, что я сделаю, это построю функцию для запуска симуляции.

(defn run-simulation [iter] (map (fn [i] (let [throws (long (Math/pow 10 i))] (compute-pi-throwing-dart throws))) (range 0 iter)))

Если я проведу 4 моделирования, я получу 1, 10, 100 и 1000 бросков, которые затем возвращаются в виде списка. Если я проведу 9 симуляций (что может занять некоторое время в зависимости от используемой вами машины) в REPL, я получу следующее:

mathematical.programming.examples.montecarlo> (run-simulation 9) (0.0 3.6 3.28 3.128 3.1176 3.1428 3.142932 3.1425368 3.14173752)

Это хорошее приближение, Pi равно 3,14159265, поэтому неплохо получить метод Монте-Карло для вычисления Pi путем случайных вычислений.

Первоначально опубликовано на http://dataissexy.wordpress.com 15 апреля 2019 г.