Python 2 (запустити швидше, якщо запустити за допомогою Pypy)
Вважається, що майже завжди здогадуються про правильне сполучення в 10 раундів або нижче
Мій алгоритм взято з моєї відповіді на натхнення як моє хобі (див. В Ideone ). Ідея полягає у тому, щоб знайти здогад, який мінімізує кількість можливостей, залишених у гіршому випадку. Мій алгоритм нижче просто змушує його, але щоб заощадити час, він просто вибирає випадкові здогадки, якщо кількість залишених можливостей більше, ніж RANDOM_THRESHOLD
. Ви можете пограти з цим параметром, щоб прискорити роботу або побачити кращу ефективність.
Алгоритм досить повільний, в середньому 10 секунд за один пробіг, якщо запускати за допомогою Pypy (якщо використовується звичайний інтерпретатор CPython - це близько 30-х років), тому я не можу перевірити його на всіх перестановках. Але продуктивність досить хороша, після приблизно 30 тестів я не бачив жодного випадку, де б не вдалося знайти правильне сполучення в 10 раундів або нижче.
У будь-якому випадку, якщо це використовується в реальному шоу, у нього є достатньо часу до наступного раунду (тиждень?), Тому цей алгоритм можна використовувати в реальному житті = D
Тому я вважаю, що можна припустити, що в середньому це знайде правильні пари у 10 здогадах чи нижче.
Спробуйте самі. Я міг би покращити швидкість у найближчі кілька днів (EDIT: подальше вдосконалення здається складною, тому я просто залишу код таким, який є. Я намагався робити лише випадковий вибір, але навіть у size=7
такому випадку він не вдається в 3 з 5040 випадків , тому я вирішив дотримати розумніший метод). Ви можете запустити його як:
pypy are_you_the_one.py 10
Або, якщо ви просто хочете побачити, як це працює, введіть меншу кількість (щоб вона працювала швидше)
Щоб виконати повний тест (попередження: це займе дуже багато часу size
> 7), введіть від’ємне число.
Повний тест на size=7
(завершено через 2 м 32):
...
(6, 5, 4, 1, 3, 2, 0): 5 здогадів
(6, 5, 4, 2, 0, 1, 3): 5 здогадів
(6, 5, 4, 2, 0, 3, 1): 4 здогадки
(6, 5, 4, 2, 1, 0, 3): 5 здогадів
(6, 5, 4, 2, 1, 3, 0): 6 здогадів
(6, 5, 4, 2, 3, 0, 1): 6 здогадів
(6, 5, 4, 2, 3, 1, 0): 6 здогадів
(6, 5, 4, 3, 0, 1, 2): 6 здогадів
(6, 5, 4, 3, 0, 2, 1): 3 здогадки
(6, 5, 4, 3, 1, 0, 2): 7 здогадів
(6, 5, 4, 3, 1, 2, 0): 7 здогадів
(6, 5, 4, 3, 2, 0, 1): 4 здогадки
(6, 5, 4, 3, 2, 1, 0): 7 здогадів
Середня кількість: 5,05
Максимальна кількість: 7
Мінімальна кількість: 1
Кількість успіхів: 5040
Якщо RANDOM_THRESHOLD
і CLEVER_THRESHOLD
обидва встановлені на дуже високе значення (наприклад, 50000), це змусить алгоритм знайти оптимальну здогадку, яка мінімізує кількість можливостей у гіршому випадку. Це дуже повільно, але дуже потужно. Наприклад, запускаючи його, size=6
стверджує, що він може знайти правильні пари у максимум 5 раундів.
Хоча середнє значення вище, ніж використання наближення (яке в середньому становить 4,11 раунда), але це завжди вдається, навіть більше, якщо залишиться один раунд. Це ще більше посилює нашу гіпотезу про те size=10
, що коли , майже завжди слід знаходити правильні пари в 10 раундів або менше.
Результат (завершено за 3м 9с):
(5, 4, 2, 1, 0, 3): 5 здогадів
(5, 4, 2, 1, 3, 0): 5 здогадів
(5, 4, 2, 3, 0, 1): 4 здогадки
(5, 4, 2, 3, 1, 0): 4 здогадки
(5, 4, 3, 0, 1, 2): 5 здогадів
(5, 4, 3, 0, 2, 1): 5 здогадів
(5, 4, 3, 1, 0, 2): 5 здогадів
(5, 4, 3, 1, 2, 0): 5 здогадів
(5, 4, 3, 2, 0, 1): 5 здогадів
(5, 4, 3, 2, 1, 0): 5 здогадів
Середня кількість: 4,41
Максимальна кількість: 5
Мінімальна кількість: 1
Число успіхів: 720
Кодекс.
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)