Натуральний Пі №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. Якщо вам справді потрібен Пі під час апокаліпсису зомбі, ці методи не витрачають боєприпаси ! Всього дев'ять викликів .


Я думав, ти вже робив номер 1?
Conor O'Brien


Проблема в цьому полягає в тому, що в мовах без складних чисел потрібно перетворити число 0..180 в 0..pi, що швидше перемагає мету експерименту з голкою буфона.
Річка Рівня Св

@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-осі не має значення, лише положення відносно найближчих паралельних прямих.

По суті це завдання в 1-мірному просторі, де ми формуємо лінію довжиною в [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)

Ці цифри інтерпретуються як півповорот (тобто .590 градусів). (ОП просить градусів від 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))

Решта коду - це лише реалізація формули наближення pi, тобто (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 Приємний! Невже ви на початку не зможете пропустити дужки? (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..$_

Підрахувавши шебанг як один, вхід береться зі stdin, пробіл розділений. Якби дозволені не цілі випадкові значення, це може бути дещо коротшим.

Я взяв одну свободу, наближаючи π / 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

Пітон 2, 141 байт

безсоромний порт rtumbull, вже пропускаючи 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.
примо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.