Не ваша обычная бобовая машина

42

Рассмотрим ASCII версию механизма , сходного с бобовой машины или plinko / пачинко игры:

    O

    ^
   \ ^
  ^ ^ \
 \ ^ / ^
U U U U U
1 2 3 4 5

Это Oшар, который падает вниз.

  • Когда он поражает ^, есть 50-50 шансов, что он пойдет влево или вправо.
  • Когда он попадает в /, он всегда идет влево.
  • Когда он попадает \, он всегда идет правильно.

Мяч в конце концов попадает в одну из пронумерованных Uвпадин в нижней части. Вопрос в том, какова вероятность того, что он окажется в каждой впадине?

Для этого конкретного случая, вероятности являются 0.0, 0.1875, 0.5625, 0.125и 0.125, по желобам с 1 по 5 соответственно.

Вот еще один пример с 3 впадинами вместо 5. Вероятности являются 0.5, 0.5, и 0.0:

  O

  /
 ^ ^
U U U
1 2 3

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

Вызов

Напишите программу или функцию, которая принимает ASCII-представление структуры пирамиды механизма. (Ввод через стандартный ввод / командную строку / аргумент функции.)

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

   ^
  \ ^
 ^ ^ \
\ ^ / ^

Или вы можете предположить, что он вводится без пробелов, например

^
\^
^^\
\^/^

(При желании вы можете предположить, что есть завершающий символ новой строки и / или некоторый непротиворечивый шаблон пробелов.)

Структура входной пирамиды может иметь любое количество уровней (или линий), включая ноль. Каждый уровень имеет еще один ^, /или \чем последний, и есть levels + 1впадины на день (которые не являются частью ввода).

Ваша программа / функция должна напечатать / вернуть список вероятностей того, что мяч приземлится в каждой впадине (в порядке от самой левой впадины к самой правой впадине). Это должны быть значения с плавающей запятой, которые при печати имеют по крайней мере 3 десятичных знака (лишние нули или десятичные точки не требуются; 1подходит для 1.000, .5подходит для 0.500и т. Д.). Если вы написали функцию, вы можете напечатать значения или вернуть список / массив с плавающей точкой.

Любой разумный формат печатного списка в порядке. например 0.5 0.5 0.0, [0.5 0.5 0.0], [0.5, 0.5, 0.0], {0.5, 0.5, 0.0}или 0.5\n0.5\n0.0все будет в порядке.

Примеры

0 уровней: (сводится к одному тривиальному U)

Вход: [no input/empty string given]
Выход:1.0

1 уровень:

Вход: ^
Выход:0.5 0.5

Вход: /
Выход:1.0 0.0

Вход: \
Выход:0.0 1.0

2 уровня: (второй пример выше)

Входные данные:

 /
^ ^

Выход: 0.5 0.5 0.0

3 уровня:

Входные данные:

  ^
 ^ ^
^ ^ ^

Выход: 0.125 0.375 0.375 0.125

Входные данные:

  \
 / \
/ / \

Выход: 0.0 0.0 0.0 1.0

4 уровня: (первый пример выше)

Входные данные:

   ^
  \ ^
 ^ ^ \
\ ^ / ^

Выход: 0.0 0.1875 0.5625 0.125 0.125

7 уровней:

Входные данные:

      ^
     / ^
    ^ ^ /
   / \ / \
  ^ ^ / ^ \
 ^ \ ^ \ / ^
\ ^ ^ ^ \ ^ /

Выход: 0.0 0.09375 0.28125 0.4375 0.1875 0.0 0.0 0.0

счет

Самый короткий ответ в байтах побеждает. Tiebreaker - более ранний пост.

Кальвин Хобби
источник
16
Quincunx было лучше ответить на этот вопрос .
Увлечения Кэлвина
«какой-то непротиворечивый шаблон конечных пробелов» - могу ли я предположить, что конечные пробелы на N-й строке кодируют вероятность попадания мяча в N-ную корзину?
Random832
4
@ Random832 Нет. (Ты действительно думаешь, что полетел бы?)
Увлечения Кэлвина
Есть причина, по которой я разместил это как комментарий, а не как ответ. Я просто подумал, что было интересно, насколько разрешительной была эта загадка для ввода - большинство из того, что я видел, более строго определяют формат ввода и / или вывода.
Random832
@ Random832: вход и выход очень однозначны. Стандартные лазейки (например, кодирование ответа во входных данных) не нужно решать в каждой задаче.
Алекс А.

Ответы:

10

CJam, 50 48 45 44 42 40 байт

1]q{iD%"(+0 0+( (\Y/::+ (d2/_a+"S/=~+}/p

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

^
\^
^^\
\^/^

[0 0,1875 0,5625 0,125 0,125]

Алгоритм

Основная идея заключается в том, что вы продолжаете анализировать каждый символ (есть только 4 разных символа) и выполняете различные операции над распределением вероятности (первоначально массив, содержащий 1 элемент со значением 1). Для каждой строки входных символов (начиная с первого символа в первой строке) мы поддерживаем массив вероятностей того же размера. Каждый символ действует в соответствии с первой вероятностью из списка и выталкивает полученную пару в конец списка. После каждой строки мы суммируем пары из списка, чтобы получить точное количество элементов в качестве элементов на следующей строке.

Вот четыре символа и необходимые действия, соответствующие каждому:

  • ^: Когда появляется этот символ, вы разделяете текущую вероятность на две части. Например, если у нас есть это в первой строке, мы должны преобразовать [1]в[0.5 0.5]
  • /: Когда этот символ встречается, мы должны поместить <current probability> 0вместо текущей вероятности в массиве.
  • \: Когда этот символ встречается, мы должны поместить 0 <current probability>вместо текущей вероятности в массиве.
  • \n: Когда этот символ встречается, у нас есть новая строка. Таким образом, мы группируем все пары сверху из 3 символов и суммируем их, чтобы получить вероятность каждого элемента для следующей строки. Например [0 0.5 0.25 0.25]превращается в [0 0.75 0.25]. Обратите внимание, что первый и последний элементы имеют неявную пару (значение 0) до и после них.

Теперь нам остается только определить правильного персонажа и выполнить правильное действие. Для этого воспользуемся обычной математикой. В ASCII - коды ^, \, /и \nявляются 94, 92, 47, и 10. После нескольких испытаний мы получаем это простое уравнение для преобразования этих чисел в 0, 1, 2 и 3:

"^\/
":ied13f%ed4f%ed

дает:

Stack: [[94 92 47 10]]
Stack: [[3 1 8 10]]
Stack: [[3 1 0 2]]
3102

В массиве длиной 4 последний 4f%будет неявным. Таким образом, мы просто делаем %13ASCII-код символа и выбираем правильное действие из массива действий.

Объяснение кода :

1]                                 e# Initial probability array with 1 probability
  q{                          }/   e# Read the whole input and iterate char by char
    iD%                            e# mod the ASCII code of the character with 13
"(+0 0+( (\Y/::+ (d2/_a+"S/        e# This is our actions array in order of [\ / \n ^]
                           =~      e# We pick the correct action and eval it
                             +     e# Evaling each action will leave one number out of the
                                   e# pairs out of the array. So we put it back in
                                p  e# Print the final probability array

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

оптимизатор
источник
1
Как вы тестируете это с 0 строками?
Трихоплакс
1
@trichoplax должен работать сейчас.
Оптимизатор
Теперь он работает с пустым вводом :)
trichoplax
15

Рубин 140

->s{r=[1.0]
s.lines.map{|l|n=[i=0.0]*(r.size+1)
l.scan(/\S/).map{|e|a,b=e>?/?e>?]?[0.5]*2:[0,1]:[1,0]
z=r[i]
n[i]+=z*a
n[i+=1]+=z*b}
r=n}
r}

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

Проверьте это онлайн: http://ideone.com/kmsZMe

Довольно простая реализация. Вот это не одурачено:

F = -> input {
  probabilities = [1.0]

  input.lines.each { |line|

    new_probabilities = [0.0] * (probabilities.size+1)
    elements = line.scan /\S/
    elements.map.with_index{|el, i|
      deltas = el > '/' ? (el > ']' ? [0.5,0.5] : [0,1]) : [1,0]

      d1, d2 = deltas

      new_probabilities[i] += probabilities[i] * d1
      new_probabilities[i + 1] += probabilities[i] * d2
    }
    probabilities = new_probabilities
  }
  return probabilities
}
Кристиан Лупаску
источник
13

Рубин, 140 158 байт

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

Безымянная функция с одним аргументом. Не должно содержать пробелов. Может или не может содержать завершающий перевод строки.

->s{Z=(s.split'
')<<[]
K=[]
F=->i,j,f{k=Z[i][j]
K[i]||=0
k==?^?2.times{|m|F[i+1,j+m,f/2]}:!k ?K[j]+=f :F[i+1,j+(k==?/?0:1),f]}
F[0,0,1.0]
K}

Тратит 9 байтов на необходимость обработки 0 levels(пустая строка). Все тестовые примеры работают правильно, смотрите здесь на ideone .

blutorange
источник
4
Тот факт, что есть «лучший» ответ Ruby, не означает, что ваш (или любой другой ответ Ruby) не стоит голосов. :)
Алекс А.
Я проголосовал за это сам, потому что он содержит некоторые хорошие трюки. Мне нравится твоя многострочная split, например.
Кристиан Лупаску
12

Pyth, 43 42 41 байт

umsdc+0sm@c[ZJhkJZKcJ2K)2x"\/"ekC,GH2.z]1

Это предполагает, что ввод будет без пробелов. Попробуйте онлайн: Pyth Compiler / Executor

Pyth, 40 байт (сомнительно)

umsdc+0sm,K@[ZJhkcJ2)x"\/"ek-JKC,GH2.z]1

Спасибо @isaacg за сохранение одного байта. Обратите внимание, что эта версия на самом деле не работала в версии Pyth, когда был задан вопрос. В компиляторе была небольшая ошибка. Несмотря на то, что в этом коде не используются новые функции Pyth (только то, что долгое время было в документации Pyth и должно было работать), это может быть неверным ответом. Решай сам.

Попробуйте онлайн: Pyth Compiler / Executor

Объяснение:

umsdc+0sm,K@[ZJhkcJ2)x"\/"ek-JKC,GH2.z]1   
u                                   .z]1  reduce G, starting with G = [1], for H in all_input():
                               C,GH         zip(G,H)
        m                                   map each pair k to:
            [ZJhkcJ2)                         create a list [0, k[0], k[0]/2]
                     x"\/"ek                  index of k[1] in "\/" (-1 for "^")
          K@                                  take the correspondent element of the list and store in K
         ,                  -JK               create a pair (K, k[0]-K)                                                      
     +0s                                    sum and insert 0 at the begin
    c                              2        chop into pairs
 msd                                        sum up each pair
                                            G gets updated with this new list

Например, если у меня есть входные вероятности G = [0.5, 0.5, 0.0]и строка, H = "^/^"происходит следующее:

  • почтовый индекс ... [(0.5,"^"), (0.5,"/"), (0.0,"^")]
  • создать выходные вероятности ... [[0.25,0.25], [0.5,0.0], [0.0, 0.0]]
  • 0 + сумма ... [0, 0.25, 0.25, 0.5, 0.0, 0.0, 0.0]
  • чоп ... [0,0.25], [0.25,0.5], [0.0,0.0], [0.0]]
  • сумма ... [0.25, 0.75, 0.0, 0.0]
Jakube
источник
3
Я пытался сыграть в гольф, и это привело меня к ошибке в компиляторе. Гольф работает, теперь, когда я исправил ошибку. Задача состоит в том, чтобы заменить список списков с двумя значениями одним списком, а затем сгенерировать другое значение, вычитая первое значение из hk. ,K@[ZJhkcJ2)x"\/"ek-JK
Исаак
1
Это действительно тонкая грань, когда исправление ошибки не считается более новой версией языка (и, следовательно, остается в силе для вопросов, заданных до исправления)
Оптимизатор
1
@ Оптимизатор Ваше право. К ответу добавлены примечания, объясняющие обстоятельства.
Якуб
2
@Optimizer В этом случае кажется хорошим эмпирическим правилом, является ли это действительно более новой версией языка или более новой версией языковой реализации, которая исправляет ошибку, приближая реализацию в соответствие со спецификацией.
Дести
@Desty Вот почему я упоминал, что это тонкая грань;)
Оптимизатор
8

C #, 274 247 байт

Ничего сложного, полная программа, которая считывает строки (с пробелами или без них, просто удаляет их) из STDIN и печатает результаты, разделенные пробелами, в STDOUT.

using Q=System.Console;class P{static void Main(){decimal[]k={1},t;string L;for(int l=0,i;(L=Q.ReadLine())!=null;k=t)for(L=L.Replace(" ",""),t=new decimal[++l+1],i=0;i<l;)t[i]+=k[i]-(t[i+1]=(8-L[i]%8)/2*k[i++]/2);Q.WriteLine(string.Join(" ",k));}}

Код Tidier с комментариями:

using Q=System.Console;

class P
{
    // / 47
    // \ 92
    // ^ 94

    static void Main()
    {
        decimal[]k={1},t; // k is old array, t is new array

        string L; // L is the current line, R is the current result (1 if no rows)
        for(int l=0,i; // l is length of old array, i is index in old array
            (L=Q.ReadLine())!=null; // for each line of input
            k=t) // swap array over
            for(L=L.Replace(" ",""), // remove spaces
                t=new decimal[++l+1], // create a new array
                i=0;

                i<l;) // for each position

                t[i]+=k[i]-( // add to left position (for last time)
                        t[i+1]=(8-L[i]%8)/2*k[i++]/2 // assign right position (k is decimal)
                    );

        Q.WriteLine(string.Join(" ",k)); // print result
    }
}
VisualMelon
источник
7

Python 3, 113

P=[1]
for C in input().split():
 l,*Q=0,
 for p,c in zip(P,C):r=p*"\^/".find(c)/2;Q+=l+r,;l=p-r
 P=Q+[l]
print(P)

Повторно обновляет вектор вероятности Pв ответ на каждую строку. Этот новый вектор вероятности Qсоздается по одной записи за раз. Выполняет итерацию по новым слотам и вычисляет вклад от колышка справа от него r, а также вычисляет оставшийся вклад в предстоящий слот как p-r.

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

XNOR
источник
Ввод будет состоять из нескольких строк, поэтому я не думаю, что один input()может справиться с этим.
Рандомра
завершающий символ новой строки не требуется для каждой строки ввода.
Брайан Минтон
@randomra Я написал это на холостом ходу, и он отлично работал, вставляя многострочные входные данные в приглашение оболочки.
xnor
6

Python 3, 138 байт

def f(s):
 r=[1];p=t=0
 for e in s:
  if'!'<e:b=p==t*-~t/2;r+=[0]*b;t+=b;v=ord(e)%7+1;a=r[p]/2;r[-1]+=v//3*a;r+=v%3*a,;p+=1
 return r[~t:]

Работает с любыми пробелами, поскольку они все отфильтрованы (по if'!'<e).

Метод:

  • Мы ведем постоянно расширяющийся список rвероятностей достижения каких-либо препятствий и скрытых впадин под ними. Начнем с списка [1].
  • Если мы находимся на первом препятствии в ряду, нам нужно добавить дополнительный 0в список для ведущей впадины. Мы решаем, является ли это первым препятствием, сравнивая его индекс pсо следующим треугольным числом t*-~t/2.
  • Для каждого препятствия мы добавляем его значение списка частично к последнему элементу и частично к новому конечному элементу. Мы делим список значений на основе символа препятствия ( ^:0.5 0.5; /:1 0; \:0 1). Мы используем следующий метод:
    • Возьми v = ord(char) mod 7 + 1уступки^:4 /:6 \:2
    • v div 3 / 2дает первую дробь ( ^:0.5 /:1 \:0)
    • v mod 3 / 2дает вторую фракцию ( ^:0.5 /:0 \:1)
  • Результатом являются последние t + 1элементы окончательного списка r.

2 байта благодаря чату @ Sp3000.

randomra
источник
4

Perl, 78

#!perl -p0a
@r=($/=1);$^=.5;@r=map$r-$l+($l=$$_*($r=shift@r)),/./g,$r=$l=0for@F;$_="@r"

Принимает ввод без пробелов.

Попробуй меня .

nutki
источник
1

TI-BASIC, 73 76

Принимает ввод по одной строке за раз и заканчивается, когда пробел вводится сам по себе, потому что ни разрывы строк в строках, ни пустые строки недопустимы в TI-BASIC.

{1→X
Input Str1
While Str1≠"  //one space
.5seq(inString("^/",sub(Str1,I,1)),I,1,dim(Ans
augment(Ans∟X,{0})+augment({0},∟X-∟XAns→X
Input Str1
End
∟X

Я почти уверен, что получил правильный размер (TI-BASIC маркирован, поэтому каждая команда занимает один или два байта - seq () принимает один, inString () принимает два, dim () принимает один и т. Д. I посчитал размер вручную.)

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

lirtosiast
источник
0

Javascript - 117

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

Шляпа к xnor за идею вычитания, которая сбрила дюжину и более символов.

w=s=>{a=[1];s.split('\n').map(m=>{for(b=[i=0];z=a[i],f=m[i];b[i++]+=z-b[i])b[i+1]=f>']'?z/2:f<':'?0:z;a=b})
return a}

Ungolfed:

// s must not have spaces
w=s=>{
  // a is the current probability array
  a=[1];
  s.split('\n').map(
    // for each row of input...
    m=>{
      b=[0];  // b is the next row's probability array
      for(i=0; i<m.length;){
        z=a[i]; // z = probability
        f=m[i]; // f = letter
                                  // let's assume i == 0
        b[i+1] = (f>']') ? z/2 :  // if f == '^', b[1] = z/2
          (f<':' ? 0 :            // else if f == '/', b[1] = 0 
            z);                   // else b[1] = z
        b[i++]+=z-b[i];           // then add (z-b[1]) to b[0]
      }
      a=z-b    // increment current probability array
    }
  )
  return a;
}
Не тот Чарльз
источник