1. บทนำ
นี่คือวิธีแก้ไขปัญหานี้อย่างเป็นระบบ: หากคุณมีอัลกอริทึมที่เล่นเพชฌฆาตได้ดีคุณสามารถใช้ความยากของแต่ละคำเป็นจำนวนการเดาผิดที่โปรแกรมของคุณจะใช้หากเดาคำนั้น
2. นอกเหนือจากกลยุทธ์เพชฌฆาต
มีความคิดที่เป็นนัยในคำตอบและความคิดเห็นอื่น ๆ ว่ากลยุทธ์ที่ดีที่สุดสำหรับผู้แก้ปัญหาคือการตัดสินใจตามความถี่ของตัวอักษรในภาษาอังกฤษหรือความถี่ของคำในคลังข้อมูลบางส่วน นี่เป็นความคิดที่ยั่วยวน แต่ก็ไม่ถูกต้องนัก ผู้แก้จะทำได้ดีที่สุดหากสร้างแบบจำลองการกระจายของคำที่ผู้กำหนดเลือกได้อย่างถูกต้องและผู้ตั้งค่าที่เป็นมนุษย์อาจเลือกคำตามความหายากหรือการหลีกเลี่ยงตัวอักษรที่ใช้บ่อย ตัวอย่างเช่นแม้ว่าE
เป็นตัวอักษรที่ใช้บ่อยที่สุดในภาษาอังกฤษถ้าหมามักจะเลือกจากคำJUGFUL
, RHYTHM
, SYZYGY
และZYTHUM
แล้วแก้ที่สมบูรณ์แบบไม่ได้เริ่มต้นด้วยการคาดเดาE
!
แนวทางที่ดีที่สุดในการสร้างแบบจำลองเซ็ตเตอร์นั้นขึ้นอยู่กับบริบท แต่ฉันเดาว่าการอนุมานอุปนัยแบบเบย์บางประเภทจะทำงานได้ดีในบริบทที่ผู้แก้เล่นเกมหลายเกมกับเซตเทอร์คนเดียวกันหรือกับกลุ่มเซตเทอร์ที่คล้ายกัน
3. อัลกอริทึมเพชฌฆาต
ที่นี่ฉันจะร่างตัวแก้ที่ค่อนข้างดี (แต่ยังห่างไกลจากความสมบูรณ์แบบ) มันจำลองเซ็ตเตอร์ว่าเลือกคำจากพจนานุกรมคงที่ มันเป็นอัลกอริทึมโลภ : ในแต่ละขั้นตอนจะคาดเดาตัวอักษรที่ลดจำนวนการพลาดนั่นคือคำที่ไม่มีการคาดเดา ตัวอย่างเช่นถ้าไม่มีคาดเดาได้รับการทำเพื่อให้ห่างไกลและคำพูดที่เป็นไปได้DEED
, DEAD
และDARE
แล้ว:
- ถ้าคุณเดา
D
หรือE
ไม่มีพลาด
- ถ้าคุณเดา
A
มีหนึ่ง miss ( DEED
);
- ถ้าคุณเดา
R
มีสองพลาด ( DEED
และDEAD
);
- หากคุณเดาตัวอักษรอื่นมีการพลาดสามครั้ง
อย่างใดอย่างหนึ่ง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ในองค์กรภาษาอังกฤษต่างๆ
ตัวอย่างเช่นในบรรดา 1,700 คำหกตัวอักษรใน10,000 คำที่พบบ่อยที่สุดใน Project Gutenberg ณ ปี 2549สิบข้อที่ยากที่สุด ได้แก่ :
[(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 เป็นตัวละครในForsyte Saga โดย John Galsworthyรายการคำถูกแปลงเป็นตัวพิมพ์เล็กดังนั้นฉันจึงไม่สามารถลบชื่อที่เหมาะสมได้อย่างรวดเร็ว)
f(w) = (# unique letters) * (7 - # vowels) * (sum of the positions of unique letters in a list, ordered by frequency)
ฉันจะทำให้การทำงานยากลำบากเช่น จากนั้นคุณสามารถแบ่งช่วงของฟังก์ชั่นออกเป็นสามส่วนและเรียกความยากลำบากของคุณออกมา