Python 2 (chạy nhanh hơn nếu chạy bằng Pypy)
Được tin là hầu như luôn luôn đoán đúng cặp trong 10 vòng hoặc thấp hơn
Thuật toán của tôi được lấy từ câu trả lời của tôi cho chủ mưu là sở thích của tôi (xem trong Ideone ). Ý tưởng là tìm ra dự đoán giúp giảm thiểu số lượng khả năng còn lại trong trường hợp xấu nhất. Thuật toán của tôi dưới đây chỉ đơn giản là ép buộc nó, nhưng để tiết kiệm thời gian, nó chỉ cần chọn dự đoán ngẫu nhiên nếu số khả năng còn lại lớn hơnRANDOM_THRESHOLD
. Bạn có thể chơi xung quanh với tham số này để tăng tốc mọi thứ hoặc để xem hiệu suất tốt hơn.
Thuật toán khá chậm, trung bình 10 giây cho một lần chạy nếu chạy bằng Pypy (nếu sử dụng trình thông dịch CPython bình thường thì khoảng 30 giây) vì vậy tôi không thể kiểm tra nó trên toàn bộ hoán vị. Nhưng hiệu suất khá tốt, sau khoảng 30 thử nghiệm, tôi chưa thấy trường hợp nào không thể tìm thấy cặp đôi chính xác trong 10 vòng hoặc thấp hơn.
Dù sao, nếu điều này được sử dụng trong chương trình thực tế, nó có nhiều thời gian trước vòng tiếp theo (một tuần?) Để thuật toán này có thể được sử dụng trong cuộc sống thực = D
Vì vậy, tôi nghĩ thật an toàn khi cho rằng trung bình điều này sẽ tìm thấy các cặp chính xác trong 10 lần đoán hoặc thấp hơn.
Hãy thử nó. Tôi có thể cải thiện tốc độ trong vài ngày tới (EDIT: có vẻ khó cải thiện hơn nữa, vì vậy tôi sẽ chỉ để lại mã như vậy. Tôi đã thử chỉ chọn ngẫu nhiên, nhưng ngay cả size=7
, nó đã thất bại trong 3 trong số 5040 trường hợp , vì vậy tôi quyết định giữ phương pháp thông minh hơn). Bạn có thể chạy nó dưới dạng:
pypy are_you_the_one.py 10
Hoặc, nếu bạn chỉ muốn xem nó hoạt động như thế nào, hãy nhập số nhỏ hơn (để nó chạy nhanh hơn)
Để chạy thử nghiệm đầy đủ (cảnh báo: sẽ mất rất nhiều thời gian cho size
> 7), đặt số âm.
Kiểm tra đầy đủ cho size=7
(hoàn thành trong 2m 32s):
...
(6, 5, 4, 1, 3, 2, 0): 5 lần đoán
(6, 5, 4, 2, 0, 1, 3): 5 lần đoán
(6, 5, 4, 2, 0, 3, 1): 4 lần đoán
(6, 5, 4, 2, 1, 0, 3): 5 lần đoán
(6, 5, 4, 2, 1, 3, 0): 6 lần đoán
(6, 5, 4, 2, 3, 0, 1): 6 lần đoán
(6, 5, 4, 2, 3, 1, 0): 6 lần đoán
(6, 5, 4, 3, 0, 1, 2): 6 lần đoán
(6, 5, 4, 3, 0, 2, 1): 3 lần đoán
(6, 5, 4, 3, 1, 0, 2): 7 lần đoán
(6, 5, 4, 3, 1, 2, 0): 7 lần đoán
(6, 5, 4, 3, 2, 0, 1): 4 lần đoán
(6, 5, 4, 3, 2, 1, 0): 7 lần đoán
Số lượng trung bình: 5,05
Số lượng tối đa: 7
Số lượng tối thiểu: 1
Số thành công: 5040
Nếu RANDOM_THRESHOLD
và CLEVER_THRESHOLD
cả hai đều được đặt thành một giá trị rất cao (như 50000), nó sẽ buộc thuật toán tìm ra dự đoán tối ưu giúp giảm thiểu số lượng khả năng trong trường hợp xấu nhất. Điều này rất chậm, nhưng rất mạnh mẽ. Ví dụ: chạy nó trên size=6
khẳng định rằng nó có thể tìm thấy các cặp chính xác trong tối đa 5 vòng.
Mặc dù trung bình cao hơn so với việc sử dụng xấp xỉ (trung bình là 4,11 vòng), nhưng nó luôn thành công, thậm chí nhiều hơn với một vòng còn lại để dự phòng. Điều này càng củng cố giả thuyết của chúng tôi rằng khi nào size=10
, nó hầu như luôn luôn phải tìm ra các cặp chính xác trong 10 vòng hoặc ít hơn.
Kết quả (hoàn thành trong 3 giây 9 giây):
(5, 4, 2, 1, 0, 3): 5 lần đoán
(5, 4, 2, 1, 3, 0): 5 lần đoán
(5, 4, 2, 3, 0, 1): 4 lần đoán
(5, 4, 2, 3, 1, 0): 4 lần đoán
(5, 4, 3, 0, 1, 2): 5 lần đoán
(5, 4, 3, 0, 2, 1): 5 lần đoán
(5, 4, 3, 1, 0, 2): 5 lần đoán
(5, 4, 3, 1, 2, 0): 5 lần đoán
(5, 4, 3, 2, 0, 1): 5 lần đoán
(5, 4, 3, 2, 1, 0): 5 lần đoán
Số lượng trung bình: 4,41
Số lượng tối đa: 5
Số lượng tối thiểu: 1
Số thành công: 720
Mật mã.
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)