Python 2 (schneller laufen, wenn Pypy verwendet wird)
Es wird davon ausgegangen, dass fast immer die richtige Paarung in 10 Runden oder weniger erraten wird
Mein Algorithmus wird von meiner Antwort für Mastermind als mein Hobby genommen (siehe in Ideone ). Die Idee ist, die Vermutung zu finden, die die Anzahl der im schlimmsten Fall verbleibenden Möglichkeiten minimiert. Mein unten stehender Algorithmus erzwingt es einfach, aber um Zeit zu sparen, wählt er einfach eine Zufallsrate, wenn die Anzahl der verbleibenden Möglichkeiten größer als ist RANDOM_THRESHOLD
. Mit diesem Parameter können Sie herumspielen, um Dinge zu beschleunigen oder eine bessere Leistung zu erzielen.
Der Algorithmus ist ziemlich langsam, im Durchschnitt 10s für einen Lauf, wenn er mit Pypy ausgeführt wird (wenn ein normaler CPython-Interpreter verwendet wird, sind es ungefähr 30s), daher kann ich ihn nicht auf allen Permutationen testen. Aber die Leistung ist ziemlich gut, nach ungefähr 30 Tests habe ich noch keinen Fall gesehen, bei dem es in 10 Runden oder weniger nicht gelingt, die richtige Paarung zu finden.
Wie auch immer, wenn dies in der realen Show verwendet wird, hat es genügend Zeit vor der nächsten Runde (eine Woche?), So dass dieser Algorithmus im realen Leben verwendet werden kann = D
Ich bin also der Meinung, dass man davon ausgehen kann, dass dies im Durchschnitt die richtigen Paarungen in 10 oder weniger Vermutungen ergibt.
Versuch es selber. Ich könnte die Geschwindigkeit in den nächsten Tagen verbessern (BEARBEITEN: Es scheint schwierig zu sein, sich weiter zu verbessern, also lasse ich den Code einfach so, wie er ist. Ich habe versucht, nur zufällige Auswahl zu machen, aber selbst bei size=7
scheitert er in 3 der 5040 Fälle Also habe ich beschlossen, die clevere Methode beizubehalten. Sie können es ausführen als:
pypy are_you_the_one.py 10
Oder, wenn Sie nur sehen möchten, wie es funktioniert, geben Sie eine kleinere Zahl ein (damit es schneller läuft)
Geben Sie size
eine negative Zahl ein, um einen vollständigen Test durchzuführen (Warnung: > 7 dauert sehr lange ).
Voller Test für size=7
(abgeschlossen in 2m 32s):
...
(6, 5, 4, 1, 3, 2, 0): 5 Vermutungen
(6, 5, 4, 2, 0, 1, 3): 5 Vermutungen
(6, 5, 4, 2, 0, 3, 1): 4 Vermutungen
(6, 5, 4, 2, 1, 0, 3): 5 Vermutungen
(6, 5, 4, 2, 1, 3, 0): 6 Vermutungen
(6, 5, 4, 2, 3, 0, 1): 6 Vermutungen
(6, 5, 4, 2, 3, 1, 0): 6 Vermutungen
(6, 5, 4, 3, 0, 1, 2): 6 Vermutungen
(6, 5, 4, 3, 0, 2, 1): 3 Vermutungen
(6, 5, 4, 3, 1, 0, 2): 7 Vermutungen
(6, 5, 4, 3, 1, 2, 0): 7 Vermutungen
(6, 5, 4, 3, 2, 0, 1): 4 Vermutungen
(6, 5, 4, 3, 2, 1, 0): 7 Vermutungen
Durchschnittliche Anzahl: 5,05
Maximale Anzahl: 7
Mindestanzahl: 1
Num Erfolg: 5040
Wenn RANDOM_THRESHOLD
und CLEVER_THRESHOLD
auf einen sehr hohen Wert gesetzt sind (wie 50000), wird der Algorithmus gezwungen, die optimale Schätzung zu finden, die die Anzahl der Möglichkeiten im schlimmsten Fall minimiert. Das ist sehr langsam, aber sehr mächtig. Wenn Sie es beispielsweise mit ausführen, wird davon size=6
ausgegangen, dass es in maximal 5 Runden die richtigen Paarungen finden kann.
Der Durchschnitt ist zwar höher als bei Verwendung der Näherung (die im Durchschnitt 4,11 Runden beträgt), aber es gelingt immer, noch mehr, wenn noch eine Runde übrig ist. Dies stärkt unsere Hypothese, dass size=10
fast immer in 10 Runden oder weniger die richtigen Paarungen gefunden werden sollten.
Das Ergebnis (abgeschlossen in 3m 9s):
(5, 4, 2, 1, 0, 3): 5 Vermutungen
(5, 4, 2, 1, 3, 0): 5 Vermutungen
(5, 4, 2, 3, 0, 1): 4 Vermutungen
(5, 4, 2, 3, 1, 0): 4 Vermutungen
(5, 4, 3, 0, 1, 2): 5 Vermutungen
(5, 4, 3, 0, 2, 1): 5 Vermutungen
(5, 4, 3, 1, 0, 2): 5 Vermutungen
(5, 4, 3, 1, 2, 0): 5 Vermutungen
(5, 4, 3, 2, 0, 1): 5 Vermutungen
(5, 4, 3, 2, 1, 0): 5 Vermutungen
Durchschnittliche Anzahl: 4.41
Maximale Anzahl: 5
Mindestanzahl: 1
Anzahl der Erfolge: 720
Der Code.
from itertools import permutations, combinations
import random, sys
from collections import Counter
INTERACTIVE = False
ORIG_PERMS = []
RANDOM_THRESHOLD = 100
CLEVER_THRESHOLD = 0
class Unbuffered():
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
self.stream.getattr(attr)
sys.stdout = Unbuffered(sys.stdout)
def init(size):
global ORIG_PERMS
ORIG_PERMS = list(permutations(range(size)))
def evaluate(solution, guess):
if len(guess) == len(solution):
cor = 0
for sol, gss in zip(solution, guess):
if sol == gss:
cor += 1
return cor
else:
return 1 if solution[guess[0]] == guess[1] else 0
def remove_perms(perms, evaluation, guess):
return [perm for perm in perms if evaluate(perm, guess)==evaluation]
def guess_one(possible_perms, guessed_all, count):
if count == 1:
return (0,0)
pairs = Counter()
for perm in possible_perms:
for pair in enumerate(perm):
pairs[pair] += 1
perm_cnt = len(possible_perms)
return sorted(pairs.items(), key=lambda x: (abs(perm_cnt-x[1]) if x[1]<perm_cnt else perm_cnt,x[0]) )[0][0]
def guess_all(possible_perms, guessed_all, count):
size = len(possible_perms[0])
if count == 1:
fact = 1
for i in range(2, size):
fact *= i
if len(possible_perms) == fact:
return tuple(range(size))
else:
return tuple([1,0]+range(2,size))
if len(possible_perms) == 1:
return possible_perms[0]
if count < size and len(possible_perms) > RANDOM_THRESHOLD:
return possible_perms[random.randint(0, len(possible_perms)-1)]
elif count == size or len(possible_perms) > CLEVER_THRESHOLD:
(_, next_guess) = min((max(((len(remove_perms(possible_perms, evaluation, next_guess)), next_guess) for evaluation in range(len(next_guess))), key=lambda x: x[0])
for next_guess in possible_perms if next_guess not in guessed_all), key=lambda x: x[0])
return next_guess
else:
(_, next_guess) = min((max(((len(remove_perms(possible_perms, evaluation, next_guess)), next_guess) for evaluation in range(len(next_guess))), key=lambda x: x[0])
for next_guess in ORIG_PERMS if next_guess not in guessed_all), key=lambda x: x[0])
return next_guess
def main(size=4):
if size < 0:
size = -size
init(size)
counts = []
for solution in ORIG_PERMS:
count = run_one(solution, False)
counts.append(count)
print '%s: %d guesses' % (solution, count)
sum_count = float(sum(counts))
print 'Average count: %.2f' % (sum_count/len(counts))
print 'Max count : %d' % max(counts)
print 'Min count : %d' % min(counts)
print 'Num success : %d' % sum(1 for count in counts if count <= size)
else:
init(size)
solution = ORIG_PERMS[random.randint(0,len(ORIG_PERMS)-1)]
run_one(solution, True)
def run_one(solution, should_print):
if should_print:
print solution
size = len(solution)
cur_guess = None
possible_perms = list(ORIG_PERMS)
count = 0
guessed_one = []
guessed_all = []
while True:
count += 1
# Round A, guess one pair
if should_print:
print 'Round %dA' % count
if should_print:
print 'Num of possibilities: %d' % len(possible_perms)
cur_guess = guess_one(possible_perms, guessed_all, count)
if should_print:
print 'Guess: %s' % str(cur_guess)
if INTERACTIVE:
evaluation = int(raw_input('Number of correct pairs: '))
else:
evaluation = evaluate(solution, cur_guess)
if should_print:
print 'Evaluation: %s' % str(evaluation)
possible_perms = remove_perms(possible_perms, evaluation, cur_guess)
# Round B, guess all pairs
if should_print:
print 'Round %dB' % count
print 'Num of possibilities: %d' % len(possible_perms)
cur_guess = guess_all(possible_perms, guessed_all, count)
if should_print:
print 'Guess: %s' % str(cur_guess)
guessed_all.append(cur_guess)
if INTERACTIVE:
evaluation = int(raw_input('Number of correct pairs: '))
else:
evaluation = evaluate(solution, cur_guess)
if should_print: print 'Evaluation: %s' % str(evaluation)
if evaluation == size:
if should_print:
print 'Found %s in %d guesses' % (str(cur_guess), count)
else:
return count
break
possible_perms = remove_perms(possible_perms, evaluation, cur_guess)
if __name__=='__main__':
size = 4
if len(sys.argv) >= 2:
size = int(sys.argv[1])
if len(sys.argv) >= 3:
INTERACTIVE = bool(int(sys.argv[2]))
main(size)