Python 2 (exécutez plus rapidement si vous utilisez Pypy)
On croit presque toujours deviner le bon appariement en 10 tours ou moins
Mon algorithme est tiré de ma réponse pour le cerveau comme mon hobby (voir dans Ideone ). L'idée est de trouver la supposition qui minimise le nombre de possibilités restantes dans le pire des cas. Mon algorithme ci-dessous le force simplement brutalement, mais pour gagner du temps, il suffit de choisir au hasard si le nombre de possibilités restantes est supérieur à RANDOM_THRESHOLD
. Vous pouvez jouer avec ce paramètre pour accélérer les choses ou pour voir de meilleures performances.
L'algorithme est assez lent, en moyenne 10 secondes pour une exécution s'il est exécuté à l'aide de Pypy (si vous utilisez un interpréteur CPython normal, c'est environ 30 secondes), je ne peux donc pas le tester sur toutes les permutations. Mais les performances sont assez bonnes, après environ 30 tests, je n'ai vu aucun cas où il ne pouvait pas trouver le bon appariement en 10 tours ou moins.
Quoi qu'il en soit, si cela est utilisé dans une émission réelle, il a beaucoup de temps avant le prochain tour (une semaine?), Donc cet algorithme peut être utilisé dans la vraie vie = D
Je pense donc qu'il est prudent de supposer qu'en moyenne, cela trouvera les bons appariements en 10 suppositions ou moins.
Essayez-le vous-même. Je pourrais améliorer la vitesse dans les prochains jours (EDIT: il semble difficile de continuer à s'améliorer, je vais donc laisser le code tel size=7
quel . J'ai essayé de faire un choix aléatoire, mais même à , il échoue dans 3 des 5040 cas , j'ai donc décidé de garder la méthode la plus intelligente). Vous pouvez l'exécuter comme:
pypy are_you_the_one.py 10
Ou, si vous voulez simplement voir comment cela fonctionne, entrez un nombre plus petit (pour qu'il s'exécute plus rapidement)
Pour exécuter un test complet (avertissement: cela prendra très longtemps pour size
> 7), mettez un nombre négatif.
Test complet pour size=7
(terminé en 2m 32s):
...
(6, 5, 4, 1, 3, 2, 0): 5 suppositions
(6, 5, 4, 2, 0, 1, 3): 5 suppositions
(6, 5, 4, 2, 0, 3, 1): 4 suppositions
(6, 5, 4, 2, 1, 0, 3): 5 suppositions
(6, 5, 4, 2, 1, 3, 0): 6 suppositions
(6, 5, 4, 2, 3, 0, 1): 6 suppositions
(6, 5, 4, 2, 3, 1, 0): 6 suppositions
(6, 5, 4, 3, 0, 1, 2): 6 suppositions
(6, 5, 4, 3, 0, 2, 1): 3 suppositions
(6, 5, 4, 3, 1, 0, 2): 7 suppositions
(6, 5, 4, 3, 1, 2, 0): 7 suppositions
(6, 5, 4, 3, 2, 0, 1): 4 suppositions
(6, 5, 4, 3, 2, 1, 0): 7 suppositions
Nombre moyen: 5,05
Nombre max: 7
Nombre minimal: 1
Succès num: 5040
Si RANDOM_THRESHOLD
et CLEVER_THRESHOLD
sont tous deux définis sur une valeur très élevée (comme 50000), cela forcera l'algorithme à trouver la supposition optimale qui minimise le nombre de possibilités dans le pire des cas. C'est très lent, mais très puissant. Par exemple, l'exécuter sur size=6
affirme qu'il peut trouver les bons appariements en 5 tours maximum.
Bien que la moyenne soit plus élevée par rapport à l'utilisation de l'approximation (qui est de 4,11 tours en moyenne), elle réussit toujours, d'autant plus qu'il reste un tour à revendre. Cela renforce encore notre hypothèse selon laquelle size=10
, il devrait presque toujours trouver les bons appariements en 10 tours ou moins.
Le résultat (complété en 3m 9s):
(5, 4, 2, 1, 0, 3): 5 suppositions
(5, 4, 2, 1, 3, 0): 5 suppositions
(5, 4, 2, 3, 0, 1): 4 suppositions
(5, 4, 2, 3, 1, 0): 4 suppositions
(5, 4, 3, 0, 1, 2): 5 suppositions
(5, 4, 3, 0, 2, 1): 5 suppositions
(5, 4, 3, 1, 0, 2): 5 suppositions
(5, 4, 3, 1, 2, 0): 5 suppositions
(5, 4, 3, 2, 0, 1): 5 suppositions
(5, 4, 3, 2, 1, 0): 5 suppositions
Nombre moyen: 4,41
Nombre max: 5
Nombre minimal: 1
Succès numérique: 720
Le 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)