Алгоритм класифікації слів для рівнів складності вішалки як "Легкий", "Середній" або "Жорсткий"


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). Звідти ви можете просто розділити діапазон функції на три сегменти і назвати ті ваші труднощі.
Блендер

2
Я б запропонував вам зробити пошук в Інтернеті для цього - швидше за все, існують алгоритми чи словники, які мають намір обчислити / повідомити про складність слова. Я знаю, що існує довший текст.
Гарячі лизи

3
Пов'язане: youtube.com/watch?v=bBLm9P-ph6U (QI XL - найскладніше здогадатися у вішанці)
Клаус Йоргенсен

5
Що б ви не робили, не забудьте включити EXTINCTIONSPECTROPHOTOPOLERISCOPEOCCULOGRAVOGYROKYNETOMETER.
Гарячі лизи

2
Користувачам, які, можливо, не знайомі з Ruby, можливо, ви хочете пояснити, що робить перший рядок вашого методу? n = w.chars.to_a.uniq.lengthЧи підраховує кількість унікальних букв?
T Nguyen

Відповіді:


91

1. Введення

Ось спосіб систематично підходити до цієї проблеми: якщо у вас є алгоритм, який добре розіграє вішалку, то ви можете сприймати складність кожного слова як кількість помилкових здогадок, які ваша програма сприйняла б, якщо вгадати це слово.

2. Окрім стратегії вішалки

В інших відповідях та коментарях є ідея, що оптимальною стратегією вирішувача буде базувати свої рішення на частоті букв англійською мовою або на частоті слів у якомусь корпусі. Це спокуслива ідея, але це не зовсім правильно. Розв’язувач найкраще робить, якщо він точно моделює розподіл слів, обраних сеттером , і людина, що задає, цілком може вибирати слова, виходячи з їх рідкості чи уникання часто використовуваних букв. Наприклад, хоча Eнайбільш часто використовуються лист англійською мовою, якщо сетер завжди вибирає з слів JUGFUL, RHYTHM, SYZYGY, і ZYTHUM, то ідеальний вирішувач не починається гадати E!

Найкращий підхід до моделювання сетера залежить від контексту, але я здогадуюсь, що якийсь байєсівський індуктивний умовивід спрацював би добре в контексті, коли сольвер грає багато ігор проти того ж сетера або проти групи подібних сеттерів.

3. Алгоритм вішалки

Тут я окреслю вирішувач, який досить добре (але далеко не ідеальний). Він моделює сетер як вибір слів рівномірно з фіксованого словника. Це жадібний алгоритм : на кожному етапі він відгадує букву, яка мінімізує кількість пропусків, тобто слів, які не містять здогаду. Наприклад, якщо досі не було здогадань, а можливі слова є DEED, DEADі DARE, тоді:

  • якщо ви вгадати , Dчи E, немає промахів;
  • якщо ви здогадуєтесь A, є одна міс ( DEED);
  • якщо ви здогадуєтесь R, є два промахи ( DEEDі DEAD);
  • якщо ви вгадаєте будь-який інший лист, то є три промахи.

Так що Dабо Eє гарною здогадкою в цій ситуації.

(Дякую полковнику Паніку в коментарях за те, що він вказав, що правильні здогадки вільні у вішалки - я повністю забув це в першій своїй спробі!)

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. Вибір списку слів

Звичайно, готуючи списки слів для своїх дітей, ви не починали б із системного словника свого комп’ютера, ви б почали зі списку слів, які, на вашу думку, вони можуть знати. Наприклад, ви можете ознайомитись зі списками Вікісловника найбільш часто вживаних слів у різних англійських корпораціях.

Наприклад, серед 1700 шестибуквених слів у 10 000 найпоширеніших слів у проекті Гутенберг станом на 2006 рік , найскладнішими десятьма є такі:

[(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')]

(Сомс Форсайт - персонаж із саги про Форсайта Джона Голсуорсі ; список слів перетворений в малі регістри, тому мені не вдалося швидко видалити власні імена.)


1
Хороший дзвінок у списках часто використовуваних слів. invokeit.wordpress.com/frequency-word-lists має англійську та шведську мови, тому приємно мати обоє.
груссел

1
Я б очікував, bingleщо його оцінюватимуть важче, ніж singleабо tingle- bingleце менш поширене слово і b є менш поширеною буквою
BlueRaja - Danny Pflughoeft

5
Класний алгоритм (і дякую за пояснення англійською мовою перед написанням коду!). Але я думаю, вам слід спробувати мінімізувати кількість неправильних здогадок. Таким чином, якби словник був [bat, bet, hat, hot, yum], я б здогадався "T" (а не B, A чи H). Якщо я маю рацію, мені це нічого не коштує. Якщо я помиляюся, то залишається лише «ням».
Полковник Паніка

8
Це дуже класний алгоритм, але я думаю, що це не відображає стратегію, яку люди, ймовірно, роблять - замість того, щоб знати кожне кожне слово, люди збираються розпізнавати (імовірнісно) найпоширеніші слова, інакше спробують розпізнати достатньо і префікси (наприклад, іон, ing) та невдачі, які просто відгадують загальні букви (починаючи з голосних, потім роблячи t / r / s / n / тощо). Не знаєте, як це
зашифрувати,

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? Що трапиться, якщо я вставлю інший арбітражний номер?
Паван

@Pavan: Насправді нічого. Оцінки всіх слів будуть зміщені на ту саму суму.
Блендер

так, я помітив зміну, коли грав у мережі з виконавцем онлайн-пітонів. Я щось помітив, і це коли я набираю щось на кшталт фантастичного порівняно з огидним, огидний матиме нижче значення, ніж фантастичний, незважаючи на фантастичне, що слово, яке написано правильніше, таким чином повинно з’являтися на нижчому рівні складності у грі на слова. Це змусило мене зрозуміти, що складність є суб'єктивною, але в мене було думати, що потрібно зробити якесь дослідження, щоб визначити, які слова найскладніше вимовити за іншими, правда? Не могли б ви вказати мені на таке дослідження pls?
Pavan

Або принаймні, як називалося б таке дослідження, оскільки мені важко знайти колекцію слів із відсотком людей, які неправильно написали слово з першої спроби - це те, про що я зараз.
Pavan

9

Ви можете використовувати метод Монте-Карло, щоб оцінити складність слова:

  • Симулюйте гру, відгадуючи кожен раз випадкову літеру, зважену за частотою букви на вашій цільовій мові, і порахуйте, скільки здогадок знадобилося вашому рандомізованому гравцеві, щоб знайти рішення. Зауважте, що оскільки кожна здогадка усуває букву, цей процес є кінцевим, і він повертає число від 1 до 26 включно.
  • Повторіть цей 2*Nчас, де Nкількість унікальних літер у вашому слові,
  • Обчисліть рахунок, усереднюючи результати 2*Nпробігів,
  • Визначте рівень складності: бали менше десяти означають легке слово, а бали вище шістнадцяти - тверде слово; все інше середнє.

2
Я думаю, вам слід рахувати лише неправильні здогадки. Немає штрафу за правильні здогадки.
Полковник Паніка

Чому така кількість повторів? Я думаю, що ця стратегія (як і більшість рандомізованих стратегій) має більшу варіацію для коротших слів.
Полковник Паніка

@ColonelPanic Я думаю, що підрахувати загальну кількість здогадок краще, тому що це, природно, включає відповідь у відповідь. Ви можете мати рацію щодо відхилення від коротших слів, які є вищими. Можливо, кількість повторень слід тоді фіксувати. Однак я думаю, що 2N був би гарним початком.
dasblinkenlight

4

Попередня аналогічна дискусія навколо тієї ж теми: Визначте складність англійського слова

Мені подобається відповідь в кінці посилання ^. Для дитячої гри-вішалки просто застосуйте такий підхід, як це робить скрабл.

Призначте кожну букву крапкове значення, а потім просто складіть літери.


1
Це, разом з уникненням рідкісних або незрозумілих слів на легких рівнях, зараз здається дорогою вперед. Ускладнення, про яке я не згадував, полягає в тому, що слова вибираються з величезних словників, основна частина яких за визначенням повинна бути рідко вживаними словами :-)
grrussel

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

3

Нещодавно я написав розв'язувальник, що використовує очевидний алгоритм: даючи початковий словник усіх можливих слів, на кожному кроці ми вибираємо букву, що зустрічається в більшості слів, що залишилися в словнику, а потім видаляємо невідповідні слова (залежно від відповідь) зі словника.

Алгоритм не настільки простий, як це, оскільки часто є кілька букв, кожна з яких зустрічається в однаковій кількості слів у словнику. У цьому випадку вибір літери може істотно змінитись, скільки здогадок потрібно для слова. Ми вибираємо максимуми, коли отримана інформація про розміщення цього листа (якщо це дійсно є в слові) дає максимальну інформацію про систему (лист із максимальною ентропією інформації ). наприклад, якщо два можливих слова є "енциклопедія" та "енциклопедичний", буква "с" має таку ж ймовірність появи, як і e, n, y, l, o, p, e, d, i (тобто це гарантовано, що це слово), але спершу слід запитати про "c", оскільки він має ненульову ентропію інформації.

Джерело (C ++, GPL) знаходиться тут

Результат усього цього - це перелік слів із кількістю потрібних здогадок для кожного з них: труднощі.txt (630 КБ). Найважче для цього алгоритму знайти слово "буде" (з 14 невдалими здогадами); i i double l здогадуються досить швидко, але тоді варіанти включають рахунок, кроп, заливка, зябра, гірка, вбити, млин, пігулку, заливку, до, буде, і з цього моменту єдиний варіант - вгадати кожну букву в черга. Дещо протизаконно, довші слова набагато швидше здогадуються (просто не те, що з них можна вибрати).

Зрозуміло, у людській грі на вісі психологія (і широта словника) відіграють набагато більшу роль, ніж цей алгоритм припадає на ...


3

Просто зроби це! Грайте вішалки проти слова. Порахуйте, скільки фактів (тобто неправильних здогадів) потрібно, щоб перемогти.

Вам буде потрібна стратегія для гри. Ось така людська (іш) стратегія. Зі словника викресліть усі слова, що не відповідають розкриттям досі. Вгадайте найчастішу букву серед решти слів.

Якщо ваша стратегія є рандомізованою, ви можете визначити міру як очікувану кількість втрат, і оцінити це емпірично.


Інша детермінована стратегія, від ката бота я написав кілька років тому. Вгадайте букву, яка мінімізує кількість слів, що залишилися у випадку, якщо здогад невірний (тобто оптимізуйте найгірший випадок). Сьогодні мені не подобається, що ця стратегія є занадто механічною, я віддаю перевагу тій, що описано вище.


Ха-ха, я просто збирався запропонувати те саме. Але серйозна версія: напишіть простий бот, який здогадується, використовуючи якусь просту стратегію, а потім просто запустіть цілу купу разів над словами зі словника.
Тихон Єлвіс

Так, це я мав на увазі!
Полковник Паніка

2

По-перше, звичайно, ви сформуєте список унікальних листів. Потім сортуйте за частотою (англійською або будь-якою мовою - для цього є списки ), при цьому рідкісні букви мають більшу складність.

Тоді вам потрібно вирішити, чи поєднуєте ви бали, додаючи, множуючи чи використовуючи якусь іншу схему.


(Насправді, вам може не потрібно сортувати за частотою, а просто накопичувати частотні показники. Хоча може бути, що сортування надає додаткову інформацію - варто спробувати перевірити, чи здається, що щось робить для вас.)
Hot Licks

І ви, можливо, хочете якось врахувати комбіновані букви - тобто, якщо є Q, майже напевно є U, а U робить Q набагато більш імовірним. Тому може бути доцільним, наприклад, розглядати QU як одну букву з частоти POV.
Гарячі лизи

1

Ви отримуєте недовіру, оскільки ви просите нас створити для вас дуже складний алгоритм.

Чому б вам просто не створити три масиви (прості, середні та жорсткі) і не заповнити кожен сотнями слів? Це займе близько 20 хвилин.

Я обіцяю, що ваші діти будуть нудьгувати від повішеної людини задовго до того, як вони спалять через кілька сотень ігор ...: D


3
Це не повинно бути таким складним. Наприклад, подивіться, наприклад, коментар Блендера. Ваша відповідь насправді не стосується основного питання і не особливо корисна.
Тихон Єлвіс

4
"Чому б вам просто не створити три масиви (прості, середні та жорсткі) і не заповнити кожен сотнями слів?": Також називається методом "вирішити проблему, вважаючи, що проблема вже вирішена".
Паскаль Куок

Критика прийнята, дякую ... Я думаю, з академічної точки зору, ви абсолютно праві, моя відповідь нічого не вирішує. Але з практичної точки зору, тобто найпростішого способу побудувати гру-вішалку для своїх дітей, моя відповідь вирішує це дешево та швидко.
BBagi

1
@PascalCuoq Або можна сказати, що це підхід до "вирішення проблеми, припускаючи, що люди краще вибирають відповідні списки, ніж алгоритми". Зважаючи на те, що запитуючий бажає гри для дітей, здається, краще, що "шапка, кішка, сонце" є у легкому списку, а "ксилофон, ніщо, школа" входять у складний список, навіть якщо вони можуть бути знайдені з меншою кількістю здогадів в середньому.
Даррен Кук

1
@PascalCuoq Немає нічого поганого в тому, щоб обійти складну проблему, хоч і просте рішення, якщо ви зможете піти з нею. У створенні складних алгоритмів для розваги немає нічого поганого, але просте рішення, принаймні, заслуговує на згадку.
Девід

1

Що ж, потенційно може бути багато чого:

  1. Як усі казали, частота окремих букв;
  2. Довжина слова, безумовно, повинна рахуватися, але не лінійним способом - довге слово може змусити випадкові здогади потрапляти на літери, а коротке - важко дістати;
  3. Крім того, слід враховувати самі слова - "двостороннє" може бути словом для людей, що перебувають на ЗП, але, можливо, не для нетехнічного населення.

Насправді, ви можете спробувати спільно розвинути кілька стратегій , половина з яких визначила значення слова, а половина - для спроби виграти гру. Остання група намагатиметься максимізувати рахунок, а перша намагається мінімізувати рахунок. Через деякий час може з’явитися схема, і тоді половина для визначення вартості слова може дати вам кілька орієнтирів.


Частота вживання слова - хороший момент. Моя перша спроба, заснована на забитті унікальних літер за частотою, стверджувала, що "евтектика" була "легким" словом. Google ngrams storage.googleapis.com/books/ngrams/books/datasetsv2.html сьогодні , ймовірно, допоможе визначити слова для загального користування.
груссел

1

Почніть зі списку слів та запустіть пошук Google для кожного. Нехай кількість хітів служить (грубим) проксі-сервісом складності терміна.

У доопрацьованій версії ви б згрупували слова за синонімом Відношення на основі Тезауруса та визначили найскладніше слово категорії шляхом підрахунку Результатів пошуку Google.

Прийняття поняття n-грамів Ще на крок складність слова можна оцінити за частотою його складів у прозі. Звичайно, залежить від якості статистики складів. Можливо, вам, мабуть, доведеться розрізняти лексеми та слова функції (детермінанти, сполучники тощо) та нормалізувати за кількістю складів у слові (відчуває себе як надмірність, як я пишу ...).


0

Мені подобається ідея побудови алгоритму, який навчається та змінюється залежно від користувачів. На початку ви можете реалізувати будь-який алгоритм, запропонований скласти список, потім, оскільки все більше людей грають у гру, ви присвоюєте вагу кожному з слів залежно від кількості здогадок (що також постійно відслідковується та обчислюється ). Це перешкоджає складним, але популярним словам давати складну оцінку, але вони добре відомі людям.


0

Обчисліть значення кожної літери слова в точках Scrabble: E = 1, D = 2, V = 4, X = 8 тощо. Додайте їх і розділіть на кількість букв, щоб отримати середнє значення літери, і використовуйте це, щоб оцінити слово. Обчисліть середнє значення для кожного слова у великому словнику та визначте точки перерви між квартилями. Назвіть слова в нижньому кварталі "легкий", слова у двох середніх квартілях "середній", а слова у найвищому кварталі - "важкий".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.