Вероятность рисования данного слова из пакета букв в скрэббл


18

Предположим, у вас была сумка с плитками, на каждой из которых была буква. Есть тайлы с буквой 'A', с 'B' и т. Д., И плитки с подстановочными знаками (у нас есть ). Предположим, у вас был словарь с конечным числом слов.NNANВN*Nзнак равноNA+NВ+...+NZ+N*

Вы выбираете плиток из сумки без замены.К

Как бы вы вычислили (или оценили) вероятность того, что вы можете сформировать данное слово длиной (с 1 < = < ) из словаря, учитывая выбранных плиток?LLКК

Для тех, кто не знаком с Scrabble (TM), подстановочный знак может использоваться для соответствия любой букве. Таким образом, слово «BOOT» может быть «написано» с плитками «B», «*», «O», «T». Порядок, в котором нарисованы буквы, не имеет значения.

Предложение: чтобы упростить написание ответов, может быть лучше просто ответить на вопрос: какова вероятность того, что слово «BOOT» будет среди ваших возможных ходов после того, как вы вытащили 7 букв из свежей сумки.

(введение проблемы было скопировано из этого похожего вопроса )


Я бы посоветовал сначала заняться более простым делом, например, без подстановочных знаков.
Glen_b

@Glen_b Я согласен. Поскольку моя последняя цель - упорядочить слова по вероятности, я думаю, что игнорирование подстановочных знаков является приемлемым приближением. Однако у меня все еще нет навыков, чтобы построить формулу для решения этой более простой задачи
Себастьян

1
Если вы хотите начать еще проще, рассчитайте вероятность выбора «B», затем «O», затем «O», затем «T». После этого рассчитайте вероятность подбора букв в любом порядке. После этого учтите, что у вас есть семь попыток. Затем введите подстановочные знаки.
Джерри Ширмер

1
Простым способом решения этой проблемы было бы использование приближения Монте-Карло. Будет ли этого достаточно?
Расмус Батх

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

Ответы:


12

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


Чтобы получить такую ​​формулу, разбейте возможности на взаимно непересекающиеся группы двумя способами: в зависимости от того, сколько букв в слове выбрано не в слове (пусть это будет ), и в зависимости от того, сколько подстановочных знаков (пробелов) выбрано ( пусть это будет ш ). Если в стойке имеется r = 7 плиток, N доступных плиток, M доступных плиток с буквами, не входящими в слово, и W = 2 доступных пробела, число возможных вариантов выбора, заданных ( m , w ), равномвесрзнак равно7NMWзнак равно2(м,вес)

(Mм)(Wвес)(N-M-Wр-м-вес)

потому что выбор несловесных букв, пробелов и словесных букв не зависит от (м,вес,р),

Это сводит задачу к нахождению числа способов пишутся словом при выборе только из плиток , представляющих буквы этого слова, учитывая , что имеются заготовки и т - м - ж плитки будут выбраны. Ситуация грязная, и нет закрытой формулы, кажется, доступны. Например, с w = 0 пробелами и m = 3 буквами вне слова остается ровно четыре буквы, оставляющие заклинание «boot», которые были нарисованы из тайлов «b», «o» и «t» , Учитывая, что есть 2 "b", 8 "o" и 6веср-м-весвесзнак равно0мзнак равно3286"t" в наборе плиток Scrabble, есть положительные вероятности рисования (мультимножества) "bboo", "bbot", "bbtt", "booo", "boot", "bot", "bttt", "oooo "," ooot "," oott "," ottt "и" tttt ", но только одно из этих заклинаний" boot ". И это был легкий случай! Например, если предположить, что стойка содержит пять плиток, выбранных случайным образом из плиток «o», «b» и «t», вместе с обоими пробелами, существует еще много способов написания слова «загрузочный», а не его написания. Например, «boot» может быть написано из «__boott» и «__bbttt», но не из «__ttttt».

Этот подсчет - суть проблемы - может быть обработан рекурсивно. Я опишу это на примере. Предположим, что мы хотим подсчитать способы написания слова «boot» с одной пустой и еще четырьмя плитками из коллекции плиток «b», «o» и «t» (откуда оставшиеся две плитки показывают непустые буквы не в { "б", "о", "т"}). Рассмотрим первую букву «б»:

  1. «B» можно нарисовать в доступны пути из двух «b» плиток. Это сводит проблему к подсчету количества способов написания суффикса "oot" с использованием обоих пробелов и еще трех плиток из коллекции плиток "o" и "t".(21)

  2. Один пробел может быть обозначен как «б». Это сводит проблему к подсчету количества способов написания «oot» с использованием оставшегося пробела и еще трех плиток из набора «o» и «t» плиток.

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

Вот Rкод для рекурсивного шага. rackобычно равно , представляет собой массив подсчетов букв (например, ), представляет собой аналогичную структуру, дающую количество доступных плиток с этими буквами, и представляет собой число пробелов, предположительно встречающихся в стойке7wordc(b=1, o=2, t=1)alphabetwild

f <- function(rack, word, alphabet, wild) {
  if (length(word) == 1) {
    return(ifelse(word > rack+wild, 0, choose(alphabet, rack)))
  }
  n <- word[1]
  if (n <= 0) return(0)
  m <- alphabet[1]
  x <- sapply(max(0, n-wild):min(m, rack), 
              function(i) {
                choose(m, i) * f(rack-i, word[-1], alphabet[-1], wild-max(0, n-i))
              })
  return(sum(x))
}

Интерфейс этой функции задает стандартные фрагменты Scrabble, преобразует данное слово в его структуру данных из нескольких множеств и выполняет двойное суммирование по и w . Вот где биномиальные коэффициенты ( Ммвес и ( ш(Mм) вычисляются и умножаются.(Wвес)

scrabble <- function(sword, n.wild=2, rack=7, 
              alphabet=c(a=9,b=2,c=2,d=4,e=12,f=2,g=3,h=2,i=9,j=1,k=1,l=4,m=2,
                         n=6,o=8,p=2,q=1,r=6,s=4,t=6,u=4,v=2,w=2,x=1,y=2,z=1),
              N=sum(alphabet)+n.wild) {
  word = sort(table(strsplit(sword, NULL))) # Sorting speeds things a little
  a <- sapply(names(word), function(s) alphabet[s])
  names(a) <- names(word)
  x <- sapply(0:n.wild, function(w) {
    sapply(sum(word):rack-w, 
           function(i) {
             f(i, word, a, wild=w) *
               choose(n.wild, w) * choose(N-n.wild-sum(a), rack-w-i)
           })
  })
  return(list(numerator = sum(x), denominator = choose(N, rack),
              value=sum(x) / choose(N, rack)))
}

Давайте попробуем это решение и оценим его по ходу. В следующем тесте используются те же входные данные, которые использовались при моделировании @Rasmus Bååth :

system.time(x <- sapply(c("boot", "red", "axe", "zoology"), scrabble))

Эта машина сообщает об общем прошедшем времени в секунды: достаточно быстро. Результаты?0,05

> x
            boot        red         axe         zoology     
numerator   114327888   1249373480  823897928   11840       
denominator 16007560800 16007560800 16007560800 16007560800 
value       0.007142118 0.07804896  0.0514693   7.396505e-07

Вероятность «загрузка» из точно равна значению +2381831 / +333490850 , полученную в моем другом ответе (который использует подобный метод , но кушетки его в более мощных рамках требующих символическую алгебры вычислительной платформу). Вероятности всех четырех слов достаточно близки к симуляции Баас (которые не могли бы ожидать , чтобы дать точное значение «зоология» в связи с его низкой вероятностью 11840 / 16007560800 , что меньше , чем один на миллион).114327888/160075608002381831/33349085011840/16007560800,


Классное и элегантное решение! И намного быстрее, чем у меня ... :)
Расмус Батх

1
Это отличный ответ, спасибо. Мне было бы сложно кодировать ваш алгоритм, поэтому готовый к использованию код приветствуется. Я не знал, Rно все же сумел использовать ваши функции менее чем за час работы, так что скрипт получает данные из файла словаря из 20 тысяч слов и записывает результаты в файл .csv. (это заняло менее 10 минут на среднем уровне Core i5)
Себастьян

16

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

Моделирование (показано в конце) поддерживает вычисленные ответы.


Детали

Как и в предыдущем ответе, Mathematica используется для выполнения расчетов.

  1. Укажите проблему: слово (или слова, если хотите), буквы, их количество и размер стойки. Поскольку все буквы, не входящие в это слово, действуют одинаково, это значительно ускоряет вычисления, заменяя их все одним символом представляющим «любую букву, не входящую в слово».χ

    word = {b, o, o, t};
    letters = {b, o, t, \[Chi], \[Psi]};
    tileCounts = {2, 8, 6, 82, 2};
    rack = 7;
  2. Создайте словарь этого слова (или слов) и дополните его, чтобы включить все возможные подстановочные символы.

    dict[words_, nWild_Integer] := Module[{wildcard, w},
       wildcard = {xx___, _, yy___} -> {xx, \[Psi], yy};
       w = Nest[Flatten[ReplaceList[#, wildcard] & /@ #, 1] &, words, nWild];
       Union[Times @@@ Join[w, Times @@@ words]]];
    dictionary = dict[{word}, 2]

    {bo2t,bo2ψ,botψ,o2tψ,boψ2,o2ψ2,btψ2,otψ2}

  3. Вычислить не слова:

    alphabet = Plus @@ letters;
    nonwords = Nest[PolynomialMod[# alphabet, dictionary] &, 1, rack]

    b7+7b6o+21b5o2++7χψ6+ψ7

    185

  4. Подсчитайте шансы. Для выборки с заменой просто замените количество плиток на переменные:

    chances = (Transpose[{letters, tileCounts/(Plus @@ tileCounts)}] /. {a_, b_} -> a -> b);
    q = nonwords /. chances;
    1 - q

    20726341339062500000

    0.00756036.

    Для выборки без замены используйте факторные полномочия вместо полномочий:

    multiplicities = MapThread[Rule, {letters, tileCounts}];
    chance[m_] :=  (ReplaceRepeated[m , Power[xx_, n_] -> FactorialPower[xx, n]] 
                   /. multiplicities);
    histor = chance /@ MonomialList[nonwords];
    q0 = Plus @@ histor  / FactorialPower[Total[tiles], nn];
    1 - q0

    2381831333490850

    0.00714212.


Результаты симуляции

106

simulation = RandomChoice[tiles -> letters, {10^6, 7}];
u = Tally[Times @@@ simulation];
(p = Total[Cases[Join[{PolynomialMod[u[[All, 1]], dictionary]}\[Transpose], 
       u, 2], {0, _, a_} :> a]] / Length[simulation] ) // N

0.007438

Сравните его с вычисленным значением относительно его стандартной ошибки:

(p - (1 - q)) / Sqrt[q (1 - q) / Length[simulation]] // N

1.41259

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

106

tilesAll = Flatten[MapThread[ConstantArray[#1, #2] &, {letters, tiles}] ]
    (p - (1 - q)) / Sqrt[q (1 - q) / Length[simulation]] // N;
simulation = Table[RandomSample[tilesAll, 7], {i, 1, 10^6}];
u = Tally[Times @@@ simulation];
(p0 = Total[Cases[Join[{PolynomialMod[u[[All, 1]], dictionary]}\[Transpose], 
       u, 2], {0, _, a_} :> a]] / Length[simulation] ) // N

0.00717

Сделайте сравнение:

(p0 - (1 - q0)) / Sqrt[q0 (1 - q0) / Length[simulation]] // N

0.331106

Согласие в этом моделировании было превосходным.

12


13

Так что это решение Монте-Карло , то есть мы собираемся смоделировать рисование тайлов миллион раз, а затем рассчитаем, сколько из этих симулированных розыгрышей привело к тому, что мы смогли сформировать данное слово. Я написал решение на R, но вы можете использовать любой другой язык программирования, например, Python или Ruby.

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

# The tile frequency used in English Scrabble, using "_" for blank.
tile_freq <- c(2, 9 ,2 ,2 ,4 ,12,2 ,3 ,2 ,9 ,1 ,1 ,4 ,2 ,6 ,8 ,2 ,1 ,6 ,4 ,6 ,4 ,2 ,2 ,1 ,2 ,1)
tile_names <- as.factor(c("_", letters))
tiles <- rep(tile_names, tile_freq)
## [1] _ _ a a a a a a a a a b b c c d d d d e e e e e e
## [26] e e e e e e f f g g g h h i i i i i i i i i j k l
## [51] l l l m m n n n n n n o o o o o o o o p p q r r r
## [76] r r r s s s s t t t t t t u u u u v v w w x y y z
## 27 Levels: _ a b c d e f g h i j k l m n o p q r ... z

Затем закодируйте слово как вектор букв.

word <- "boot"
# A vector of the counts of the letters in the word
word_vector <- table( factor(strsplit(word, "")[[1]], levels=tile_names))
## _ a b c d e f g h i j k l m n o p q r s t u v w x y z 
## 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 1 0 0 0 0 0 0 

Теперь нарисуйте образец из семи плиток и закодируйте их так же, как слово.

tile_sample <- table(sample(tiles, size=7))
## _ a b c d e f g h i j k l m n o p q r s t u v w x y z 
## 1 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 0 1 0 0 0 

Наконец, посчитайте, какие буквы отсутствуют ...

missing <- word_vector - tile_sample
missing <- ifelse(missing < 0, 0, missing)
## _ a b c d e f g h i j k l m n o p q r s t u v w x y z 
## 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 

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

sum(missing) - tile_sample["blank"] <= 0
## FALSE

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

word_prob <- function(word, reps = 50000) {
  tile_freq <- c(2, 9 ,2 ,2 ,4 ,12,2 ,3 ,2 ,9 ,1 ,1 ,4 ,2 ,6 ,8 ,2 ,1 ,6 ,4 ,6 ,4 ,2 ,2 ,1 ,2 ,1)
  tile_names <- as.factor(c("_", letters))
  tiles <- rep(tile_names, tile_freq)
  word_vector <- table( factor(strsplit(word, "")[[1]], levels=tile_names))
  successful_draws <- replicate(reps, {
    tile_sample <- table(sample(tiles, size=7))
    missing <- word_vector - tile_sample
    missing <- ifelse(missing < 0, 0, missing)
    sum(missing) - tile_sample["_"] <= 0
  })
  mean(successful_draws)
}

Вот repsколичество смоделированных розыгрышей. Теперь мы можем попробовать это на нескольких разных словах.

> word_prob("boot")
[1] 0.0072
> word_prob("red")
[1] 0.07716
> word_prob("axe")
[1] 0.05088
> word_prob("zoology")
[1] 2e-05

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

2
Я считаю, что sampleэто не так, как вы ожидаете. Например, что произойдет с вашим кодом, если в игру внесены изменения в стойку из 28 плиток? Перейдите, size=7чтобы size=28узнать.
whuber

2
@whuber Вы правы, спасибо за указание! Теперь он работает и дает тот же ответ, что и ваш код!
Расмус Батх

Спасибо за эту хорошую работу. Действительно подход Монте-Карло идеально подходит. Однако, в основном из соображений производительности, я решил использовать точный алгоритм расчета, предоставляемый Whuber.
Себастьян

7

p0=(nb1)(no2)(nt1)(n43)(n7)
pkk
p0=(nb1)(no2)(nt1)(n43)(n7)p1=p0+(n1)(no2)(nt1)(n43)(n7)+(nb1)(no1)(n1)(nt1)(n43)(n7)+(nb1)(no2)(n1)(n43)(n7)=p0+(n1)(n43)(n7)((no2)(nt1)+(nb1)(no1)(nt1)+(nb1)(no2))p2=p1+(n2)(n43)(n7)((nb1)(no1)+(nb1)(nt1)+(no2)+(no1)(nt1))p3=p2+(n3)(n43)(n7)((nb1)+(no1)+(nt1))p4=p3+(n4)(n43)(n7)pi=p4,i4

The idea is correct (although it would help to explain why and to explain the notation, especially concerning exactly what "n" means: whether it counts all other letters or all other letters and the wildcards), but the treatment of wildcards is incomplete. Without any explanation and without any worked examples, it is difficult to determine whether your formulas are correct so we must consider them unreliable. Generally, it is possible to write down a formula for the probability in terms of sums of products of binomial coefficients.
whuber

1
There are mistakes in the calculation of p0: it assumes exactly 1 "b", 2 "o"s, and 1 "t" will be chosen; and then it assumes the choice of the other three letters will be independent of those choices, which it is not. Assuming n=100 is the total number of tiles, the resulting value is larger than it should be (it equals 8/25850.0031). The same mistake is propagated into the calculations of the wildcard probabilities.
whuber

-1

Meh.

γc=b0xcln(x)r=0(c+y1)(c+α)r(c+β)r(c+1)r(c+γ)rxr+

+b0xcr=0(c+γ1)(c+α)r(c+β)r(c+1)r(c+γ)r(1c+γ1+

+k=0r1(1c+α+κ+1c+β+κ+1c+1+κ1c+γ+κ))xr

=b0xcr=0(c+γ1)(c+α)r(c+β)r(c+1)r(c+γ)r(ln x+1c+γ1+

+k=0r1(1c+α+κ+1c+β+κ1c+1+κ1c+γ+κ))xr
.

It's been a while since I looked at how I built my project. And my math may be entirely incorrect below, or correct. I may have it backwards. Honestly, I forget. BUT! Using only binomial combination, without taking into account blank tiles which throws the entire thing out of whack. The simple combination solution without wild.

I asked these questions myself, and built my own scrabble words probability dictionary because of it. You don't need a dictionary of possible words pulled out, only the math behind it and available letters based on letters in tile bag. The array of English rules is below. I spent weeks developing the math just to answer this question for all English words that can be used in a game, including words that can not be used in a game. It may all be incorrect.

The probability of drawing a given word from a bag of letters in Scrabble, requires how many letters are available in the bag, for each letter ( A-Z ) and, whether we're using the wild card as an addition to the math. The blank tiles are included in this math - assuming 100 tiles, 2 of which are blank. Also, how many tiles are available differs based on language of the game, and game rules from around the world. English scrabble differs from Arabic scrabble, obviously. Just alter the available letters, and the math should do the work.

If anyone finds errors, I will be sure to update and resolve them.

Boot: The probability of Boot in a game of scrabble is 0.000386% which is a chance of 67 out of 173,758 hands as shown on the word page for boot.

English Tiles

all is the array of letters in the bag. count is the array of available tiles for that letter, and point is the point value of the letter.

// All arranged by letter, number of letters in scrabble game, and point for the letter.
$all = array("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z");
    $count = array("9", "2", "2", "4", "12", "2", "3", "2", "9", "1", "1", "4", "2", "6", "8", "2", "1", "6", "4", "6", "4", "2", "2", "1", "2", "1");
$point = array("1", "3", "3", "2", "1", "4", "2", "4", "1", "8", "5", "1", "3", "1", "1", "3", "10", "1", "1", "1", "1", "4", "4", "8", "4", "10");

There are 100 tiles in an English scrabble game (i.e., the sum of $count). It does not matter how the tiles are pulled, so it's not a permutation.

The Math I Used Determine how many letters are in the word and what letters are in the word, how many of those letters are available in the tile bag ( count for each letter, unique and allchars ). Binomial coefficient of each, divided by binomial coefficient of length word.

Determine the binomial combinations available

let C(n,r) be binomial coefficient: n!/[n!(n-r)!], or 0 if r > n

Foreach letter, what is the binomial coefficient.

There is 1 "B". There are 2 available, a 2% chance of pulling the b.
There is 2 "O". There are 8 available, a 8% chance of pulling the o.
There is 1 "T". There are 6 available, a 6% chance of pulling the t.
BOOT is a 4 letter word, being taken from a 100 tile set with blanks, 98 without.

n = 98. The number of tiles without blank in the English set

B=(21)=2!2!(21)!
O=(82)=8!8!(82)!
T=(61)=6!6!(61)!

B×O×T divided by the binomial coefficient of tilecount 98!98!(98length)!


It's hard to evaluate your solution without knowing what n and r refer to in the final formula. How do you handle the effect of the blank tiles? That's what makes this a difficult problem. Regardless, it would be interesting to see a demonstration that the value of 38248840160075608000.00239 is incorrect: this was obtained using the R solution I posted. Try this one-second R simulation: let <- c(rep("b", 2), rep("o", 8), rep("t", 6), rep("_", 84)); boot <- function(x) sum(x=="b")>=1 && sum(x=="o")>=2 && sum(x=="t")>=1; mean(replicate(1e5, boot(sample(let, 7))))
whuber

Re the edit: one obvious error is that your calculation does not account for the number of blanks at all. As far as I can tell from your formulas, if that number were to change (from 2 to 50, say) then your answer would not change. That's obviously wrong. Another problem you face is to explain how your answer can conflict with three other answers already posted, which use three completely different techniques yet agree with one another (and disagree with yours).
whuber

If combinations - the math is binomial coefficients. So, let x be the count of blank tiles. The only math that changes, is n! - is there blanks used, or not. If so, add the count of blank to n! since blank allows 2 more options of every letter possible (n+x)! - if not, leave n! as is. Yes? No? If blanks are not used depending on language rule set in this case English, n! = 98 or 100 with. Each letter without blank is C(n,r), else with blank C((n+x),r). In the array, blank is there - but I forgot to put blank in the math. So just change n to work with blanks. Yes?
James Cordeiro

No, your reasoning is invalid. I invite you to try out your formulas with smaller numbers so you can see where they go wrong.
whuber

What do you mean by smaller numbers - whuber? Give me an example. Are you saying pulling boot from a set of 10 letters instead, 1 b, 2 o, 1 t's with a 1 blank in the set and 5 other letters. Or something completely different. I'm no math major, but it seems we've become poker players. We're now calculating poker odds with scrabble tiles that don't have suits.
James Cordeiro
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.