Натуральный Пи № 1 - Песок

9

Цель

Генерируйте ( N) случайные отрезки одинаковой длины ( l), проверьте, пересекают ли они равноотстоящие ( t) параллельные линии.

моделирование

Что мы моделируем? Игла Буффона . Разгладьте песок в своей песочнице, нарисуйте набор параллельных линий, расположенных на равных расстояниях (назовите расстояние между ними t). Возьмите прямую палку длины lи бросьте ее Nв песочницу. Пусть будет количество пересечений линии c. Тогда Pi = (2 * l * n) / (t * c)!

Как мы моделируем это?

  • Принять вход N,t,l
  • Со N, t, lвсеми положительными целыми числами
  • Делайте следующее Nвремя:
    • Генерация равномерно случайной целочисленной координаты x,y
    • С 1 <= x, y <= 10^6
    • x,y является центром отрезка длины l
    • Генерация равномерно случайного целого числа a
    • С 1 <= a <= 180
    • Позвольте Pбыть точкой, где отрезок будет пересекать ось X
    • Тогда aугол(x,y), P, (inf,0)
  • Подсчитайте количество cотрезков, которые пересекают линию x = i*tдля любого целого числаi
  • Возвращение (2 * l * N) / (t * c)

введите описание изображения здесь

введите описание изображения здесь

Спецификация

  • вход
    • Гибкость, принимать входные данные любым из стандартных способов (например, параметр функции, STDIN) и в любом стандартном формате (например, String, Binary)
  • Вывод
    • Гибкость, вывод на печать любым из стандартных способов (например, возврат, печать)
    • Пробел, конечный и ведущий пробел приемлем
    • Точность, укажите не менее 4 знаков после запятой (т.е. 3.1416)
  • счет
    • Самый короткий код выигрывает!

Тестовые случаи

Ваш результат может не совпадать с этим из-за случайного шанса. Но в среднем вы должны получить примерно такую ​​точность для заданного значения N, t, l.

Input (N,t,l)    ->  Output 
-----------        ------
10,10,5          -> ?.????
10,100,50        -> ?.????
1000,1000,600    -> 3.????
10000,1000,700   -> 3.1???
100000,1000,700  -> 3.14??

TL; DR

Эти проблемы представляют собой симуляции алгоритмов, которые требуют только природы и вашего мозга (и, возможно, некоторых ресурсов многократного использования) для приближения Pi. Если вам действительно нужен Пи во время апокалипсиса зомби, эти методы не тратят патроны ! Всего девять задач .

NonlinearFruit
источник
Я думал, что вы уже сделали номер 1?
Конор О'Брайен
1
@ ConorO'Brien, я ноль, индексирую это XD
Нелинейный
проблема в том, что в языках без комплексных чисел вам нужно превратить число 0..180 в 0..pi, что, скорее, отрицательно сказывается на цели эксперимента с иглой буффона.
Уровень Река St
@NonlinearFruit может ли направление aбыть создано другим методом, если оно однородно? (думая о 2D
пузырьке
1
Можно ли это предположить t > l? Два решения ниже делают это предположение, которое немного упрощает проверку на пересечение.
Примо

Ответы:

9

R, 113 100 75 70 68 67 65 59 63 57 байт

Как статистический, функциональный язык программирования, неудивительно, что R достаточно хорошо подходит для такого рода задач. Тот факт, что большинство функций может принимать векторизованный ввод, действительно полезен для этой проблемы, поскольку вместо циклического Nповторения мы просто передаем векторы размера N. Спасибо @Billywob за некоторые предложения, которые приводят к сокращению 4 байтов. Большое спасибо @Primo за терпеливое объяснение, как мой код не работал в тех случаях t > l, когда это исправлено.

pryr::f(2*l*N/t/sum(floor(runif(N)+sinpi(runif(N))*l/t)))

Попробуйте онлайн!

Пример вывода:

N=1000, t=1000, l=500
3.037975

N=10000, t=1000, l=700
3.11943

N=100000, t=1000, l=700
3.140351

объяснение

Проблема сводится к тому, чтобы определить, находятся ли два xзначения стрелки по обе стороны параллельной линии. Это имеет несколько важных последствий:

  1. y-значения не имеют значения
  2. Абсолютное местоположение на xоси не имеет значения, только положение относительно ближайших параллельных линий.

По сути, это задача в одномерном пространстве, где мы генерируем линию с длиной в [0, l] (угол aопределяет эту длину), а затем мы проверяем, во сколько раз эта длина превышает t. Грубый алгоритм тогда:

  1. Выборочные x1значения из [0, 1000000]. Поскольку параллельные линии встречаются в каждой tточке вдоль xоси-осей, относительное xположение является xмодулем t.
  2. Пример угла a.
  3. Рассчитать x2позицию на основе a.
  4. Проверьте, сколько раз x1+x2вписывается t, то есть взять слово (x1+x2)/t.

Выборка Nчисел в [0, 1e6] по модулю tэквивалентна простой выборке Nчисел в [0, t]. Поскольку (x1+x2)/tэквивалентно x1/t + x2/t, первый шаг становится выборкой из [0, t] / t, то есть [0, 1]. К счастью для нас, это диапазон по умолчанию для runifфункции R , которая возвращает Nдействительные числа от 0 до 1 из равномерного распределения.

                          runif(N)

Мы повторяем этот шаг, чтобы сформировать aугол наклона иглы.

                                         runif(N)

Эти числа интерпретируются как пол оборота (то .5есть 90 градусов). (ОП просит градусов от 1 до 180, но в комментариях это уточнить , что любой метод, допускается , если это так , или более точным.) Для угла θ, sin(θ)дает нам расстояние по оси х между концами иглы. (Обычно вы используете косинус для чего-то подобного; но в нашем случае мы рассматриваем угол θкак относительный относительно оси y, а не оси x (то есть значение 0 градусов увеличивается , а не справа ), и поэтому мы используем синус, который в основном сдвигает числа по фазам.) Умноженное на lэто дает нам xместоположение конца иглы.

                                   sinpi(runif(N))*l

Теперь мы делим на tи добавляем x1значение. Это дает то (x1+x2)/t, насколько далеко выступает игла x1, с точки зрения количества параллельных линий. Чтобы получить целое число пересеченных линий, мы берем floor.

                    floor(runif(N)+sinpi(runif(N))*l/t)

Мы вычисляем сумму, давая нам счет cтого, сколько линий пересекают иглы.

                sum(floor(runif(N)+sinpi(runif(N))*l/t))

Остальная часть кода просто реализует формулу для аппроксимации пи, то есть (2*l*N)/(t*c). Мы экономим несколько байтов в скобках, используя тот факт, что (2*l*N)/(t*c) == 2*l*N/t/c:

        2*l*N/t/sum(floor(runif(N)+sinpi(runif(N))*l/t))

И все это заключено в анонимную функцию:

pryr::f(2*l*N/t/sum(floor(runif(N)+sinpi(runif(N))*l/t)))
rturnbull
источник
@rturnbull Хороший! Разве вы не должны быть в состоянии пропустить скобки в начале, хотя? (2*l*N) => 2*l*N?
Billywob
@Billywob Хорошо заметили! Спасибо.
rturnbull
@rturnbull Да, кстати, (2*l*N)/(t*c) = 2*l*N/t/cвы можете сохранить еще два байта, пропустив скобки и в последней части.
Billywob
@ Billywob Опять хорошо заметили! Еще раз спасибо.
rturnbull
1
@primo Еще раз спасибо, это должно быть исправлено сейчас.
rturnbull
6

Perl, 97 байт

#!perl -p
/ \d+/;$_*=2*$'/$&/map{($x=(1+~~rand 1e6)/$&)-$a..$x+($a=$'/$&/2*sin~~rand(180)*71/4068)-1}1..$_

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

Я взял одну свободу, приблизив π / 180 к 71/4068 , что с точностью до 1,48 · 10 -9 .

Образец использования

$ echo 1000000 1000 70000 | perl pi-sand.pl
3.14115345174061

Более или менее математически эквивалентные замены

Предполагая, что координата x представляет крайнюю левую точку иглы, а не ее середину, как указано в описании проблемы:

89 байт

#!perl -p
/ \d+/;$_*=2*$'/$&/map{($x=(1+~~rand 1e6)/$&)..$x+($'/$&*sin~~rand(180)*71/4068)-1}1..$_

Проблема указывает, что xвыборка должна быть случайным целым числом. Если спроецировать межстрочный интервал на разрыв одного, это оставит нас значение формы n/tс 0 <= n < t, не обязательно однородным, если tне равномерно разделить 1e6. Предполагая, что равномерное распределение, тем не менее, приемлемо:

76 байт

#!perl -p
/ \d+/;$_*=2*$'/$&/map{($x=rand)..$x+($'/$&*sin~~rand(180)*71/4068)-1}1..$_

Обратите внимание, что, поскольку randвсегда будет меньше единицы (и, следовательно, обрезается до нуля), в начале диапазона это необязательно:

70 байт

#!perl -p
/ \d+/;$_*=2*$'/$&/map{1..(rand)+($'/$&*sin~~rand(180)*71/4068)}1..$_

Предполагая, что угол наклона иглы не должен быть целым градусом, а только равномерно случайным:

59 байт

#!perl -p
/ \d+/;$_*=2*$'/$&/map{1..(rand)+abs$'/$&*sin rand$`}1..$_

Предполагая, что угол может быть любым равномерным распределением:

52 байта

#!perl -p
/ \d+/;$_*=2*$'/$&/map{1..(rand)+abs$'/$&*sin}1..$_

Выше приведено математически правильное моделирование иглы Буффона. Тем не менее, на данный момент я думаю, что большинство людей согласятся, что это на самом деле не тот вопрос, о котором спрашивают.


Действительно толкаю это

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

47 байт

#!perl -p
/ \d+/;$_*=$'/$&/map{1..(rand)+$'/$&*sin}1..$_

Обратите внимание, что значения tи lнесущественны для результатов эксперимента. Мы могли бы просто игнорировать их (неявно предполагая, что они равны):

28 байт

#!perl -p
$_/=map{1..(rand)+sin}1..$_

Очевидно, что он неконкурентный, но вы должны признать, что он обладает определенной элегантностью.

Примо
источник
4

Python 2, 141 байт

бесстыдный порт грохота, уже пропускающий, yпотому что совершенно не нужен.

from math import*
from random import*
lambda N,t,l:(2.*l*N)/(t*sum(randint(1,1e6)%t+abs(cos(randint(1,180)*pi/180))*l>t for _ in range(N)))

Проблема только в том, что пи уже известен в программе.

Вот он (играбельный) с неизвестным пи и без тригонометрических функций

def g(N,t,l):
 c=0
 for _ in range(N):
    x,y=gauss(0,1),gauss(0,1);c+=randint(1,1e6)%t+abs(x/sqrt(x*x+y*y))*l>t
 return(2.*l*N)/(t*c)

x,yв gтолько для направления.

Карл Напф
источник
Требуется from random import randint;from math import cos,pi. Сбой t < l, например 1000000,1000,70000.
Примо