Problema di dodici monete


14

sfondo

Il problema delle dodici monete è un classico puzzle di equilibrio comunemente usato nelle interviste di lavoro. Il puzzle apparve per la prima volta nel 1945 e fu proposto a mio padre da mio nonno quando chiese di sposare mia madre! Nel puzzle ci sono dodici monete, una delle quali è più pesante o più leggera delle altre (non sai quale). Il problema è usare una bilancia tre volte per determinare la moneta unica. In alcune varianti, è anche necessario identificare se la moneta è più pesante o più leggera.

Il compito qui consiste nel risolvere il problema generale che coinvolge n monete, usando il minor numero possibile di pesature nel caso peggiore. Non è necessario identificare se la moneta è più pesante o più leggera, solo quale sia. Inoltre, non hai accesso a monete aggiuntive al di fuori del set dato (che, curiosamente, fa la differenza).

Si scopre che k pesate sono sufficienti per un massimo di (3 ^ k-1) / 2 monete (quindi 4 pesate in questa variazione possono effettivamente gestire 13 monete). Inoltre (e sorprendentemente), è possibile (ma non richiesto qui) selezionare in anticipo l'intero set di pesature, piuttosto che le future pesate dipendono dai risultati passati. Per le descrizioni di due possibili soluzioni, vedere questo documento e questa risposta Quora .

Compito

Scrivi una funzione o un programma, prendendo un numero intero n come input tramite STDIN, argomento della riga di comando o argomento della funzione, che risolve il problema per n monete usando il minor numero possibile di pesature nel caso peggiore. Il programma dovrebbe:

  • Stampa le pesate su STDOUT nel formato 1,2,3-4,5,6per indicare gli elenchi di monete su ciascun lato della bilancia. Le monete non pesate non devono essere menzionate. Le monete sono implicitamente numerate da 1 a n e non devono essere stampate in ordine numerico (quindi 2,1-3,4è uguale a 1,2-3,4).
  • Dopo ogni pesata, il programma dovrebbe attendere un ingresso tramite STDIN, che dovrebbe essere <, =o >, indicando se il lato sinistro della bilancia è più leggero, uguale o più pesante del lato destro.
  • Dopo l'ultimo risultato di pesatura, il programma dovrebbe stampare o restituire il numero della moneta unica.
  • Il programma non deve gestire input di risultati incoerenti da parte dell'utente.
  • Non è necessario che il programma gestisca n meno di 3.

Esempi di output

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

punteggio

Il codice più corto vince. Si applicano le regole standard.

Risposte:


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

Ho il sospetto che questo potrebbe essere ridotto un po 'di più, ma non vedo più nessun posto ovvio (dopo circa 5 versioni diverse di ciascuna funzione).

Il codice implementa una versione leggermente modificata dell'algoritmo da questa pagina usando tre funzioni. La Ifunzione esegue l'IO (stampa delle opzioni e restituzione della risposta dell'utente). Le funzioni Ae Bimplementano il principale dell'algoritmo. Aprende due liste che differiscono per dimensioni esattamente di un elemento (anche se una delle liste può essere la più grande): una moneta in apuò essere più leggera del normale o una moneta in bpuò essere più pesante. Bfa il doppio dovere. Prende un elenco di monete ae facoltativamente un secondo elenco con una singola moneta che è noto per essere il peso corretto. Il comportamento di arrotondamento della lunghezza deve essere diverso tra i due casi, il che non ha causato mal di testa.

Le due funzioni dell'algoritmo possono trovare la moneta insolitamente ponderata nelle kpesate con input fino alle seguenti dimensioni:

  • A: 3^kmonete totali, divise in due liste di (3^k-1)/2e (3^k+1)/2.
  • B: (3^k + 1)/2monete se viene fornita una moneta di valore noto, (3^k - 1)/2 altrimenti.

La domanda posta qui specifica che non abbiamo tutte le monete nota bene alla partenza, in modo da possiamo risolvere trovare la cattiva moneta in una serie di (3^k - 1)/2in kpesate.

Ecco una funzione di test che ho scritto per assicurarmi che il mio codice non richiedesse pesature fasulle o che usasse più del numero di pesature che avrebbe dovuto:

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

Questo stampa il numero peggiore di pesate per un determinato set dopo il test con ogni combinazione di moneta e peso ridotto (pesante o leggero).

Ecco l'output di test per set fino a 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

I punti di interruzione sono esattamente dove ti aspetteresti, tra (3^k - 1)/2e (3^k + 1)/2.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.