Crea un planificador de pruebas de vino envenenado

16

Recientemente en Puzzling.SE, escribí un problema acerca de determinar qué dos botellas de un número mayor están envenenadas cuando el veneno solo se activa si ambos componentes están borrachos. Terminó siendo una experiencia terrible, con la mayoría de las personas logrando reducirlo a 18 o 19 prisioneros usando algoritmos completamente diferentes.

La declaración original del problema es la siguiente:

Eres el gobernante de un reino medieval al que le encantan las fiestas. El cortesano que trató de envenenar una de sus botellas de vino la última vez se enfureció al saber que logró identificar qué botella había envenenado de 1,000 con solo diez prisioneros.

Esta vez es un poco más astuto. Ha desarrollado un veneno compuesto P : un líquido binario que solo es mortal cuando dos componentes individualmente inofensivos se mezclan; Esto es similar a cómo funciona el epoxi. Te ha enviado otra caja de 1,000 botellas de vino. Una botella tiene componente C_ay otra tiene componente C_b. ( P = C_a + C_b)

Cualquiera que beba ambos componentes morirá a la medianoche de la noche en que bebió el componente final, independientemente de cuándo en el día en que bebieron el líquido. Cada componente de veneno permanece en el cuerpo hasta que se activa el segundo componente, por lo que si bebe un componente un día y otro componente al día siguiente, morirá a medianoche al final del segundo día.

Tienes dos días antes de tu próxima fiesta. ¿Cuál es el número mínimo de prisioneros que necesita usar para las pruebas para identificar qué dos botellas están contaminadas y qué algoritmo necesita seguir con ese número de prisioneros?


Bono
Además, suponga que tiene un límite fijo de 20 prisioneros a su disposición, ¿cuál es el número máximo de botellas que en teoría podría probar y llegar a una conclusión precisa sobre qué botellas se vieron afectadas?

Su tarea es construir un programa para resolver el problema de bonificación. Dado a los npresos, su programa diseñará un cronograma de pruebas que podrá detectar las dos botellas envenenadas entre mbotellas, donde msea ​​lo más grande posible.

Inicialmente, su programa tomará como entrada el número N, el número de prisioneros. Luego generará:

  • M, la cantidad de botellas que intentará probar. Estas botellas serán etiquetadas de 1a M.

  • N líneas, que contienen las etiquetas de las botellas que tomará cada prisionero.

Su programa tomará como entrada qué presos murieron el primer día, con el prisionero en la primera línea 1, la siguiente línea 2, etc. Luego, generará:

  • Nmás líneas, que contienen las etiquetas de las botellas que tomará cada prisionero. Los prisioneros muertos tendrán líneas en blanco.

Luego, su programa tomará como entrada qué prisioneros murieron en el segundo día, y generará dos números Ay Brepresentará las dos botellas que su programa cree que contienen el veneno.

Un ejemplo de entrada para dos prisioneros y cuatro botellas podría ser así, si las botellas 1y 3están envenenadas:

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

El programa de pruebas de su programa debe determinar con éxito cada posible par de botellas envenenadas para que sea un envío válido.

Su programa se puntuará según los siguientes criterios, en orden:

  • El número máximo de botellas que puede discernir para el caso N = 20.

  • El número de botellas para el caso N = 21, y sucesivamente casos más altos después de eso.

  • La longitud del código. (El código más corto gana).

Joe Z.
fuente
¿Cómo se verá la entrada si más de un prisionero muere en un solo día? Ninguno de sus ejemplos cubre ese caso, y la especificación es ambigua para mí.
ESultanik
¿Es una sola línea con una lista de prisioneros separados por espacios que murieron?
ESultanik
¿El código más corto importa más que el número de botellas? ¿Es productivo aumentar la longitud del código para que maneje una botella más, como hice en mi edición reciente?
pppery
El número de botellas tiene prioridad. Si hace que su código sea más largo y complejo para exprimir más botellas, es productivo.
Joe Z.
En el problema original solo hay 2 días para resolver el problema. ¿Esa es también la regla para el desafío? (limita severamente las posibles soluciones, sin embargo, una cantidad ilimitada de días sería
demasiado

Respuestas:

7

Python 2.7.9 - 21 botellas

Asumiendo que la especulación de ESultanik es correcta sobre cuál es la información cuando mueren varios prisioneros

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: cada preso bebe de cada botella excepto su número (el primer preso no bebe la primera botella). Si no mueren, su botella número está envenenada. Si solo un prisionero sobrevive, la botella extra está envenenada.

pppery
fuente
3

Perl 5 , 66 botellas

(72 botellas para 21 prisioneros)

Los prisioneros se dividen de manera óptima en 2 grupos. Las botellas se agrupan en juegos.

Cada prisionero del grupo 1 beberá de todos los grupos excepto uno. Habrá 1 o 2 sobrevivientes. Los 1 o 2 sets que no fueron tomados por ellos continuarán hasta el día 2.

El día 2, los prisioneros restantes (incluidos los sobrevivientes) beben de todas las botellas restantes, excepto una.
Cuando 2 prisioneros sobreviven, las botellas que no bebieron están envenenadas.
Si solo queda 1 prisionero, entonces la botella que bebieron todos también es sospechosa.

El código incluye una funcionalidad adicional para facilitar las pruebas. Cuando las botellas aplanadas se agregan como parámetros adicionales, no solicitará información sobre quién murió.

($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"

Prueba

$ 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

Prueba sin entrada manual

$ 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
LukStorms
fuente
2

Como es tradición, publicaré una respuesta de referencia de último lugar.

Python - 7 botellas

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]

Haga que cada prisionero tome un posible par de botellas, excepto el par de las dos últimas. Si algún prisionero muere, la pareja que bebió ese prisionero fueron los envenenados. De lo contrario, fueron las dos últimas botellas que fueron envenenadas.

Para asignaciones de al menos n(n-1)/2 - 1prisioneros, puede hacer hasta nbotellas. Para n = 7, este límite inferior es 20.

Solo necesitamos un día para que esta solución funcione. Una solución de dos días con un alcance similar podría obtener hasta 20 botellas porN = 20 , pero es demasiado trabajo para una respuesta trivial.

Joe Z.
fuente