从拼字游戏中从一包字母中画出给定单词的可能性


18

假设您有一个带有磁贴的袋子,每个磁贴上都有一个字母。有带有字母“ A”的图块,和“ B”等等,还有 “通配符”图块(我们有)。假设您有一本单词数量有限的字典。n A n B n n = n A + n B + + n Z + n ññ一种ñññ=ñ一种+ñ++ñž+ñ

您可以从袋子中挑选块瓷砖,而无需更换。ķ

给定所选的图块,您如何计算(或估计)从字典中形成长度为(1 < = <)的给定单词的概率?ķ ķķķ

对于不熟悉Scrabble(TM)的用户,可以使用通配符来匹配任何字母。因此,单词“ BOOT”可以用图块“ B”,“ *”,“ O”,“ T”“拼写”。字母的顺序无关紧要。

建议:为了简化答案的编写,最好回答以下问题:从一个新鲜的书包中拿出7个字母后,在可能的动作中使用“引导”一词的可能性是多少?

(该问题的介绍已从该类似问题中复制而来)


我建议您先处理一个简单的案例,例如不使用通配符的案例。
Glen_b-恢复莫妮卡

@Glen_b我同意。由于我的最终目的是按概率对单词进行排序,因此我认为忽略通配符是可以接受的近似值。不过,我仍然不具备的技能来构建一个公式来解决这个简单的问题
塞巴斯蒂安

1
如果要开始更简单,请计算选择“ B”,“ O”,“ O”,“ T”的概率。之后,计算以任意顺序选择字母的概率。之后,请考虑您进行了7次尝试。然后考虑通配符。
杰里·席默

1
解决此问题的一种简单方法是使用蒙特卡洛近似。这样就足够了吗?
RasmusBååth2013年

1
您是在谈论仅使用您选择的字母来构成单词,还是考虑已经选择的字母以及已经放置在板上的单词?
samthebrand

Answers:


12

需要一个公式。不幸的是,情况是如此复杂,以至于任何公式都只是列举所有可能性的一种round回方式。相反,此答案提供了一种算法,该算法(a)等于涉及二项式系数乘积之和的公式,并且(b)可以移植到许多平台上。


要获得这样的公式,请通过两种方式将可能性分解为互不相交的组:根据在机架中选择了多少个不在单词中的字母(将其设为)以及根据选择了多少个通配符(空白)(让它成为w)。当机架中有r = 7个图块,N个可用图块,M个可用字母不在单词中的可用图块且W = 2个空白时,由m w 给出的可能选择数为w[R=7ñ中号w ^=2w

(Mw ^wñ-中号-w ^[R--w

因为非单词字母,空格和单词字母的选择是独立条件w[R

假设只有空白可用,将选择贴,这可以减少查找仅在代表单词字母的贴中选择单词时拼写单词的方法的问题。情况很混乱,似乎没有封闭的公式可用。例如,在空白和的单词外的字母被绘制的情况下,刚好剩下四个字母来拼写从“ b”,“ o”和“ t”图块绘制的“ boot” 。假设有 “ b”, “ o”和r m w w = 0 m = 3 2 8 6wrmww=0m=3286在拼字游戏图块集中有“ t”,存在绘画(多集)的正概率。 ”,“ ooot”,“ oott”,“ ottt”和“ tttt”,但其中只有一种拼写是“ boot”。那是简单的情况!例如,假设机架包含从“ o”,“ b”和“ t”图块中随机选择的五个图块,再加上两个空白,则有更多拼写“ boot”(不拼写)的方法。例如,可以从“ __boott”和“ __bbttt”拼写“ boot”,但不能从“ __ttttt”拼写。

这种计数(问题的核心)可以递归处理。 我将用一个例子来描述它。假设我们希望计算“ b”,“ o”和“ t”图块集合中的一个空白和另外四个图块拼写“ boot”的方式(因此,其余两个图块显示的非空白字母不在{ “ b”,“ o”,“ t”})。考虑第一个字母“ b”:

  1. 可以从可用的两个“ b”图块中以方式绘制“ b”。这样就减少了用两个空格和“ o”和“ t”图块集合中的三个以上的图块拼写后缀“ oot”的方法数量的麻烦。21个

  2. 可以将一个空白指定为“ b”。这样就减少了使用剩余的空白以及“ o”和“ t”图块集合中的三个以上图块来拼写“ oot”的方法数量的问题。

通常,步骤(1)和(2)是不相交的,因此对概率计算有附加作用。可以将步骤(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中号w ^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)))
}

让我们尝试一下该解决方案,并随着时间的推移对其进行计时。以下测试使用@RasmusBåå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

“启动”概率恰好等于其他答案中获得的值(使用类似方法,但将其置于需要符号代数计算平台的功能更强大的框架中)。这四个单词的概率都与Bååth的模拟相当接近(由于其低概率(小于百万分之一),因此不能期望为“动物学”提供准确的值)。114327888/160075608002381831/33349085011840/16007560800


酷而优雅的解决方案!而且比我快得多... :)
RasmusBååth13年

1
谢谢,这是一个很好的答案。我会很难编码您的算法,因此非常欢迎使用现成的代码。我不知道,R但是仍然设法在不到一个小时的工作中使用了您的函数,因此脚本从2万个单词的字典文件中获取输入并将结果写入.csv。(这在中档i5上花费了不到10分钟的时间)
塞巴斯蒂安

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]

    {bØ2ŤbØ2ψbØŤψØ2ŤψbØψ2Ø2ψ2bŤψ2ØŤψ2}

  3. 计算非单词:

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

    b7+7b6Ø+21b5Ø2++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

我得到不同的答案。考虑到您的模拟代码的复杂性,很难说出它们为什么不同意,但是我将在处理通配符时开始寻找原因。
胡伯

2
我相信这sample并不像您期望的那样。例如,如果将游戏修改为允许放置28个磁贴,会对代码产生什么影响?改变size=7size=28找出。
whuber

2
@whuber你是对的,感谢你指出!现在它可以正常工作,并且得到与您的代码相同的答案!
RasmusBååth'13

感谢您的出色工作。确实,蒙特卡洛方法非常适合。但是,主要是出于性能原因,我选择使用Whuber提供的精确计算算法。
塞巴斯蒂安

7

p0=ñb1个ñØ2ñŤ1个ñ-43ñ7
pķķ
p0=ñb1个ñØ2ñŤ1个ñ-43ñ7p1个=p0+ñ1个ñØ2ñŤ1个ñ-43ñ7+ñb1个ñØ1个ñ1个ñŤ1个ñ-43ñ7+ñb1个ñØ2ñ1个ñ-43ñ7=p0+ñ1个ñ-43ñ7ñØ2ñŤ1个+ñb1个ñØ1个ñŤ1个+ñb1个ñØ2p2=p1个+ñ2ñ-43ñ7ñb1个ñØ1个+ñb1个ñŤ1个+ñØ2+ñØ1个ñŤ1个p3=p2+ñ3ñ-43ñ7ñb1个+ñØ1个+ñŤ1个p4=p3+ñ4ñ-43ñ7p一世=p4一世4

ñ

1
p0ñ=1008/25850.0031

-1

γC=b0XCñX[R=0C+ÿ-1个C+α[RC+β[RC+1个[RC+γ[RX[R+

+b0XC[R=0C+γ-1个C+α[RC+β[RC+1个[RC+γ[R1个C+γ-1个+

+ķ=0[R-1个1个C+α+κ+1个C+β+κ+1个C+1个+κ-1个C+γ+κX[R

=b0XC[R=0C+γ-1个C+α[RC+β[RC+1个[RC+γ[Rñ X+1个C+γ-1个+

+ķ=0[R-1个1个C+α+κ+1个C+β+κ-1个C+1个+κ-1个C+γ+κX[R

自从我看过如何构建项目以来已经有一段时间了。我的数学下面可能是完全错误的,或者是正确的。我可能会倒退。老实说,我忘记了。但!仅使用二项式组合,而不考虑空白图块,而空白图块会使整个过程变得毫无用处。没有野性的简单组合解决方案。

我自己问了这些问题,并因此建立了自己的拼字单词概率词典。您不需要字典中列出可能出现的单词,只需隐藏其背后的数学信息以及基于平铺书包中字母的可用字母即可。英文规则的排列如下。我花了数周的时间开发数学,只是为了回答游戏中所有可以使用的英语单词,包括游戏中无法使用的单词。可能全部不正确。

在Scrabble中从一包字母中得出一个给定单词的可能性,要求每个字母(AZ)袋中有多少个字母,以及我们是否将通配符用作数学的补充。此数学运算中包括空白图块-假设有100个图块,其中2个为空白。此外,根据游戏的语言以及世界各地的游戏规则,可用的磁贴数量也有所不同。显然,英语拼字游戏与阿拉伯语拼字游戏不同。只需更改可用字母,数学就可以完成工作。

如果有人发现错误,我将确保进行更新和解决。

引导:在拼字游戏中引导的概率为0.000386%,这在引导的单词页上显示,在173,758手牌中有67的机会。

英式瓷砖

都是袋子里的字母数组。count是该字母的可用磁贴数组,point是字母的点值。

// 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");

英文拼字游戏中有100个磁贴(即的总和$count)。瓦片的拖动方式无关紧要,因此不是排列。

我使用的数学 确定单词中有多少个字母,单词中有哪些字母,磁贴袋中有多少个字母可用(每个字母,唯一字符和allchars计数)。每个的二项式系数除以长度字的二项式系数。

确定可用的二项式组合

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

对于每个字母,二项式系数是多少。

有1个“ B”。有2个可用,有2%的机会拉b。
有2个“ O”。有8个可用,有8%的机会拉o。
有1个“ T”。有6个可用选项,拉t的几率是6%。
BOOT是一个4字母的单词,取自100个带空白的图块,其中98个不带空格。

n =98。英语集中不含空白的图块数

=21个=222-1个
Ø=82=888-2
Ť=61个=666-1个

×Ø׍ 除以tilecount的二项式系数 989898-ËñGŤH


不知道什么就很难评估您的解决方案 ñ[R请参考最终公式。您如何处理空白图块的效果?这就是使这成为难题的原因。无论如何,看到一个示范38248840160075608000.00239是不正确的:这是使用R我发布的解决方案获得的。试试这个一秒钟的R模拟: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))))
ub

重新编辑:一个明显的错误是您的计算根本没有考虑空白的数量。据我从您的公式中得知,如果该数字更改(例如从2更改为50),您的答案将不会更改。这显然是错误的。您面临的另一个问题是解释您的答案如何与已经发布的其他三个答案冲突,后者使用三种完全不同的技术却彼此同意(不同意您的方法)。
ub

如果组合-数学是二项式系数。因此,令x为空白图块的计数。唯一改变的数学是n!-是否使用了空白。如果是这样,将空白计数加到n!因为空白允许每个字母再增加2个选项(n + x)!-如果没有,离开n!照原样。是?没有?如果在这种情况下,根据语言规则设置不使用空格,则为英语,n!= 98或100。每个不带空格的字母为C(n,r),否则带空格C((n + x),r)。在数组中,有空白-但是我忘了在数学中加上空白。因此,只需更改n即可使用空格。是?
James Cordeiro 2014年

不,您的推理是无效的。我邀请您尝试使用较小的数字来计算公式,以便查看错误之处。
ub

你说小数目是什么意思?给我一个例子。您是说从10个字母的集合中拉启动,而是从1 b,2 o,1 t的集合中拉,其中集合中有1个空白,其他5个字母。或完全不同的东西。我不是数学专业的学生,​​但看来我们已经成为扑克玩家。现在,我们使用没有西装的拼字游戏来计算扑克赔率。
James Cordeiro 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.