Случайный Гольф Дня № 4: Парадокс Бертран


19

О серии

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

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

Отверстие 4: парадокс Бертран

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

В этой задаче вы должны генерировать случайные аккорды единичного круга, используя «правильный» метод, то есть тот, который производит распределение аккордов, которое является инвариантным при масштабировании и трансляции. В связанной статье в Википедии «Метод 2» является таким методом.

Вот точные правила:

  • Вы должны взять одно положительное целое число,N которое указывает, сколько аккордов должно быть возвращено. На выходе должен быть список Nаккордов, каждый из которых указан в виде двух точек на единичной окружности, заданных их полярным углом в радианах.
  • Ваш код должен иметь возможность возвращать как минимум 2 20 различных значений для каждого из двух углов . Если имеющийся у вас ГСЧ имеет меньший диапазон, вы должны сначала создать ГСЧ с достаточно большим диапазоном поверх встроенного, либо вы должны реализовать свой собственный подходящий ГСЧ . Эта страница может быть полезна для этого.
  • Распределение аккордов должно быть неотличимо от распределения, произведенного «Методом 2» в связанной статье Википедии. Если вы применяете другой алгоритм выбора аккордов, пожалуйста, приложите подтверждение правильности. Какой бы алгоритм вы ни выбрали для реализации, он теоретически должен быть способен генерировать любой действительный аккорд в единичном круге (за исключением ограничений базовых типов данных PRNG или ограниченной точности).
  • Ваша реализация должна использовать и возвращать либо числа с плавающей запятой (шириной не менее 32 бит), либо числа с фиксированной запятой (шириной не менее 24 бит), и все арифметические операции должны быть точными в пределах не более 16 ulp .

Вы можете написать полную программу или функцию и получить ввод через STDIN (или ближайшую альтернативу), аргумент командной строки или аргумент функции и произвести вывод через STDOUT (или ближайшую альтернативу), возвращаемое значение функции или параметр функции (out).

Вывод может быть в любом удобном формате списка или строки, если отдельные числа четко различимы, а их общее число всегда четное.

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

Визуализация

Вы можете использовать следующий фрагмент для рендеринга сгенерированных строк и проверки их распределения. Просто вставьте список пар углов в текстовую область. Фрагмент должен быть в состоянии обрабатывать практически любой формат списка, если числа являются простыми десятичными числами (без научных обозначений). Я рекомендую вам использовать не менее 1000 строк, чтобы получить хорошее представление о распределении. Я также предоставил некоторые примеры данных для различных методов, представленных в статье ниже.

Пример данных, сгенерированных методом 1.

Пример данных, сгенерированных по методу 2.

Пример данных, сгенерированных по методу 3.

Leaderboard

Первый пост серии генерирует таблицу лидеров.

Чтобы убедиться, что ваши ответы отображаются, начните каждый ответ с заголовка, используя следующий шаблон уценки:

# Language Name, N bytes

где Nразмер вашего представления. Если вы улучшите свой счет, вы можете сохранить старые результаты в заголовке, вычеркнув их. Например:

# Ruby, <s>104</s> <s>101</s> 96 bytes

(Язык в настоящее время не отображается, но фрагмент требует и анализирует его, и я могу добавить таблицу лидеров по языкам в будущем.)

Ответы:


12

Машинный код IA-32, 54 байта

Hexdump кода:

68 00 00 00 4f 0f c7 f0 50 db 04 24 58 d8 34 24
f7 d9 78 f1 d9 c0 dc c8 d9 e8 de e1 d9 fa d9 c9
d9 f3 dc c0 d9 eb de ca d8 c1 dd 1a dd 5a 08 83
c2 10 e2 d1 58 c3

Он использует (слегка измененный) алгоритм, описанный в Википедии. В псевдокоде:

x = rand_uniform(-1, 1)
y = rand_uniform(-1, 1)
output2 = pi * y
output1 = output2 + 2 * acos(x)

Я использую диапазон, -1...1потому что в этом диапазоне легко создавать случайные числа: rdrandинструкция генерирует целое число между -2^31и 2^31-1, которое можно легко разделить на 2 ^ 31.

Я должен был использовать диапазон 0...1для другого случайного числа (х), которое вводится в acos; однако, отрицательная часть симметрична положительной части - отрицательные числа создают аккорды, чей размах больше, чем пи радиан, но для иллюстрации парадокса Бертрана это не имеет значения.

Поскольку в наборе команд 80386 (или x87) нет выделенной acosинструкции, мне пришлось выразить вычисление, используя только atanинструкцию:

acos(x) = atan(sqrt(1-x^2)/x)

Вот исходный код, который генерирует машинный код выше:

__declspec(naked) void __fastcall doit1(int n, std::pair<double, double>* output)
{
    _asm {
        push 0x4f000000;        // [esp] = float representation of 2^32

    myloop:
        rdrand eax;             // generate a random number, -2^31...2^31-1
        push eax;               // convert it
        fild dword ptr [esp];   // to floating-point
        pop eax;                // restore esp
        fdiv dword ptr [esp];   // convert to range -1...1
        neg ecx;
        js myloop;              // do the above 2 times

        // FPU stack contents:  // x           | y
        fld st(0);              // x           | x   | y
        fmul st(0), st;         // x^2         | x   | y
        fld1;                   // 1           | x^2 | x | y
        fsubrp st(1), st;       // 1-x^2       | x   | y
        fsqrt;                  // sqrt(1-x^2) | x   | y
        fxch;                   // x           | sqrt(1-x^2) | y
        fpatan;                 // acos(x)     | y
        fadd st, st(0);         // 2*acos(x)   | y
        fldpi;                  // pi          | 2*acos(x) | y
        fmulp st(2), st;        // 2*acos(x)   | pi*y
        fadd st, st(1);         // output1     | output2
        fstp qword ptr [edx];   // store the numbers
        fstp qword ptr [edx + 8];

        add edx, 16;            // advance the output pointer
        loop myloop;            // loop

        pop eax;                // restore stack pointer
        ret;                    // return
    }
}

Чтобы сгенерировать два случайных числа, код использует вложенный цикл. Чтобы организовать цикл, код использует ecxположительный регистр (счетчик внешнего цикла). Он временно отменяет ecx, а затем делает это снова, чтобы восстановить первоначальное значение. jsИнструкция повторяет цикл , когда ecxотрицательный (это происходит ровно один раз).


Мне нравится этот ответ для использования сборки IA32! Просто чтобы сказать: это не строго 386 машинный код, так как 80386, очевидно, не имеет ни инструкции rdrand, ни необходимого сопроцессора FP
user5572685

Да, IA32 - лучшее имя. Не достаточно конкретно, но, вероятно, более правильно, чем 80386.
Анатолий

7

Pyth, 25 23 22 байта

Порт ответа C ++ 11 на rcrmn. Это мое первое использование Pyth, и мне было очень весело!

VQJ,*y.n0O0.tOZ4,sJ-FJ

23-байтовая версия:

VQJ*y.n0O0K.tOZ4+JK-JKd

Сократите байт, изменив программу на использование складок + сумм и установив J в кортеж, удалив K.

Оригинал:

VQJ**2.n0O0K.tO0 4+JK-JKd

Отрежьте 2 байта благодаря @orlp.

Объяснение:

VQ                         loop as many times as the input number
  J,                       set J to the following tuple expression
    *y.n0O0                2 * .n0 (pi) * O0 (a random number between 0 and 1)
            .tOZ4          .tOZ 4 (acos of OZ (a random number))
                 ,sJ-FJ    print the sum of J and folding J using subtraction in parenthesis, separated by a comma, followed by another newline

1
Советы Pyth: так *2_же, как y_. Переменная Zизначально равна 0, поэтому вы можете удалить пробел .tO0 4, написав .tOZ4.
orlp

1
Я считаю 25 байтов ...
Maltysen

Кроме того, вы можете лучше отформатировать вывод с помощью,+JK-JK
Maltysen

@ Maltysen Оба исправлены. Благодарность!
kirbyfan64sos

@ orlp Я понятия не имел, yи забыл о Z. Исправлена; Благодарность!
kirbyfan64sos

6

Юлия, 48 байт

n->(x=2π*rand(n);y=acos(rand(n));hcat(x+y,x-y))

При этом используется алгоритм метода 2, как и большинство ответов на данный момент. Он создает лямбда-функцию, которая принимает целочисленный ввод и возвращает массив nx 2. Чтобы назвать его, дайте ему имя, например f=n->....

Ungolfed + объяснение:

function f(n::Int64)
    # The rand() function returns uniform random numbers using
    # the Mersenne-Twister algorithm

    # Get n random chord angles
    x = 2π*rand(n)

    # Get n random rotations
    y = acos(rand(n))

    # Bind into a 2D array
    hcat(x+y, x-y)
end

Мне очень нравится, как выглядят визуализации, поэтому я включу одну. Это результат f(1000).

Круг


5

Pyth, 22 байта

Порт ответа C ++. У меня было еще одно 23-байтовое решение (теперь 22!), Но это была почти копия точного ответа @ kirbyfan64sos с оптимизацией, поэтому мне пришлось немного нестандартно подумать и творчески (ab) использовать оператор свертывания.

m,-Fdsdm,y*.nZOZ.tOZ4Q

Обратите внимание, что это не работает сейчас из-за ошибки в операторе сгиба после введения reduce2. Я помещаю запрос на получение.

m             Map    
 ,            Tuple of
  -Fd         Fold subtraction on input
  sd          Fold addition on input
 m      Q     Map over range input
  ,           Tuple           
   y          Double
    *         Product
     .nZ      Pi
     OZ       [0, 1) RNG
  .t  4       Acos
    OZ        [0, 1) RNG

Для справки это было мое другое решение, которое работает таким же образом: VQKy*.nZOZJ.tOZ4,+KJ-KJ


Вы неправильно написали мое имя пользователя ... :(
kirbyfan64sos

@ kirbyfan64sos сумасшедший. Извините;)
Maltysen

4

IDL, 65 байт

Очевидно, это тот же алгоритм, что и у @rcrmn, хотя я вывел его независимо, прежде чем читать их ответ.

read,n
print,[2,2]#randomu(x,n)*!pi+[-1,1]#acos(randomu(x,n))
end

Функция randomu в IDL использует Mersenne Twister с периодом 2 19937 с -1.

РЕДАКТИРОВАТЬ: я провел 1000 аккордов через визуализатор выше, вот скриншот результата:

IDL бертран


4

C ++ 11, 214 байтов

#include<random>
#include<iostream>
#include<cmath>
int main(){using namespace std;int n;cin>>n;random_device r;uniform_real_distribution<> d;for(;n;--n){float x=2*M_PI*d(r),y=acos(d(r));cout<<x+y<<' '<<x-y<<';';}}

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

Объяснение:

#include<random>
#include<iostream>
#include<cmath>
int main()
{
    using namespace std;
    int n;
    cin>>n; // Input number
    random_device r; // Get a random number generator
    uniform_real_distribution<> d;   // Get a uniform distribution of 
                                     // floats between 0 and 1
    for(;n;--n)
    {
        float x = 2*M_PI*d(r),       // x: Chosen radius angle
              y = acos(d(r));        // y: Take the distance from the center and 
                                     // apply it an inverse cosine, to get the rotation

        cout<<x+y<<' '<<x-y<<';';    // Print the two numbers: they are the rotation
                                     // of the radius +/- the rotation extracted from
                                     // the distance to the center
    }
}

1
Этот фактор M_PI_2выглядит подозрительно. Я думаю, что это должно быть 1 вместо.
Анатолий

Да, совершенно верно, собираюсь исправить это сейчас! Большое спасибо!
rorlork

4

APL, 46 байт

f←{U←⍵ 2⍴∊{(○2×?0)(¯1○?0)}¨⍳⍵⋄⍉2⍵⍴∊(+/U)(-/U)}

Моя первая программа APL! Конечно, это может быть значительно улучшено (поскольку мое общее понимание APL отсутствует), поэтому любые предложения будут фантастическими. Это создает функцию, fкоторая принимает целое число в качестве входных данных, вычисляет пары точек хорды, используя метод 2, и печатает каждую пару, разделенную новой строкой.

Вы можете попробовать это онлайн !

Объяснение:

f←{ ⍝ Create the function f which takes an argument ⍵

    ⍝ Define U to be an ⍵ x 2 array of pairs, where the first
    ⍝ item is 2 times a random uniform float (?0) times pi (○)
    ⍝ and the second is the arccosine (¯1○) of another random
    ⍝ uniform float.

    U ← ⍵ 2 ⍴ ∊{(○2×?0)(¯1○?0)}¨⍳⍵

    ⍝ Create a 2 x ⍵ array filled with U[;1]+U[;2] (+/U) and
    ⍝ U[;1]-U[;2] (-/U). Transpose it into an ⍵ x 2 array of
    ⍝ chord point pairs and return it.

    ⍉ 2 ⍵ ⍴ ∊(+/U)(-/U)
}

Примечание: мое предыдущее 19-байтовое решение было недопустимым, поскольку оно вернуло (x, y), а не (x + y, xy). Грусть изобилует.


3

Java, 114 байт

n->{for(;n-->0;){double a=2*Math.PI*Math.random(),b=Math.acos(Math.random());System.out.println(a+b+" "+(a-b));}};

Базовая реализация в Java. Используйте в качестве лямбда-выражения.

Пример использования


Вы не можете уменьшить размер, храня Mathгде-нибудь? Или что-то? (Я не программист на Java)
Исмаэль Мигель

@IsmaelMiguel Это будет стоить дополнительно 2 символа.
TheNumberOne

Извините: / Это заманчиво, чтобы попытаться уменьшить количество раз, которое Mathпоказывает. Что говорит мета об использовании кода для генерации другого кода для решения проблемы?
Исмаэль Мигель

2
@IsmaelMiguel Это честная игра, хотя я буду удивлен, если ты действительно лучше в метагольфинге, чем в гольфе.
Мартин Эндер

3

Рубин, 72 байта

Мой первый гольф здесь! Я использовал тот же код, что и все, я надеюсь, что все в порядке

gets.chomp.to_i.times{puts"#{x=2*Math::PI*rand},#{x+2*Math.acos(rand)}"}

2

Ява, 115 123

Это в основном то же самое, что и большинство других, но мне нужна оценка Java для этой дыры, так что вот так:

void i(int n){for(double x;n-->0;System.out.println(x+2*Math.acos(Math.random())+" "+x))x=2*Math.PI*Math.random();}

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

8.147304676211474 3.772704020731153
8.201346559916786 3.4066194978900106
4.655131524088468 2.887965593766409
4.710707820868578 3.8493686706403984
3.3839198612642423 1.1604092552846672

1

CJam, 24 22 байта

Подобно другим алгоритмам, здесь есть версия в CJam.

{2P*mr_1dmrmC_++]p}ri*

Ввод 1000 производит распределение как:

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

Как это устроено

Алгоритм просто x = 2 * Pi * rand(); print [x, x + 2 * acos(rand())]

{                 }ri*        e# Run this loop int(input) times
 2P*mr                        e# x := 2 * Pi * rand()
      _                       e# copy x
       1dmr                   e# y := rand()
           mC                 e# z := acos(y)
             _++              e# o := x + z + z
                ]             e# Wrap x and o in an array
                 p            e# Print the array to STDOUT on a new line

Обновление : 2 байта сохранены благодаря Мартину!

Попробуй здесь


1

Python 3, 144 117 байт

(спасибо Blckknght за lambdaуказатель)

Используя тот же метод, что и другие:

import math as m;from random import random as r;f=lambda n:[(x,x+2*m.acos(r()))for x in(2*m.pi*r()for _ in range(n))]

Из документации Python:

Python использует Mersenne Twister в качестве генератора ядра. Он производит 53-битные точности и имеет период 2 19937 с -1.

Выход

>>> f(10)
[(4.8142617617843415, 0.3926824824852387), (3.713855302706769, 1.4014527571152318), (3.0705105305032188, 0.7693910749957577), (1.3583477245841715, 0.9120275474824304), (3.8977143863671646, 1.3309852045392736), (0.9047010644291349, 0.6884780437147916), (3.333698164797664, 1.116653229885653), (3.0027328050516493, 0.6695430795843016), (5.258167740541786, 1.1524381034989306), (4.86435124286598, 1.5676690324824722)]

И так далее.

Визуализация

визуализация


Вы можете сэкономить около 20 байт, если вы используете лямбду для функции и возвращаете понимание списка (с внутренним выражением генератора):f=lambda n:[(x,x+2*m.acos(r()))for x in(2*m.pi*r()for _ in range(n))]
Blckknght

Ах, у меня изначально была лямбда. Думаю, я не думал удваивать списки. Благодарность! @Blckknght
Зак Гейтс

Может быть сокращено до 109 байт путем махинаций с импортом: tio.run/#python2
Triggernometry


1

R 60 56 53 49 байт

Дополнительные 4 байта благодаря @JayCe и превращению его в функцию.

Используя ту же базовую формулу, что и остальные. R по умолчанию использует метод Мерсенна-Твистера, но можно установить и другие. Выводит разделенный пробелами список.

function(n,y=acos(runif(n)))runif(n)*2*pi+c(y,-y)

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


Привет, Микки, ты можешь сохранить 4 байта , сделав это функцией, а не определив x.
JayCe

@ JayCe, это намного лучше, спасибо
MickyT

0

SmileBASIC, 62 байта

INPUT N
FOR I=1TO N
X=PI()*2*RNDF()Y=ACOS(RNDF())?X+Y,X-Y
NEXT
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.