Точно рассчитать вероятность

9

Эта задача о написании кода для точного вычисления вероятности. Вывод должен быть точной вероятностью, записанной в виде дроби в наиболее сокращенной форме. То есть это никогда не должно выводиться, 4/8а скорее 1/2.

Для некоторого положительного целого числа nрассмотрим равномерно случайную строку длиной 1 с и 1 с nи назовем ее A. Теперь объединяем Aее первое значение. То есть A[1] = A[n+1]если индексирование от 1. Aтеперь имеет длину n+1. Теперь также рассмотрим вторую случайную строку длины n, первые nзначения которой равны -1, 0 или 1 с вероятностью 1 / 4,1 / 2, 1/4 каждая и назовем ее B.

Например, рассмотрим n=3. Возможные значения Aи Bмогут быть A = [-1,1,1,-1]и B=[0,1,-1]. В этом случае два внутренних продукта 0и 2.

Теперь рассмотрим внутреннее произведение A[1,...,n]и Bи и внутреннее произведение A[2,...,n+1]и B.

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

Для n=1этой вероятности это ясно 1/2.

Я не против, как nуказано в коде, но это должно быть очень просто и понятно, как это изменить.

Языки и библиотеки

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

Мартин Эндер
источник
2
Тестовые случаи для первых нескольких nбыли бы полезны. Также может быть полезен явный пример A, B и двух внутренних продуктов.
Мартин Эндер
Если мы решим кодировать целое число, будет ли n=4считаться ноль, два или три байта? Должен ли вывод быть точным a/b или будет [a b], например, разрешен?
Деннис
@ Денис Это должно быть точно. Если вы жестко закодируете целое число, мне нужно будет изменить его только в одном месте n? В противном случае, я думаю, что это не разрешено.
Да, моя программа использует целое число только один раз для вычисления декартовой степени. Все остальное получено из результирующего массива.
Деннис

Ответы:

7

Pyth, 48 47 46 44 байта

K,smlf!|Fms*Vd.>Tk2^,1_1Q^+0tM3Q^8Qj\//RiFKK

Попробуйте онлайн: Демонстрация

Онлайн-версия, вероятно, не рассчитывает n=6. На моем ноутбуке (автономная версия) это занимает около 45 секунд.

Метод грубой силы.

Объяснение:

smlf!|Fms*Vd.>Tk2^,1_1Q^+0tM3Q   implicit: Q = input number
                          tM3    the list [-1, 0, 1]
                        +0       add zero, results in [0, -1, 0, 1]
                       ^     Q   all possible lists of length Q using these elements
 m                               map each list d (B in Lembik's notation) to:
                  ,1_1              the list [1, -1]
                 ^    Q             all possible lists of length Q
   f                                filter for lists T (A in Lembik's notation),
                                    which satisfy:
       m        2                      map each k in [0, 1] to:
        s*Vd.>Tk                          scalar-product d*(shifted T by k)
    !|F                                not or (True if both scalar-products are 0)      
  l                                 determine the length                
s                                add all possibilities at the end

K,...^8QQj\//RiFKK   
 ,...^8Q             the list [result of above, 8^Q]
K                    store it in K
              iFK    determine the gcd of the numbers in K
            /R   K   divide the numbers in K by the gcd
         j\/         join the two numbers by "/" and print
Jakube
источник
черт, забыл про gcd, знал, что кое-что я пропустил
Maltysen
+0r1_2короче чем /R2r2_2.
Исаак
Я думаю, чтобы быть справедливым, это должна быть версия 89/512, которую вы считаете.
@Lembik Хорошо. Изменено.
Якубе
Я должен признать, мне никогда не приходило в голову, что это может быть сделано в 47 символов!
8

Mathematica, 159 100 87 86 85 байт

n=3;1-Mean@Sign[##&@@Norm/@({1,0,0,-1}~t~n.Partition[#,2,1,1])&/@{1,-1}~(t=Tuples)~n]

Для изменения nпросто измените определение переменной в начале.

Так как это грубая сила, это довольно медленно, но вот первые восемь результатов:

n   P(n)
1   1/2
2   3/8
3   7/32
4   89/512
5   269/2048
6   903/8192
7   3035/32768
8   169801/2097152

Последний уже занял 231 секунду, и время выполнения ужасно экспоненциально.

объяснение

Как я уже сказал, это грубая сила. По сути, я просто перечисляю все возможные Aи Bвычисляю два точечных произведения для каждой возможной пары, а затем нахожу полученную долю пар {0, 0}. Комбинаторика Mathematica и функции линейной алгебры очень помогли в игре в гольф:

{1,-1}~(t=Tuples)~n

Это создает все n-кортежи, содержащие 1или -1, то есть все возможные A. Для n = 3этого есть:

{{1, 1, 1}, 
 {1, 1, -1}, 
 {1, -1, 1}, 
 {1, -1, -1}, 
 {-1, 1, 1}, 
 {-1, 1, -1}, 
 {-1, -1, 1}, 
 {-1, -1, -1}}

Для вычисления Bмы делаем почти то же самое:

{1,0,0,-1}~t~n

Повторяя 0, мы дублируем каждый кортеж для каждого, который 0он содержит, тем самым делая в 0два раза больше вероятности, чем 1или -1. Снова используя n = 3в качестве примера:

{{-1, -1, -1},
 {-1, -1, 0}, {-1, -1, 0},
 {-1, -1, 1},
 {-1, 0, -1}, {-1, 0, -1},
 {-1, 0, 0}, {-1, 0, 0}, {-1, 0, 0}, {-1, 0, 0},
 {-1, 0, 1}, {-1, 0, 1},
 {-1, 1, -1},
 {-1, 1, 0}, {-1, 1, 0},
 {-1, 1, 1},
 {0, -1, -1}, {0, -1, -1},
 {0, -1, 0}, {0, -1, 0}, {0, -1, 0}, {0, -1, 0},
 {0, -1, 1}, {0, -1, 1},
 {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
 {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0},
 {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1},
 {0, 1, -1}, {0, 1, -1},
 {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0},
 {0, 1, 1}, {0, 1, 1},
 {1, -1, -1},
 {1, -1, 0}, {1, -1, 0},
 {1, -1, 1},
 {1, 0, -1}, {1, 0, -1},
 {1, 0, 0}, {1, 0, 0}, {1, 0, 0}, {1, 0, 0},
 {1, 0, 1}, {1, 0, 1},
 {1, 1, -1},
 {1, 1, 0}, {1, 1, 0},
 {1, 1, 1}}

Теперь, для каждого возможного A, мы хотим получить точечное произведение каждого из этих возможных B, как с, так A[1 .. n]и A[2 .. n+1]. Например , если наш текущий Aявляется {1, 1, -1}, мы хотим , чтобы скалярное произведение с обоими {1, 1, -1}и с {1, -1, 1}. Поскольку все наши Bуже удобно являются строками матрицы, мы хотим, чтобы эти два подсписка были Aстолбцами другой матрицы, чтобы мы могли вычислить простое произведение точек между ними. Но транспонирование {{1, 1, -1}, {1, -1, 1}}просто дает, {{1, 1}, {1, -1}, {-1, 1}}который является просто списком всех 2-элементных циклических подсписков A. Вот что это делает:

Partition[#,2,1,1]

Итак, мы вычислим это и возьмем скалярное произведение с нашим списком B. Так как теперь мы получаем вложенный список (поскольку каждый возможный Aдает отдельный вектор), мы выравниваем их с помощью ##&@@.

Чтобы узнать, есть ли пара {x, y}, {0, 0}мы вычисляем, Sign[Norm[{x,y}]] где Normдает √(x²+y²). Это дает 0или 1.

Наконец, поскольку теперь мы просто хотим знать доли 1s в списке 0s и 1s, все, что нам нужно, это среднее арифметическое этого списка. Однако это дает вероятность того, что оба, по крайней мере, одного точечного произведения отличны от нуля, поэтому мы вычтем его, 1чтобы получить желаемый результат.

Мартин Эндер
источник
6

Pyth - 65 55 байт

Исправлена ​​ошибка с уменьшением дроби при стоимости одного байта.

Использует метод грубой силы, и может быть в гольфе, но просто хотел что-то получить. Очень медленно

*F-KP/Jmms*Vked,thdPhd*makhk^,1_1Q^[1ZZ_1)Q,ZZ2/lJ^2/K2

Он использует декартовы произведения для генерации обоих Aи B, делая переменные вероятности, 0появляется дважды в списке источников, а затем подсчитывает те, которые являются внутренним произведением, до нуля. Внутренний продукт облегчается путем Vупотребления синтаксического сахара. Упрощение дроби пугало меня поначалу, но это было довольно легко с Pфункцией факторизации иней и пониманием того, что нам нужно уменьшить только на степени 2.

Попробуйте это онлайн здесь .

Maltysen
источник
Как я могу изменить n?
@Lembik Программа Pyth запрашивает пользовательский ввод, который указан во втором текстовом поле (если вы используете онлайн-компилятор).
Якуб
@Jakube О, спасибо! И это, похоже, тоже работает :)
6

CJam, 58 57 54 51 46 байт

WX]m*Zm*_{~.+2,@fm<\f.*::+0-!},,__~)&:T/'/@,T/

Чтобы запустить его, вставьте желаемое целое число между WX]и m*.

Спасибо @ jimmy23013 за немного волшебства и за гольф в 5 байтах!

Попробуйте онлайн в интерпретаторе CJam .

идея

Большинство частей этого ответа просты, но в нем используются два хитрых трюка:

  • Вместо того чтобы связывать все векторы {-1, 1} n со всеми векторами {-1, 0, 1} n с желаемыми вероятностями, он считает количество триплетов векторов в {-1, 1} n, которые удовлетворяют определенное условие.

    Если мы добавим два последних вектора триплета, результатом будет вектор {-2, 0, 2} n .

    Поскольку (-1) + 1 = 0 = 1 + (-1) , 0 с будет происходить вдвое чаще, чем -2 с и 2 с.

    Разделив каждый компонент на 2 , мы получим вектор {-1, 0, 1} n с желаемыми вероятностями.

    Поскольку нас интересует только скалярное произведение 0 или нет, мы можем пропустить деление на 2 .

  • После подсчета всех триплетов, удовлетворяющих условию вопроса и общего количества триплетов, мы должны уменьшить полученную дробь.

    Вместо вычисления GCD обоих чисел, поскольку знаменатель всегда будет степенью 2, достаточно разделить оба числа на наибольшую степень 2, которая делит числитель.

    Чтобы получить наибольшую степень 2, которая делит x , мы можем взять побитовое И для x и ~ x + 1 .

    ~ x инвертирует все биты x , поэтому все завершающие 0 с становятся 1 с. При добавлении 1 к ~ x эти 1 с превратятся в 0 с, а последние 1 в ~ x + 1 будут соответствовать последним 1 в x .

    Все остальные биты либо оба равны 0 , поэтому побитовое И возвращает целое число, состоящее из последней 1 из x и всех последующих 0 . Это высшая степень 2, которая делит х .

Код

WX]    e# Push the array [-1 1].
       e# Insert N here.
m*     e# Cartesian product: Push the array of all vectors of {-1,1}^N.
Zm*    e# Cartesian product: Push the array of all triplets of these vectors.
_      e# Copy the array.
{      e# Filter; for each triplet of vectors U, V and W in {-1,1}^N:
  ~    e#   Dump U, V and W on the stack.
  .+   e#   Compute X := V + W, a vector of {-2,0,2}^N, where each component is
       e#   zero with probability 1/2.
  2,@  e#   Push [0 1]. Rotate U on top of it.
  fm<  e#   Push [U U'], where U' is U rotated one dimension to the left.
  \f.* e#   Push [U*X and U'*X], where * denotes the vectorized product.
  ::+  e#   Add the components of both products.
  0-   e#   Remove zeroes.
       e#   Push the logical NOT of the array.
},     e#   If the array was empty, keep the triplet.
,      e# Push X, the length of the filtered array.
__~)&  e# Push X & ~X + 1.
:T     e# Save the result in T and divide X by T.
'/     e# Push a slash.
@,T/   e# Dividet he length of the unfiltered array by T.
Деннис
источник
WX]m*Zm*_{~.+2,@fm<\f.*::+0-!},,__W*&:T/'/@,T/,
jimmy23013
@ jimmy23013: Это немного волшебства. Спасибо!
Деннис