Masalah dua belas koin

14

Latar Belakang

Masalah dua belas koin adalah puzzle keseimbangan klasik yang biasa digunakan dalam wawancara kerja. Teka-teki pertama kali muncul pada tahun 1945 dan diajukan kepada ayah saya oleh kakek saya ketika dia meminta untuk menikahi ibu saya! Dalam puzzle ada dua belas koin, yang salah satu lebih berat atau lebih ringan dari yang lain (Anda tidak tahu yang mana). Masalahnya adalah menggunakan timbangan timbangan tiga kali untuk menentukan koin unik. Dalam beberapa variasi, perlu juga untuk mengidentifikasi apakah koin lebih berat atau lebih ringan.

Tugas di sini melibatkan penyelesaian masalah umum yang melibatkan n koin, menggunakan penimbangan sesedikit mungkin dalam kasus terburuk. Tidak perlu mengidentifikasi apakah koin itu lebih berat atau lebih ringan, hanya koin yang mana. Selain itu, Anda tidak memiliki akses ke koin tambahan apa pun di luar set yang diberikan (yang, anehnya, membuat perbedaan).

Ternyata bobot k cukup untuk (3 ^ k-1) / 2 koin (jadi 4 bobot dalam variasi ini benar-benar dapat menangani 13 koin). Selanjutnya (dan secara mengejutkan), dimungkinkan (tetapi tidak diharuskan di sini) untuk memilih set lengkap penimbangan di muka, daripada memiliki penimbangan di masa depan tergantung pada hasil masa lalu. Untuk deskripsi dari dua kemungkinan solusi, lihat makalah ini dan jawaban Quora ini .

Tugas

Tulis fungsi atau program, ambil bilangan bulat n sebagai input melalui STDIN, argumen baris perintah atau argumen fungsi, yang menyelesaikan masalah untuk n koin menggunakan penimbangan sesedikit mungkin dalam kasus terburuk. Program harus:

  • Cetak timbangan ke STDOUT dalam format 1,2,3-4,5,6untuk menunjukkan daftar koin di setiap sisi skala. Koin apa pun yang tidak ditimbang tidak boleh disebutkan. Koin-koin tersebut secara implisit diberi nomor dari 1 hingga n dan tidak perlu dicetak dalam urutan numerik (demikian 2,1-3,4juga dengan 1,2-3,4).
  • Setelah masing-masing menimbang program harus menunggu pada input melalui STDIN, yang seharusnya <, =atau >, menunjukkan apakah sisi kiri skala lebih ringan, sama, atau lebih berat daripada sisi kanan.
  • Setelah hasil penimbangan terakhir, program harus mencetak atau mengembalikan jumlah koin unik.
  • Program tidak perlu menangani input hasil yang tidak konsisten dari pengguna.
  • Program tidak perlu menangani n kurang dari 3.

Keluaran contoh

>> 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

Mencetak gol

Kode terpendek menang. Aturan standar berlaku.

Uri Granta
sumber

Jawaban:

2

Python 3: 497 byte

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)))))

Saya menduga ini bisa menyusut lebih sedikit, tapi saya tidak melihat tempat yang jelas lagi (setelah sekitar 5 versi berbeda dari setiap fungsi).

Kode mengimplementasikan versi algoritma yang sedikit dimodifikasi dari halaman ini menggunakan tiga fungsi. The Ifungsi melakukan IO (mencetak pilihan dan kembali respon pengguna). The Adan Bfungsi melaksanakan utama dari algoritma. Amengambil dua daftar yang berbeda ukurannya dengan tepat satu elemen (meskipun salah satu daftar bisa lebih besar): satu koin amungkin lebih ringan dari biasanya, atau satu koin blebih berat. Bmelakukan tugas ganda. Dibutuhkan satu daftar koin adan secara opsional daftar kedua dengan satu koin yang diketahui memiliki berat yang benar. Perilaku pembulatan panjang harus berbeda antara kedua kasus, yang tidak menyebabkan sakit kepala.

Dua fungsi algoritme dapat menemukan koin dengan bobot luar biasa dalam kpenimbangan yang diberikan input hingga ukuran berikut:

  • A: 3^ktotal koin, dibagi menjadi dua daftar (3^k-1)/2dan (3^k+1)/2.
  • B: (3^k + 1)/2koin jika koin yang diketahui baik disediakan, (3^k - 1)/2 jika tidak.

Pertanyaan yang diajukan di sini menentukan bahwa kita tidak memiliki koin yang diketahui-baik pada awalnya, sehingga kita dapat menyelesaikan menemukan koin yang buruk dalam satu set (3^k - 1)/2dalam kpenimbangan.

Inilah fungsi tes yang saya tulis untuk memastikan kode saya tidak meminta penimbangan palsu atau menggunakan lebih dari jumlah penimbangan yang seharusnya:

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

Ini mencetak jumlah penimbangan terburuk untuk set tertentu setelah pengujian dengan masing-masing kombinasi koin dan berat buruk (berat atau ringan).

Berikut ini adalah hasil uji untuk set hingga 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

Breakpoint persis di tempat yang Anda harapkan, antara (3^k - 1)/2dan (3^k + 1)/2.

Blckknght
sumber