Проблема двенадцати монет


14

Фон

Проблема из двенадцати монет - это классическая головоломка с балансом, обычно используемая в собеседованиях. Загадка впервые появилась в 1945 году и была поставлена ​​моему отцу моим дедом, когда он попросил жениться на моей матери! В загадке двенадцать монет, одна из которых тяжелее или легче других (вы не знаете, какая именно). Проблема состоит в том, чтобы использовать весы трижды, чтобы определить уникальную монету. В некоторых вариантах также необходимо определить, является ли монета тяжелее или легче.

Задача здесь включает в себя решение общей проблемы, связанной с n монетами, с использованием наименьшего количества возможных взвешиваний в худшем случае. Нет необходимости определять, является ли монета тяжелее или легче, только какая она есть. Кроме того, у вас нет доступа к каким-либо дополнительным монетам за пределами данного набора (что, как ни странно, имеет значение).

Оказывается, что k взвешиваний достаточно для (3 ^ k-1) / 2 монет (так что 4 взвешивания в этом варианте могут фактически обработать 13 монет). Кроме того (что удивительно), можно (но не обязательно здесь) заранее выбирать полный набор взвешиваний, вместо того, чтобы будущие взвешивания зависели от прошлых результатов. Для описания двух возможных решений, см. Эту статью и этот ответ Quora .

задача

Напишите функцию или программу, принимая целое число n в качестве входных данных через STDIN, аргумент командной строки или аргумент функции, что решает проблему для n монет, используя наименьшее количество возможных взвешиваний в худшем случае. Программа должна:

  • Распечатайте взвешивания в STDOUT в формате, 1,2,3-4,5,6чтобы указать списки монет на каждой стороне шкалы. Любые не взвешенные монеты не должны упоминаться. Монеты неявно пронумерованы от 1 до n и не должны быть напечатаны в числовом порядке ( 2,1-3,4то же самое, что и для 1,2-3,4).
  • После каждого взвешивания программа должна ожидать ввода через STDIN, который должен быть <, =или >, указывая, является ли левая сторона весов светлее, такой же или тяжелее правой стороны.
  • После последнего результата взвешивания программа должна напечатать или вернуть номер уникальной монеты.
  • Программа не должна обрабатывать противоречивые результаты ввода от пользователя.
  • Программа не должна обращаться с п меньше 3.

Пример выходов

>> 3
1-2
>> =
1-3
>> <
3

# using Quora algorithm
>> 13
1,2,3,4-5,6,7,8
>> <
1,2,5-3,4,6
>> >
3-4
>> <
3

# using paper algorithm
>> 13
1,2,3,4-5,6,7,8
>> <
2,6,7,9-3,8,10,11
>> >
6,8,10,12-4,5,7,11
>> =
3

счет

Самый короткий код выигрывает. Стандартные правила применяются.

Ответы:


2

Python 3: 497 байт

I=lambda a,b:input(",".join(a)+"-"+",".join(b)+"\n>> ")
def A(a,b):
 l,L=len(a),len(b)
 if l+L==1:return(a or b)[0]
 m=(2*l+1-L)//3;M=m+L-l;x,y,X,Y=a[:m],a[m:2*m],b[:M],b[M:2*M];r=I(x+X,y+Y);return A(a[2*m:],b[2*M:])if r=="="else A(x,Y)if r=="<"else A(y,X)
def B(a,n=[]):
 if len(a)==1:return a[0]
 m=len(n);l=(len(a)+1+m)//3;x,y,z=a[:l],a[l:2*l-m],a[2*l-m:];r=I(x,y+n);return B(z,a[:1])if r=="="else A(x+z[:1-m],y)if r=="<"else A(y+z[:1-m],x)
print(B(list(map(str,range(1,int(input("N= "))+1)))))

Я подозреваю, что это может быть уменьшено немного больше, но я не вижу больше очевидных мест (после примерно 5 различных версий каждой функции).

Код реализует слегка модифицированную версию алгоритма из этой страницы, используя три функции. IФункция делает IO (печать параметров и возвращающийся ответ пользователя). Функции Aи Bреализуют основную часть алгоритма. Aберет два списка, которые различаются по размеру ровно на один элемент (хотя любой из них может быть большим): одна монета aможет быть легче, чем обычно, или одна монета bможет быть тяжелее. Bвыполняет двойную обязанность Требуется один список монет aи, опционально, второй список с одной монетой, которая, как известно, имеет правильный вес. Поведение при округлении длины должно быть разным в двух случаях, что не вызывает конца головной боли.

Две функции алгоритма могут найти необычно взвешенную монету в kвзвешивании с учетом входных данных до следующих размеров:

  • A: 3^kвсего монет, разделенных на два списка (3^k-1)/2и (3^k+1)/2.
  • B: (3^k + 1)/2монеты, если поставлена ​​монета хорошего качества, в (3^k - 1)/2 противном случае.

Вопрос , поставленный здесь указывает , что у нас нет каких - либо заведомо хорошие монеты в начале, так что мы можем решить найти плохую монету в наборе (3^k - 1)/2в kвзвешиваний.

Вот тестовая функция, которую я написал, чтобы убедиться, что мой код не запрашивает фиктивные взвешивания или использует больше, чем предполагалось:

def test(n):
    global I
    orig_I = I
    try:
        for x in range(3,n+1):
            max_count = 0
            for y in range(x*2):
                count = 0
                def I(a, b):
                    assert len(a) == len(b), "{} not the same length as {}".format(a,b)
                    nonlocal count
                    count += 1
                    if y//2 in a: return "<"if y%2 else ">"
                    if y//2 in b: return ">"if y%2 else "<"
                    return "="
                assert B(list(range(x)))==y//2, "{} {} not found in size {}".format(['heavy','light'][y%2], y//2+1, x)
                if count > max_count:
                    max_count = count
            print(x, max_count)
    finally:
        I = orig_I

Это распечатывает наихудшее количество взвешиваний для данного набора после тестирования с каждой комбинацией монеты и плохого веса (тяжелого или легкого).

Вот тестовый вывод для наборов до 125:

>>> test(150)
3 2
4 2
5 3
6 3
7 3
8 3
9 3
10 3
11 3
12 3
13 3
14 4
15 4
16 4
17 4
18 4
19 4
20 4
21 4
22 4
23 4
24 4
25 4
26 4
27 4
28 4
29 4
30 4
31 4
32 4
33 4
34 4
35 4
36 4
37 4
38 4
39 4
40 4
41 5
42 5
43 5
44 5
45 5
46 5
47 5
48 5
49 5
50 5
51 5
52 5
53 5
54 5
55 5
56 5
57 5
58 5
59 5
60 5
61 5
62 5
63 5
64 5
65 5
66 5
67 5
68 5
69 5
70 5
71 5
72 5
73 5
74 5
75 5
76 5
77 5
78 5
79 5
80 5
81 5
82 5
83 5
84 5
85 5
86 5
87 5
88 5
89 5
90 5
91 5
92 5
93 5
94 5
95 5
96 5
97 5
98 5
99 5
100 5
101 5
102 5
103 5
104 5
105 5
106 5
107 5
108 5
109 5
110 5
111 5
112 5
113 5
114 5
115 5
116 5
117 5
118 5
119 5
120 5
121 5
122 6
123 6
124 6
125 6

Точки останова находятся именно там, где вы ожидаете, между (3^k - 1)/2и (3^k + 1)/2.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.