Dies ist ein Wortspiel aus einem Satz Aktivitätskarten für Kinder. Unterhalb der Regeln befindet sich Code, um das beste Triplet mit / usr / share / dict / words zu finden. Ich dachte, es sei ein interessantes Optimierungsproblem und frage mich, ob die Leute Verbesserungen finden können.
Regeln
- Wählen Sie jeweils einen Buchstaben aus den folgenden Sätzen.
- Wählen Sie ein Wort mit den gewählten Buchstaben (und allen anderen).
- Bewerte das Wort.
- Jeder Buchstabe aus dem gewählten Satz erhält die Nummer, die mit dem Satz angezeigt wird (Wiederholungen eingeschlossen).
AEIOU
0 zählen- Alle anderen Buchstaben sind -2
- Wiederholen Sie die obigen Schritte 1 bis 3 (keine weiteren Buchstaben in Schritt 1) noch zweimal.
- Die Endnote ist die Summe der drei Wortnoten.
Setzt
(Set 1 ergibt 1 Punkt, Set 2 ergibt 2 Punkte usw.)
- LTN
- RDS
- GBM
- KWK
- FWV
- YKJ
- QXZ
Code:
from itertools import permutations
import numpy as np
points = {'LTN' : 1,
'RDS' : 2,
'GBM' : 3,
'CHP' : 4,
'FWV' : 5,
'YKJ' : 6,
'QXZ' : 7}
def tonum(word):
word_array = np.zeros(26, dtype=np.int)
for l in word:
word_array[ord(l) - ord('A')] += 1
return word_array.reshape((26, 1))
def to_score_array(letters):
score_array = np.zeros(26, dtype=np.int) - 2
for v in 'AEIOU':
score_array[ord(v) - ord('A')] = 0
for idx, l in enumerate(letters):
score_array[ord(l) - ord('A')] = idx + 1
return np.matrix(score_array.reshape(1, 26))
def find_best_words():
wlist = [l.strip().upper() for l in open('/usr/share/dict/words') if l[0].lower() == l[0]]
wlist = [l for l in wlist if len(l) > 4]
orig = [l for l in wlist]
for rep in 'AEIOU':
wlist = [l.replace(rep, '') for l in wlist]
wlist = np.hstack([tonum(w) for w in wlist])
best = 0
ct = 0
bestwords = ()
for c1 in ['LTN']:
for c2 in permutations('RDS'):
for c3 in permutations('GBM'):
for c4 in permutations('CHP'):
for c5 in permutations('FWV'):
for c6 in permutations('YJK'):
for c7 in permutations('QZX'):
vals = [to_score_array(''.join(s)) for s in zip(c1, c2, c3, c4, c5, c6, c7)]
ct += 1
print ct, 6**6
scores1 = (vals[0] * wlist).A.flatten()
scores2 = (vals[1] * wlist).A.flatten()
scores3 = (vals[2] * wlist).A.flatten()
m1 = max(scores1)
m2 = max(scores2)
m3 = max(scores3)
if m1 + m2 + m3 > best:
print orig[scores1.argmax()], orig[scores2.argmax()], orig[scores3.argmax()], m1 + m2 + m3
best = m1 + m2 + m3
bestwords = (orig[scores1.argmax()], orig[scores2.argmax()], orig[scores3.argmax()])
return bestwords, best
if __name__ == '__main__':
import timeit
print timeit.timeit('print find_best_words()', 'from __main__ import find_best_words', number=1)
Die Matrix-Version ist das, was ich gefunden habe, nachdem ich eine in reinem Python (mit Wörterbüchern und unabhängig voneinander bewerteten Wörtern) und eine andere in Numpy geschrieben habe, aber mit Indexierung statt Matrix-Multiplikation.
Die nächste Optimierung wäre, die Vokale vollständig aus der Wertung zu entfernen (und eine modifizierte ord()
Funktion zu verwenden), aber ich frage mich, ob es noch schnellere Ansätze gibt.
EDIT : hinzugefügt timeit.timeit Code
BEARBEITEN : Ich füge ein Kopfgeld hinzu, das ich für jede Verbesserung gebe, die ich am meisten mag (oder möglicherweise für mehrere Antworten, aber ich muss mehr Ansehen aufbauen, wenn das der Fall ist).
quelle
Antworten:
Durch die Idee von Keith, die bestmögliche Punktzahl für jedes Wort vorab zu berechnen, gelang es mir, die Ausführungszeit auf meinem Computer auf etwa 0,7 Sekunden zu verkürzen (unter Verwendung einer Liste von 75.288 Wörtern).
Der Trick besteht darin, die zu spielenden Wortkombinationen anstelle aller ausgewählten Buchstabenkombinationen durchzugehen. Wir können alle außer einigen Wortkombinationen ignorieren (203 unter Verwendung meiner Wortliste), da sie keine höhere Punktzahl erzielen können, als wir bereits gefunden haben. Fast die gesamte Ausführungszeit wird für die Vorberechnung von Wortnoten aufgewendet.
Python 2.7:
Dies gibt die Lösung
['KNICKKNACK', 'RAZZMATAZZ', 'POLYSYLLABLES']
mit einer Punktzahl von 95 zurück. Mit den Wörtern aus Keiths Lösung, die der Wortliste hinzugefügt wurden, erhalte ich dasselbe Ergebnis wie er. Mit Thouis '"Xylopyrographie" bekomme ich['XYLOPYROGRAPHY', 'KNICKKNACKS', 'RAZZMATAZZ']
eine Punktzahl von 105.quelle
Hier ist eine Idee - Sie können vermeiden, viele Wörter zu überprüfen, indem Sie feststellen, dass die meisten Wörter schreckliche Noten haben. Angenommen, Sie haben ein ziemlich gutes Punktespiel gefunden, das Ihnen 50 Punkte einbringt. Dann muss jedes Spiel mit mehr als 50 Punkten ein Wort von mindestens ceil (51/3) = 17 Punkten haben. Daher kann jedes Wort, das unmöglich 17 Punkte generiert, ignoriert werden.
Hier ist ein Code, der die oben genannten Aufgaben ausführt. Wir berechnen die bestmögliche Punktzahl für jedes Wort im Wörterbuch und speichern sie in einem Array, das nach Punktzahl indiziert ist. Dann verwenden wir dieses Array, um nur Wörter zu überprüfen, die die erforderliche Mindestpunktzahl haben.
Die Mindestpunktzahl steigt schnell auf 100, was bedeutet, dass wir nur Wörter mit mehr als 33 Punkten berücksichtigen müssen, was einen sehr kleinen Bruchteil der Gesamtsumme darstellt (my
/usr/share/dict/words
hat 208662 gültige Wörter, von denen nur 1723 Punkte = 0,8% sind). Läuft in ca. einer halben Stunde auf meinem Rechner und generiert:quelle