将子手难度级别的单词分类为“轻松”,“中等”或“难”的算法


114

确定子手游戏单词的“难易程度”的好算法是什么,以便游戏可以选择匹配指定难度级别的单词?

难度似乎与所需的猜测次数,字母使用的相对频率(例如,具有很多不常见字母的单词可能更难猜测)以及单词的长度有关。

还有一些主观因素需要(尝试)进行补偿,例如单词出现在玩家词汇中的可能性,并且可以被识别,从而允许从仅基于字母频率的猜测策略转向基于列表的猜测。已知的匹配词。

我现在的尝试是在红宝石下面。关于如何改进分类的任何建议?

def classify_word(w)
  n = w.chars.to_a.uniq.length # Num. unique chars in w
  if n < 5 and w.length > 4
    return WordDifficulty::Easy
  end
  if n > w.length / 2
    return WordDifficulty::Hard
  else
    return WordDifficulty::Medium
  end
end

我正在写一个我想让孩子们玩的子手游戏;我已经太老了,不能尝试“作业”,这可能就是为什么这个问题获得如此之多的反对意见的原因。确定这个词。


12
为什么要下票?这是一个体面的问题。我会做一个像这样的难度函数f(w) = (# unique letters) * (7 - # vowels) * (sum of the positions of unique letters in a list, ordered by frequency)。从那里,您可以将函数的范围分为三个部分,然后将这些问题称为困难。
Blender

2
我建议您对此进行网络搜索-可能有一些算法或词典旨在计算/报告单词的复杂性。我知道还有更长的文字。
Hot Licks

3
相关:youtube.com/watch? v=bBLm9P- ph6U(QI XL-在Hangman中最难猜到的单词)
ClausJørgensen13年

5
无论您做什么,都一定要包括灭绝光谱仪,极谱仪,眼图仪,陀螺仪。
Hot Licks

2
对于可能不熟悉Ruby的用户,也许您想解释一下方法的第一行是什么?n = w.chars.to_a.uniq.length是否计算不重复字母的数量?
T Nguyen

Answers:


91

1.简介

这是一种系统地解决此问题的方法:如果您有一个能很好地执行绞刑man的算法,那么您可以将每个单词的难度作为猜单词时程序将要进行的错误猜测的次数。

2.除了子手策略

在其他答案和注释中还暗含了一个想法,即求解器的最佳策略是根据英语中字母的出现频率或某些语料库中单词的出现频率做出决策。这是一个诱人的主意,但并不完全正确。如果求解程序能够准确地模拟设置者选择的单词的分布,则求解器将发挥最佳作用,而人类的设置者很可能会根据单词的稀有性或避免经常使用的字母来选择单词。例如,虽然E是英语中最常用的字母,如果制定者总是从字选择JUGFULRHYTHMSYZYGY,和ZYTHUM,再完美的解决者不通过猜测开始E

对setter进行建模的最佳方法取决于上下文,但是我猜想,在求解器与同一个setter或一组类似setter一起玩很多游戏的情况下,某种贝叶斯归纳推理会很好地起作用。

3.子手算法

在这里,我将概述一个很好的求解器(但远非完美)。它将设置程序建模为从固定词典中统一选择单词。这是一个贪婪的算法:在每个阶段,它都会猜测使未命中次数最少的字母,即不包含猜测的单词。例如,如果没有猜测已经进行了迄今,和可能的话DEEDDEADDARE,则:

  • 如果您猜测DE,则不会错过任何机会;
  • 如果您猜到了A,那就有一个小姐(DEED);
  • 如果您猜到了R,则有两个未命中(DEEDDEAD);
  • 如果您还猜到其他字母,则有3个未命中。

因此,在这种情况下D或者E是一个很好的猜测。

(感谢Panic上校在评论中指出,子手可以免费进行正确的猜测-我在第一次尝试中完全忘记了这一点!)

4.实施

这是此算法在Python中的实现:

from collections import defaultdict
from string import ascii_lowercase

def partition(guess, words):
    """Apply the single letter 'guess' to the sequence 'words' and return
    a dictionary mapping the pattern of occurrences of 'guess' in a
    word to the list of words with that pattern.

    >>> words = 'deed even eyes mews peep star'.split()
    >>> sorted(list(partition('e', words).items()))
    [(0, ['star']), (2, ['mews']), (5, ['even', 'eyes']), (6, ['deed', 'peep'])]

    """
    result = defaultdict(list)
    for word in words:
        key = sum(1 << i for i, letter in enumerate(word) if letter == guess)
        result[key].append(word)
    return result

def guess_cost(guess, words):
    """Return the cost of a guess, namely the number of words that don't
    contain the guess.

    >>> words = 'deed even eyes mews peep star'.split()
    >>> guess_cost('e', words)
    1
    >>> guess_cost('s', words)
    3

    """
    return sum(guess not in word for word in words)

def word_guesses(words, wrong = 0, letters = ''):
    """Given the collection 'words' that match all letters guessed so far,
    generate tuples (wrong, nguesses, word, guesses) where
    'word' is the word that was guessed;
    'guesses' is the sequence of letters guessed;
    'wrong' is the number of these guesses that were wrong;
    'nguesses' is len(guesses).

    >>> words = 'deed even eyes heel mere peep star'.split()
    >>> from pprint import pprint
    >>> pprint(sorted(word_guesses(words)))
    [(0, 1, 'mere', 'e'),
     (0, 2, 'deed', 'ed'),
     (0, 2, 'even', 'en'),
     (1, 1, 'star', 'e'),
     (1, 2, 'eyes', 'en'),
     (1, 3, 'heel', 'edh'),
     (2, 3, 'peep', 'edh')]

    """
    if len(words) == 1:
        yield wrong, len(letters), words[0], letters
        return
    best_guess = min((g for g in ascii_lowercase if g not in letters),
                     key = lambda g:guess_cost(g, words))
    best_partition = partition(best_guess, words)
    letters += best_guess
    for pattern, words in best_partition.items():
        for guess in word_guesses(words, wrong + (pattern == 0), letters):
            yield guess

5.示例结果

使用这种策略,可以评估猜测集合中每个单词的难度。在这里,我考虑系统字典中的六个字母的单词:

>>> words = [w.strip() for w in open('/usr/share/dict/words') if w.lower() == w]
>>> six_letter_words = set(w for w in words if len(w) == 6)
>>> len(six_letter_words)
15066
>>> results = sorted(word_guesses(six_letter_words))

在该词典中最容易猜测的单词(以及求解器猜测它们所需的猜测顺序)如下:

>>> from pprint import pprint
>>> pprint(results[:10])
[(0, 1, 'eelery', 'e'),
 (0, 2, 'coneen', 'en'),
 (0, 2, 'earlet', 'er'),
 (0, 2, 'earner', 'er'),
 (0, 2, 'edgrew', 'er'),
 (0, 2, 'eerily', 'el'),
 (0, 2, 'egence', 'eg'),
 (0, 2, 'eleven', 'el'),
 (0, 2, 'enaena', 'en'),
 (0, 2, 'ennead', 'en')]

最难的词是:

>>> pprint(results[-10:])
[(12, 16, 'buzzer', 'eraoiutlnsmdbcfg'),
 (12, 16, 'cuffer', 'eraoiutlnsmdbpgc'),
 (12, 16, 'jugger', 'eraoiutlnsmdbpgh'),
 (12, 16, 'pugger', 'eraoiutlnsmdbpcf'),
 (12, 16, 'suddle', 'eaioulbrdcfghmnp'),
 (12, 16, 'yucker', 'eraoiutlnsmdbpgc'),
 (12, 16, 'zipper', 'eraoinltsdgcbpjk'),
 (12, 17, 'tuzzle', 'eaioulbrdcgszmnpt'),
 (13, 16, 'wuzzer', 'eraoiutlnsmdbpgc'),
 (13, 17, 'wuzzle', 'eaioulbrdcgszmnpt')]

之所以很难,是因为在您猜测之后-UZZLE,您仍然有七种可能性:

>>> ' '.join(sorted(w for w in six_letter_words if w.endswith('uzzle')))
'buzzle guzzle muzzle nuzzle puzzle tuzzle wuzzle'

6.选择词表

当然,在为孩子准备单词列表时,您不会从计算机的系统字典开始,而会从您认为他们可能知道的单词列表开始。例如,您可能会看看Wiktionary列出的各种英语语料库中最常用的单词

例如,截至2006年古腾堡计划中10,000个最常用的单词中有1,700个六个字母的单词,其中最困难的十个是:

[(6, 10, 'losing', 'eaoignvwch'),
 (6, 10, 'monkey', 'erdstaoync'),
 (6, 10, 'pulled', 'erdaioupfh'),
 (6, 10, 'slaves', 'erdsacthkl'),
 (6, 10, 'supper', 'eriaoubsfm'),
 (6, 11, 'hunter', 'eriaoubshng'),
 (6, 11, 'nought', 'eaoiustghbf'),
 (6, 11, 'wounds', 'eaoiusdnhpr'),
 (6, 11, 'wright', 'eaoithglrbf'),
 (7, 10, 'soames', 'erdsacthkl')]

(Soames Forsyte是John GalsworthyForsyte Saga中的一个字符;单词表已转换为小写,因此我无法快速删除专有名称。)


1
在常用的单词列表上打个好电话。invokeit.wordpress.com/frequency-word-lists有英语和瑞典语,很高兴能同时使用。
grrussel

1
我希望bingle它的评级比single或难tingle- bingle是一个较不常见的词 b也是一个较不常见的字母
BlueRaja-Danny Pflughoeft13 2013年

5
很酷的算法(感谢您在编写代码之前用英语进行解释!)。但是我认为您应该尽量减少错误猜测的次数。因此,如果字典是[bat,bet,hat,hot,yum],我猜是“ T”(而不是B,A或H)。如果我是对的,那不会花我任何钱。如果我错了,那么仅保留“ yum”。
Panic Panic

8
这是一个非常酷的算法,但是我认为这并不能反映出人类玩家可能会采取的策略-人类不会知道每个单词,而是会(概率地)识别最常见的单词,否则会尝试识别足够的单词和前缀(例如ion,ing)和失败(仅猜测常见字母)(从元音开始,然后进行t / r / s / n / etc)。不确定如何编码,但是要考虑一下:)
Patashu

2
很好的分析。正如@Patashu指出的那样,要使此方法变得更好,下一步将不仅仅是使用常用单词的字典,使用完整单词的字典,但要带有关于通用性的注释,并通过试探性地权衡单词的通用性字母分布困难。但这仅是可选的改进-就目前而言,这已经是一个出色的解决方案。
本李

21

一种非常简单的方法是根据单词中缺少元音,唯一字母的数量以及每个字母的共同性来计算分数:

letters = 'etaoinshrdlcumwfgypbvkjxqz'
vowels = set('aeiou')

def difficulty(word):
    unique = set(word)
    positions = sum(letters.index(c) for c in word)

    return len(word) * len(unique) * (7 - len(unique & vowels)) * positions

words = ['the', 'potato', 'school', 'egypt', 'floccinaucinihilipilification']

for word in words:
    print difficulty(word), word

并输出:

432 the
3360 potato
7200 school
7800 egypt
194271 floccinaucinihilipilification

然后,您可以使用以下方法为单词评分:

        score < 2000   # Easy
 2000 < score < 10000  # Medium
10000 < score          # Hard

嗨,搅拌器,你能告诉我魔术数字7是做什么的吗?为什么不选择6或50?如果我输入另一个数字,该怎么办?
帕万2014年

@Pavan:没什么。所有单词的分数将上移相同的数量。
Blender

是的,当我在玩在线python执行程序时,我注意到了这种转变。我注意到了一点,那就是,当我输入类似“ absolute”的单词而不是“ abhorrent”时,尽管“ fantasy”是一个拼写正确的单词,但在文字游戏中出现的难度较低,但“ abhorrent”的价值要低于“ fantasy”。这使我意识到困难是主观的,但是我想应该做一些研究来概述哪些单词最难拼写在其他单词上,对吗?你能指出我要进行这样的研究吗?
Pavan 2014年

或者至少要进行这样的研究,因为我很难找到一个单词集合,而且第一次尝试拼写错误的人所占的百分比-这就是我现在所追求的。
帕文2014年

9

您可以使用蒙特卡洛方法来估算单词的难度:

  • 通过每次猜测一个随机字母来模拟游戏,并以目标语言中字母的频率加权,然后计算随机玩家得出解决方案所需的猜测次数。请注意,由于每个猜测都消除了一个字母,因此此过程是有限的,它将返回一个1到26(含)之间的数字。
  • 重复此过程,2*N次数N是您单词中唯一字母的数量,
  • 通过计算2*N跑步结果的平均值来计算分数,
  • 确定复杂程度:小于10的分数表示容易的单词,大于16的分数表示很难的单词;其他一切都是中等的。

2
我认为您应该只计算不正确的猜测。正确的猜测没有惩罚。
上校恐慌

为什么重复这么多?我认为这种策略(与大多数随机策略一样)对于较短的单词具有更大的差异。
Panic Panic

@ColonelPanic我认为对猜测的总数进行计数会更好,因为它自然会将不同字母的数量纳入答案。您可能对较短字词的差异较大的说法是正确的。也许重复的次数应该是固定的。但是,我认为2N是一个好的开始。
dasblinkenlight

4

先前围绕同一主题的类似讨论: 确定英语单词的难度

我喜欢链接^末尾的答案。对于儿童子手游戏,只需采用拼字游戏一样的方法即可。

给每个字母分配一个点值,然后将字母相加即可。


1
这以及在轻松级别上避免稀有或晦涩的单词,似乎是目前的前进方向。我没有提到的一个复杂问题是,这些单词是从庞大的词典中选出的,根据定义,其中大部分必须是很少使用的单词:-)
grrussel

点值可能有效,可能是使用字母频率。虽然,某些常用单词实际上可能具有奇怪的高点值。
核弹手

3

前一段时间,我使用一种明显的算法编写了一个hang子手求解器:给定所有可能单词的初始字典,在每一轮中,我们选择出现在字典中剩余单词最多的字母,然后删除不匹配的单词(取决于响应)。

该算法并不是那么简单,因为字典中经常有多个字母,每个字母以相同数量的单词出现。在这种情况下,字母的选择可以显着改变一个单词需要多少个猜测。我们选择最大值,其中有关该字母放置的结果信息(如果确实存在于单词中)给出有关系统的最大信息(信息熵最大的字母)。例如,如果剩下的两个可能的单词是“百科全书”和“百科全书”,则字母“ c”的出现概率与e,n,y,l,o,p,e,d,i相同(即保证在单词中出现),但是我们应该首先询问“ c”,因为它的信息熵非零。

源代码(C ++,GPL)在这里

所有这一切的结果是词语的列表,与为每一个所需的猜测次数:difficulty.txt(630KB)。该算法最难找到的单词是“ will”(有14个失败的猜测)。i和double l的猜测很快,但是选项包括Bill,Dill,Fill,Gill,Hill,Kill,Mill,Pill,Rill,Till,will,从那时起,唯一的选择就是猜测每个字母转。有点违反直觉的是,较长的单词会被更快地猜出(只是没有一种可供选择)。

当然,在人类的子手游戏中,心理学(和词汇的广度)所起的作用要比该算法所说明的要大得多。


3

去做就对了!对这个词玩子手。计算打败需要多少假(即不正确的猜测)。

您需要一种策略来玩。这是一项人性化策略。从字典中删除到目前为止所有不适合显示的单词。猜测剩余单词中最频繁出现的字母。

如果您的策略是随机的,则可以将度量标准定义为预期的损失数量,并凭经验进行估算。


另一种确定性策略,来自几年前我写的a 子手机器人。在猜测不正确的情况下猜测字母,以最小化剩余单词的数量(即,优化最坏的情况)。今天,我不喜欢这种过于机械的策略,我更喜欢上面的策略。


哈哈,我只想提出同样的建议。但是是一个严肃的版本:编写一个简单的机器人,使用某种简单的策略进行猜测,然后对字典中的单词运行一整遍。
Tikhon Jelvis

是的,这就是我的意思!
Panic Panic

2

首先,当然,您将生成一个唯一字母列表。然后按频率排序(使用英语或其他语言- 有此列表),频率较低的字母难度较高。

然后,您需要决定是通过加,乘还是使用其他方案来组合分数。


(实际上,您可能不需要按频率排序,而只是累加频率得分。尽管可能是排序提供了更多信息-值得一试,看看它是否对您有帮助。)
Hot Licks

您可能想以某种方式解释字母组合-即,如果有一个Q,几乎可以肯定有一个U,而U使Q的可能性更大。因此,例如从频率POV将QU视为单个字母可能是有意义的。
Hot Licks

1

您不满意,是因为您要我们为您构建一个非常复杂的算法。

您为什么不只创建三个数组(简单,中等和困难)并用一百个左右的单词填充每个数组?大约需要20分钟。

我保证您的孩子早在玩几百场游戏之前就会对吊死无聊...


3
不必那么复杂。例如,看看Blender的评论。您的答案并没有真正解决核心问题,并且不是特别有用。
Tikhon Jelvis

4
“为什么不创建三个数组(简单,中等和困难),并用一百个左右的单词填充每个数组?”:也称为“通过假设问题已经解决来解决问题”的方法。
Pascal Cuoq

批评,谢谢。。。从学术的角度来看,我想你是绝对正确的,我的回答并不能解决任何问题。但是从实际的角度来看,也就是为孩子们制作a子手游戏的最简单方法,我的回答确实可以廉价,快速地解决它。
BBagi

1
@PascalCuoq或您可以说这是“通过假设人类比算法更擅长选择合适的列表来解决问题的方法”。考虑到发问者想为孩子们玩游戏,将“帽子,猫,太阳”放在容易的列表中,而将“木琴,nought,学校”放在困难的列表中似乎更好,即使这些猜测的猜测更少一般。
达伦·库克

1
@PascalCuoq如果可以解决的话,绕过一个简单的解决方案就可以绕过一个复杂的问题,这没有什么错。构建有趣的复杂算法也没有错,但是简单的解决方案至少值得一提。
大卫,

1

好吧,可能涉及很多事情:

  1. 就像每个人都说的那样,个别信件的频率;
  2. 一个单词的长度绝对应该算,但不是线性的-一个长单词可以使字母随机猜测,而一个短单词很难得到;
  3. 同样,应该考虑使用单词“ bipartite”本身,这对于使用SO的人来说可能是个词,但对于非技术人群则可能不是。

实际上,您可以尝试共同开发几种策略,其中一半用于确定单词的价值,另一半用于尝试赢得比赛。后一组将尝试使分数最大化,而第一组将尝试使分数最小化。过了一会儿可能会有一个模式,然后决定单词价值的一半可能会给您一些基准。


使用单词的频率是一个好主意。我第一次尝试通过按频率对唯一字母进行评分来声称“共晶”是一个“容易”的词。如今,Google ngrams storage.googleapis.com/books/ngrams/books/datasetsv2.html可能有助于识别常用词。
grrussel

1

从单词列表开始,对每个单词启动Google搜索。让“点击数”作为该术语难度的(粗略)替代。

在改进的版本中,您可以根据同义词库将单词按同义词同义词进行分组,并通过计算google搜索结果来确定类别中最难的单词。

将n语法的概念进一步发展,可以通过单词的音节在散文中的出现频率来评估单词的难度。当然,取决于音节统计的质量。您可能需要区分词素和功能词(确定词,连词等),并根据词中的音节数进行归一化(感觉像我写作时的Overkill一样)。


0

我喜欢构建一个算法的想法,该算法可以根据用户进行学习和更改。刚开始时,您可以实施建议用于列表的任何算法,然后随着越来越多的人玩这个游戏,您可以根据猜测的数量为每个单词分配权重(该单词也不断地跟踪和计算) )。这样可以避免对复杂但流行的单词进行评级,但是却为人们所熟知。


0

计算拼字游戏中单词每个字母的值:E = 1,D = 2,V = 4,X = 8,依此类推。将它们加起来并除以字母数即可得到平均字母值,然后使用该值对单词进行评分。计算大型词典中每个单词的平均值,并确定四分位数之间的断点。最低四分位数的单词称为“容易”,两个中四分位数的单词称为“中等”,而最高四分位数的单词称为“硬”。

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.