ปัญหาสิบสองเหรียญ


14

พื้นหลัง

ปัญหาสิบสองเหรียญเป็นปริศนาสมดุลแบบคลาสสิคที่ใช้กันทั่วไปในการสัมภาษณ์งาน ปริศนาปรากฏตัวครั้งแรกในปีพ. ศ. 2488 และปู่ของฉันถูกพ่อของฉันวางไว้เมื่อเขาขอแต่งงานกับแม่ของฉัน! ในจิ๊กซอว์มีสิบสองเหรียญซึ่งหนึ่งในนั้นคือหนักหรือเบากว่าคนอื่น ๆ (คุณไม่ทราบว่า) ปัญหาคือการใช้เครื่องชั่งสมดุลสามครั้งเพื่อตรวจสอบเหรียญที่ไม่ซ้ำกัน ในบางรูปแบบจำเป็นต้องระบุว่าเหรียญนั้นหนักหรือเบา

งานที่นี่เกี่ยวข้องกับการแก้ปัญหาทั่วไปที่เกี่ยวข้องกับเหรียญ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 ซึ่งควรจะ<, =หรือ>แสดงให้เห็นว่าด้านซ้ายของเครื่องชั่งมีน้ำหนักเบาเดียวกันหรือหนักกว่าด้านขวา
  • หลังจากผลการชั่งน้ำหนักครั้งล่าสุดโปรแกรมควรพิมพ์หรือส่งคืนจำนวนเหรียญที่ไม่ซ้ำกัน
  • โปรแกรมไม่จำเป็นต้องจัดการอินพุตผลลัพธ์ที่ไม่สอดคล้องกันจากผู้ใช้
  • โปรแกรมที่ไม่จำเป็นต้องจัดการกับ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

เกณฑ์การให้คะแนน

รหัสที่สั้นที่สุดชนะ ใช้กฎมาตรฐาน

คำตอบ:


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ในkweighings

นี่คือฟังก์ชันทดสอบที่ฉันเขียนเพื่อให้แน่ใจว่ารหัสของฉันไม่ได้ร้องขอการชั่งน้ำหนักปลอมหรือใช้มากกว่าจำนวนการชั่งน้ำหนักที่ควรจะเป็น:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.