Costruisci un programmatore di analisi del vino avvelenato


16

Di recente su Puzzling.SE, ho scritto un problema per determinare quali due bottiglie di un numero maggiore sono avvelenate quando il veleno si attiva solo se entrambi i componenti sono ubriachi. Alla fine è stato un vero calvario, con la maggior parte delle persone che è riuscita a farlo arrivare a 18 o 19 prigionieri usando algoritmi completamente diversi.

La dichiarazione del problema originale è la seguente:

Sei il sovrano di un regno medievale che ama organizzare feste. Il cortigiano che ha tentato di avvelenare una delle tue bottiglie di vino l'ultima volta è stato furioso di apprendere che sei riuscito a identificare quale bottiglia aveva avvelenato su 1.000 con solo dieci prigionieri.

Questa volta è un po 'più furbo. Ha sviluppato un veleno composito P : un liquido binario che è mortale solo quando due componenti individualmente innocui si mescolano; questo è simile a come funziona la resina epossidica. Ti ha inviato un'altra cassa da 1.000 bottiglie di vino. Una bottiglia ha componente C_ae un'altra ha componente C_b. ( P = C_a + C_b)

Chiunque beva entrambi i componenti morirà nel colpo di mezzanotte della notte in cui ha bevuto il componente finale, indipendentemente da quando nel giorno hanno assorbito il liquido. Ogni componente velenoso rimane nel corpo fino all'attivazione del secondo componente, quindi se bevi un componente un giorno e un altro componente il successivo, morirai a mezzanotte alla fine del secondo giorno.

Hai due giorni prima della prossima festa. Qual è il numero minimo di prigionieri che è necessario utilizzare per i test al fine di identificare quali due bottiglie sono contaminate e quale algoritmo è necessario seguire con quel numero di prigionieri?


Bonus
Inoltre, supponiamo che tu abbia un limite fisso di 20 prigionieri a tua disposizione, qual è il numero massimo di bottiglie che potresti teoricamente testare e giungere a una conclusione accurata su quali bottiglie sono state colpite?

Il tuo compito è costruire un programma per risolvere il problema del bonus. Dati i nprigionieri, il tuo programma elaborerà un programma di test che sarà in grado di rilevare le due bottiglie avvelenate tra le mbottiglie, dove mè il più grande possibile.

Il tuo programma inizialmente prenderà come input il numero N, il numero di prigionieri. Verrà quindi emesso:

  • M, il numero di bottiglie che proverai a testare. Queste bottiglie saranno etichettate da 1a M.

  • N linee, contenenti le etichette delle bottiglie che ogni prigioniero berrà.

Il tuo programma prenderà quindi come input quali prigionieri sono morti il ​​primo giorno, con il prigioniero in prima linea essendo 1, la linea successiva essendo 2, ecc. Quindi, produrrà:

  • Npiù righe, contenenti le etichette delle bottiglie che ogni prigioniero berrà. I prigionieri morti avranno righe vuote.

Il tuo programma prenderà quindi come input quali prigionieri sono morti il ​​secondo giorno, e produrrà due numeri Ae B, rappresentando quali due bottiglie il tuo programma ritiene contenga il veleno.

Un input di esempio per due prigionieri e quattro bottiglie potrebbe andare come tale, se le bottiglie 1e 3sono avvelenate:

> 2      // INPUT: 2 prisoners
4        // OUTPUT: 4 bottles
1 2 3    // OUTPUT: prisoner 1 will drink 1, 2, 3
1 4      // OUTPUT: prisoner 2 will drink 1, 4
> 1      // INPUT: only the first prisoner died
         // OUTPUT: prisoner 1 is dead, he can't drink any more bottles
3        // OUTPUT: prisoner 2 drinks bottle 3
> 2      // INPUT: prisoner 2 died
1 3      // OUTPUT: therefore, the poisoned bottles are 1 and 3.

The above algorithm may not actually work in all
cases; it's just an example of input and output.

Il programma di test del programma deve determinare con successo ogni possibile coppia di flaconi avvelenati affinché sia ​​valida.

Il tuo programma verrà valutato in base ai seguenti criteri, in ordine:

  • Il numero massimo di bottiglie che può discernere per il caso N = 20.

  • Il numero di bottiglie per il caso N = 21e successivamente casi più elevati.

  • La lunghezza del codice. (Il codice più corto vince.)


Come sarà l'input se più di un prigioniero muore in un solo giorno? Nessuno dei tuoi esempi copre quel caso e la specifica è ambigua per me.
ESultanik,

È un'unica riga con un elenco separato da spazi di prigionieri che sono morti?
ESultanik,

Il codice più breve conta più del numero di bottiglie? È produttivo aumentare la lunghezza del codice per farlo gestire un'altra bottiglia, come ho fatto nella mia recente modifica?
pepido

Il numero di bottiglie ha la priorità. Se rendi il tuo codice più lungo e complesso per spremere più bottiglie, questo è produttivo.
Joe Z.

Nel problema originale ci sono solo 2 giorni per risolvere il problema. È anche questa la regola della sfida? (limita fortemente le possibili soluzioni, tuttavia un numero illimitato di giorni sarebbe
troppo

Risposte:


7

Python 2.7.9 - 21 bottiglie

Supponendo che la speculazione di ESultanik sia corretta su ciò che l'input è quando muoiono più prigionieri

r=raw_input;s=str;j=s.join;p=int(r());z=range;q=z(p);x=z(p+1)
print s(p+1)+"\n"+j("\n",(j(" ",(s(a) for a in x if a!=b)) for b in q))
v=r().split();d=[s(a) for a in q if s(a) not in v];d+=[p]if len(d)==1 else [];
print "\n"*p,;r();print j(" ",[s(a) for a in d])

Algoritmo: ogni prigioniero beve da ogni bottiglia tranne il loro numero (il 1 ° prigioniero non beve la prima bottiglia). Se non muoiono, la loro bottiglia numerica viene avvelenata. Se sopravvive un solo prigioniero, la bottiglia in più viene avvelenata.


3

Perl 5 , 66 bottiglie

(72 bottiglie per 21 prigionieri)

I prigionieri sono divisi in modo ottimale in 2 gruppi. Le bottiglie sono raggruppate in set.

Ogni prigioniero del gruppo 1 berrà da tutti i set tranne uno. Ci saranno 1 o 2 sopravvissuti. I 1 o 2 set che non sono stati bevuti da loro continueranno al giorno 2.

Il giorno 2 i prigionieri rimanenti (compresi i sopravvissuti) bevono da tutte le bottiglie rimanenti tranne una.
Quando 2 prigionieri sopravvivono, le bottiglie che non hanno bevuto vengono avvelenate.
Se rimane solo 1 prigioniero, anche la bottiglia che hanno bevuto è sospetta.

Il codice include funzionalità extra per facilitare i test. Quando le bottiglie avvelenate vengono aggiunte come parametri aggiuntivi, non chiederanno input su chi è morto.

($p,$f,$l)=@ARGV;
$p=9if!$p;
$m=$p-(2*int($p/4))+1;
$n=$p-$m+2;
$b=$m*(($n+1)/2);
@M=(1..$m);
print"Prisoners: $p\nBottles: $b\n";
# building the sets of items
for$x(@M){
    $j=$k+1;$k+=($n+1)/2;
    $s=join",",($j..$k);
    $A[$x]=$s
}
# assigning the sets to the actors
for$x(@M){
    @T=();
    for$j(@M){if($x!=$j){push@T,split/,/,$A[$j]}}
    print"Prisoner $x drinks @T\n";
    $B[$x]=join",",@T
}
if(!$f||!$l){
    # manual input
    print"Who dies: ";
    $_=<STDIN>;chomp;
    @D=split/ /;
    %h=map{($_,1)}@D;
    @S=grep{!$h{$_}}(@M)
} 
else{
    # calculate who dies based on the parameters
    for$x(@M){
        $d=0;
        map{if($_==$f||$_==$l){$d++}}split/,/,$B[$x];
        if($d>1){push@D,$x}else{push@S,$x}
    }
}
for(@D){print"Prisoner $_ dies\n"}

# calculate the remaining items
for(@S){push@R,split/,/,$A[$_]}@R=sort{$a<=>$b}grep{!$g{$_}++}@R;

# different set of actors if there were 1 or 2 sets remaining
if(@S>1){@S=($S[0],$m+1..$p,$S[1],0)}else{@S=($m+1..$p)};

$i=0;@B=@D=();
# assign an item to each actor
for$x(@S){
    @T=();
    for($j=0;$j<@R;$j++){
        if($i!=$j){push@T,$R[$j]}
    }$i++;
    print"Prisoner $x drinks @T\n"if$x>0;
    $B[$x]=join",",@T
}

if(!$f||!$l){
    # manual input
    print"Who dies: ";
    $_=<STDIN>;chomp;
    @D=sort split/ /;
    if(@D<@S-1){push@D,0} # because the set that noone drinks isn't manually put in
    %h=map{($_,1)}@D;
    @L=grep{!$h{$_}}(@S);
}
else{
    # calculate who dies based on the parameters
    @D=();
    for$x(@S){
        $d=0;
        map{if($_==$f||$_==$l){$d++}}split/,/,$B[$x];
        if($d>1){push@D,$x}else{push@L,$x}
    }
}

for(@D){print"Prisoner $_ dies\n"if$_>0}

# calculate the remaining items
for(@L){push@F,split/,/,$B[$_]}
map{$c{$_}++}@F;
for(keys%c){push(@Z,$_)if$c{$_}==1}
@R=sort{$a<=>$b}@Z;

print"Suspected bottles: @R"

Test

$ perl poisened_bottles.pl 20
Prisoners: 20
Bottles: 66
Prisoner 1 drinks 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 2 drinks 1 2 3 4 5 6 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 3 drinks 1 2 3 4 5 6 7 8 9 10 11 12 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 4 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 5 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 6 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 7 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 8 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 9 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 10 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 61 62 63 64 65 66
Prisoner 11 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
Who dies: 2 3 4 5 6 7 8 9 10
Prisoner 2 dies
Prisoner 3 dies
Prisoner 4 dies
Prisoner 5 dies
Prisoner 6 dies
Prisoner 7 dies
Prisoner 8 dies
Prisoner 9 dies
Prisoner 10 dies
Prisoner 1 drinks 2 3 4 5 6 61 62 63 64 65 66
Prisoner 12 drinks 1 3 4 5 6 61 62 63 64 65 66
Prisoner 13 drinks 1 2 4 5 6 61 62 63 64 65 66
Prisoner 14 drinks 1 2 3 5 6 61 62 63 64 65 66
Prisoner 15 drinks 1 2 3 4 6 61 62 63 64 65 66
Prisoner 16 drinks 1 2 3 4 5 61 62 63 64 65 66
Prisoner 17 drinks 1 2 3 4 5 6 62 63 64 65 66
Prisoner 18 drinks 1 2 3 4 5 6 61 63 64 65 66
Prisoner 19 drinks 1 2 3 4 5 6 61 62 64 65 66
Prisoner 20 drinks 1 2 3 4 5 6 61 62 63 65 66
Prisoner 11 drinks 1 2 3 4 5 6 61 62 63 64 66
Who dies: 1 12 14 15 16 17 18 20 11
Prisoner 1 dies
Prisoner 11 dies
Prisoner 12 dies
Prisoner 14 dies
Prisoner 15 dies
Prisoner 16 dies
Prisoner 17 dies
Prisoner 18 dies
Prisoner 20 dies
Suspected bottles: 3 63

Test senza input manuale

$ perl poisened_bottles.pl 7 2 5
Prisoners: 7
Bottles: 12
Prisoner 1 drinks 3 4 5 6 7 8 9 10 11 12
Prisoner 2 drinks 1 2 5 6 7 8 9 10 11 12
Prisoner 3 drinks 1 2 3 4 7 8 9 10 11 12
Prisoner 4 drinks 1 2 3 4 5 6 9 10 11 12
Prisoner 5 drinks 1 2 3 4 5 6 7 8 11 12
Prisoner 6 drinks 1 2 3 4 5 6 7 8 9 10
Prisoner 2 dies
Prisoner 4 dies
Prisoner 5 dies
Prisoner 6 dies
Prisoner 1 drinks 2 5 6
Prisoner 7 drinks 1 5 6
Prisoner 3 drinks 1 2 6
Prisoner 1 dies
Suspected bottles: 2 5

2

Come da tradizione, posterò una risposta di riferimento all'ultimo posto.

Python - 7 bottiglie

prisoners = int(raw_input())

bottles = 0
while (bottles * (bottles + 1) / 2 - 1) <= prisoners:
    bottles += 1

print bottles

pairs = []
for i in range(bottles):
    for j in range(i + 1, bottles):
        pairs += [str(i + 1) + " " + str(j + 1)]

for i in range(prisoners):
    if i < len(pairs):
        print pairs[i]
    else:
        print

dead_prisoner = raw_input()

for i in range(prisoners):
    print
raw_input() # discard the second day entirely

if dead_prisoner == "":
    print pairs[-1]
else:
    print pairs[int(dead_prisoner) - 1]

Fai bere a ogni prigioniero una possibile coppia di bottiglie tranne la coppia delle ultime due. Se un prigioniero muore, la coppia che quel prigioniero ha bevuto erano quelli avvelenati. Altrimenti, sono state le ultime due bottiglie ad essere avvelenate.

Per le assegnazioni di almeno n(n-1)/2 - 1prigionieri, puoi fare fino a nbottiglie. Per n = 7questo limite inferiore è 20.

In realtà abbiamo solo bisogno di un giorno per far funzionare questa soluzione. Una soluzione di due giorni con un ambito simile potrebbe richiedere fino a 20 bottiglie per N = 20, ma è troppo lavoro per una risposta banale.

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.