十二硬币问题


14

背景

十二硬币问题是工作面试中常用的经典平衡难题。这个难题第一次出现在1945年,当我祖父要求嫁给我的母亲时,就把它摆给了我父亲!拼图中有十二个硬币,其中一个比其他硬币重或更轻(您不知道哪个)。问题是要使用三次天平来确定唯一硬币。在某些变型中,还需要识别硬币是较重还是较轻。

这里的任务涉及解决涉及n一般问题硬币,在最坏的情况下使用尽可能少的称量。不必识别硬币是较重还是较轻,只需识别它是哪一个。此外,您无权访问给定集合之外的任何其他硬币(奇怪的是,这有所作为)。

事实证明,k个称重足以容纳多达(3 ^ k-1)/ 2个硬币(因此,此变体中的4个称重实际上可以处理13个硬币)。此外(令人惊讶的是),可以(但不是必需)预先选择整套称量,而不用将来的称量取决于过去的结果。有关两种可能的解决方案的说明,请参见本文Quora答案

任务

编写一个函数或程序,通过STDIN,命令行参数或函数参数将整数n作为输入,这可以解决n个硬币的问题,并在最坏的情况下使用最少的权重。该程序应:

  • 以格式1,2,3-4,5,6将称量结果打印到STDOUT上,以指示秤两边的硬币列表。任何未称重的硬币都不应提及。硬币从1到n隐式编号,不需要按数字顺序打印(因此2,1-3,4与相同1,2-3,4)。
  • 之后的每个称重程序应等待经由STDIN的输入,这应该是<=>,指示刻度的左侧是否比右侧打火机,同样的,或更重。
  • 在最后一次称量结果之后,程序应打印或返回唯一硬币的编号。
  • 该程序无需处理来自用户的不一致的结果输入。
  • 程序不需要处理n小于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

计分

最短的代码获胜。适用标准规则。

Answers:


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(打印选项并返回用户的响应)。该AB功能实现的主要算法。A取两个大小完全相差一个元素的列表(尽管任一列表可以是较大的元素):放入一个硬币a可能比普通硬币轻,或者放入一个硬币b较重。B承担双重责任。它需要一张硬币清单,a也可以选择第二张清单,其中包含一个已知重量正确的硬币。两种情况下的四舍五入行为需要有所不同,这不会导致头痛。

k给定以下输入的最大大小,这两个算法功能可以找到称量中的异常加权硬币:

  • A3^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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.