Python 2 (correre più veloce se eseguito usando Pypy)
Si ritiene che indovini quasi sempre l'abbinamento corretto in 10 round o inferiore
Il mio algoritmo è tratto dalla mia risposta per mastermind come mio hobby (vedi in Ideone ). L'idea è quella di trovare l'ipotesi che minimizzi il numero di possibilità rimaste nel caso peggiore. Il mio algoritmo sotto brutale lo costringe, ma per risparmiare tempo, seleziona ipotesi casuali se il numero di possibilità rimaste è maggiore di RANDOM_THRESHOLD
. Puoi giocare con questo parametro per velocizzare le cose o vedere prestazioni migliori.
L'algoritmo è piuttosto lento, in media 10 secondi per una corsa se eseguito usando Pypy (se usando il normale interprete CPython è di circa 30 secondi) quindi non posso testarlo su tutte le permutazioni. Ma le prestazioni sono abbastanza buone, dopo circa 30 test non ho visto nessun caso in cui non riesca a trovare l'accoppiamento corretto in 10 round o meno.
Ad ogni modo, se questo è usato nello spettacolo della vita reale, ha un sacco di tempo prima del prossimo round (una settimana?), Quindi questo algoritmo può essere usato nella vita reale = D
Quindi penso che sia sicuro supporre che in media questo troverà gli abbinamenti corretti in 10 ipotesi o meno.
Provate voi stessi. Potrei migliorare la velocità nei prossimi giorni (EDIT: sembra difficile migliorare ulteriormente, quindi lascerò il codice così com'è. Ho provato a fare solo pick casuali, ma anche a size=7
, fallisce in 3 dei 5040 casi , così ho deciso di mantenere il metodo più intelligente). Puoi eseguirlo come:
pypy are_you_the_one.py 10
Oppure, se vuoi solo vedere come funziona, inserisci un numero più piccolo (in modo che funzioni più velocemente)
Per eseguire un test completo (avviso: ci vorrà molto tempo per size
> 7), inserire un numero negativo.
Test completo per size=7
(completato in 2m 32s):
...
(6, 5, 4, 1, 3, 2, 0): 5 ipotesi
(6, 5, 4, 2, 0, 1, 3): 5 ipotesi
(6, 5, 4, 2, 0, 3, 1): 4 ipotesi
(6, 5, 4, 2, 1, 0, 3): 5 ipotesi
(6, 5, 4, 2, 1, 3, 0): 6 ipotesi
(6, 5, 4, 2, 3, 0, 1): 6 ipotesi
(6, 5, 4, 2, 3, 1, 0): 6 ipotesi
(6, 5, 4, 3, 0, 1, 2): 6 ipotesi
(6, 5, 4, 3, 0, 2, 1): 3 ipotesi
(6, 5, 4, 3, 1, 0, 2): 7 ipotesi
(6, 5, 4, 3, 1, 2, 0): 7 ipotesi
(6, 5, 4, 3, 2, 0, 1): 4 ipotesi
(6, 5, 4, 3, 2, 1, 0): 7 ipotesi
Conteggio medio: 5,05
Conteggio massimo: 7
Conteggio minimo: 1
Numero successo: 5040
Se RANDOM_THRESHOLD
e CLEVER_THRESHOLD
entrambi sono impostati su un valore molto alto (come 50000), costringerà l'algoritmo a trovare l'ipotesi ottimale che minimizza il numero di possibilità nel caso peggiore. Questo è molto lento, ma molto potente. Ad esempio, eseguendolo su size=6
asserisce che può trovare gli accoppiamenti corretti in massimo 5 round.
Sebbene la media sia più alta rispetto all'utilizzo dell'approssimazione (che è in media 4,11 round), ma ha sempre successo, ancora di più con un round rimasto a disposizione. Ciò rafforza ulteriormente la nostra ipotesi che size=10
, quando , dovrebbe quasi sempre trovare gli accoppiamenti corretti in 10 round o meno.
Il risultato (completato in 3m 9s):
(5, 4, 2, 1, 0, 3): 5 ipotesi
(5, 4, 2, 1, 3, 0): 5 ipotesi
(5, 4, 2, 3, 0, 1): 4 ipotesi
(5, 4, 2, 3, 1, 0): 4 ipotesi
(5, 4, 3, 0, 1, 2): 5 ipotesi
(5, 4, 3, 0, 2, 1): 5 ipotesi
(5, 4, 3, 1, 0, 2): 5 ipotesi
(5, 4, 3, 1, 2, 0): 5 ipotesi
(5, 4, 3, 2, 0, 1): 5 ipotesi
(5, 4, 3, 2, 1, 0): 5 ipotesi
Conteggio medio: 4,41
Conteggio massimo: 5
Conteggio minimo: 1
Numero successo: 720
Il codice.
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)