Python 3, điểm = 4/3 = 1.33 Cầu (N = 4) điểm = 1,4 (N = 7)
Cập nhật: đã thực hiện tìm kiếm brute-force trong bộ giải "tĩnh" và có kết quả mới
Tôi nghĩ rằng nó có thể được cải thiện hơn nữa bằng cách tìm kiếm các bộ giải động, có thể sử dụng kết quả trọng số cho các quyết định tiếp theo.
Dưới đây là mã Python tìm kiếm thông qua tất cả các bộ giải tĩnh cho các n
giá trị nhỏ (các bộ giải này luôn cân nhắc cùng một bộ đồng xu, do đó tên "tĩnh") và xác định số bước trong trường hợp xấu nhất của chúng bằng cách kiểm tra xem kết quả đo của chúng chỉ cho phép một đồng xu phù hợp thiết lập trong mọi trường hợp. Ngoài ra, nó theo dõi điểm số tốt nhất được tìm thấy cho đến nay và người giải mận sớm đã cho thấy rằng họ chắc chắn là tồi tệ hơn so với những người được tìm thấy trước đó. Đây là một tối ưu hóa quan trọng, nếu không tôi không thể chờ kết quả này với n
= 7. (Nhưng rõ ràng nó vẫn chưa được tối ưu hóa tốt)
Hãy đặt câu hỏi nếu không rõ nó hoạt động như thế nào
#!/usr/bin/env python3
import itertools
from functools import partial
def get_all_possible_coinsets(n):
return tuple(itertools.product(*itertools.repeat((-1, 1), n)))
def weigh(coinset, indexes_to_weigh):
return sum(coinset[x] for x in indexes_to_weigh)
# made_measurements: [(indexes, weight)]
def filter_by_measurements(coinsets, made_measurements):
return filter(lambda cs: all(w == weigh(cs, indexes) for indexes, w in made_measurements), coinsets)
class Position(object):
def __init__(self, all_coinsets, coinset, made_measurements=()):
self.all_coinsets = all_coinsets
self.made_measurements = made_measurements
self.coins = coinset
def possible_coinsets(self):
return tuple(filter_by_measurements(self.all_coinsets, self.made_measurements))
def is_final(self):
possible_coinsets = self.possible_coinsets()
return (len(possible_coinsets) == 1) and possible_coinsets[0] == self.coins
def move(self, measurement_indexes):
measure_result = (measurement_indexes, weigh(self.coins, measurement_indexes))
return Position(self.all_coinsets, self.coins, self.made_measurements + (measure_result,))
def get_all_start_positions(coinsets):
for cs in coinsets:
yield Position(coinsets, cs)
def average(xs):
return sum(xs) / len(xs)
class StaticSolver(object):
def __init__(self, measurements):
self.measurements = measurements
def choose_move(self, position: Position):
index = len(position.made_measurements)
return self.measurements[index]
def __str__(self, *args, **kwargs):
return 'StaticSolver({})'.format(', '.join(map(lambda x: '{' + ','.join(map(str, x)) + '}', self.measurements)))
def __repr__(self):
return str(self)
class FailedSolver(Exception):
pass
def test_solvers(solvers, start_positions, max_steps):
for solver in solvers:
try:
test_results = tuple(map(partial(test_solver, solver=solver, max_steps=max_steps), start_positions))
yield (solver, max(test_results))
except FailedSolver:
continue
def all_measurement_starts(n):
for i in range(1, n + 1):
yield from itertools.combinations(range(n), i)
def next_measurement(n, measurement, include_zero):
shifted = filter(lambda x: x < n, map(lambda x: x + 1, measurement))
if include_zero:
return tuple(itertools.chain((0,), shifted))
else:
return tuple(shifted)
def make_measurement_sequence(n, start, zero_decisions):
yield start
m = start
for zero_decision in zero_decisions:
m = next_measurement(n, m, zero_decision)
yield m
def measurement_sequences_from_start(n, start, max_steps):
continuations = itertools.product(*itertools.repeat((True, False), max_steps - 1))
for c in continuations:
yield tuple(make_measurement_sequence(n, start, c))
def all_measurement_sequences(n, max_steps):
starts = all_measurement_starts(n)
for start in starts:
yield from measurement_sequences_from_start(n, start, max_steps)
def all_static_solvers(n, max_steps):
return map(StaticSolver, all_measurement_sequences(n, max_steps))
def main():
best_score = 1.0
for n in range(1, 11):
print('Searching with N = {}:'.format(n))
coinsets = get_all_possible_coinsets(n)
start_positions = tuple(get_all_start_positions(coinsets))
# we are not interested in solvers with worst case number of steps bigger than this
max_steps = int(n / best_score)
solvers = all_static_solvers(n, max_steps)
succeeded_solvers = test_solvers(solvers, start_positions, max_steps)
try:
best = min(succeeded_solvers, key=lambda x: x[1])
except ValueError: # no successful solvers
continue
score = n / best[1]
best_score = max(score, best_score)
print('{}, score = {}/{} = {}'.format(best, n, best[1], score))
print('That\'s all!')
def test_solver(start_position: Position, solver, max_steps):
p = start_position
steps = 0
try:
while not p.is_final():
steps += 1
if steps > max_steps:
raise FailedSolver
p = p.move(solver.choose_move(p))
return steps
except IndexError: # solution was not found after given steps — this solver failed to beat score 1
raise FailedSolver
if __name__ == '__main__':
main()
Đầu ra:
Searching with N = 1:
(StaticSolver({0}), 1), score = 1/1 = 1.0
Searching with N = 2:
(StaticSolver({0}, {0,1}), 2), score = 2/2 = 1.0
Searching with N = 3:
(StaticSolver({0}, {0,1}, {0,1,2}), 3), score = 3/3 = 1.0
Searching with N = 4:
(StaticSolver({0,1}, {1,2}, {0,2,3}, {0,1,3}), 3), score = 4/3 = 1.3333333333333333
Searching with N = 5:
Searching with N = 6:
Searching with N = 7:
(StaticSolver({0,2}, {0,1,3}, {0,1,2,4}, {1,2,3,5}, {0,2,3,4,6}), 5), score = 7/5 = 1.4
Searching with N = 8:
Searching with N = 9:
(I gave up waiting at this moment)
Dòng này mở
(StaticSolver({0,2}, {0,1,3}, {0,1,2,4}, {1,2,3,5}, {0,2,3,4,6}), 5), score = 7/5 = 1.4
ra bộ giải tốt nhất được tìm thấy. Các số trong {}
dấu ngoặc nhọn là chỉ số của đồng xu để đặt trên thiết bị đo trọng lượng ở mỗi bước.