Divide con avidez la lista de combinaciones con repetición

10

Primero, algunas definiciones:

  • Dado ny k, considere la lista ordenada de conjuntos múltiples , donde para cada conjunto múltiple elegimos knúmeros {0, 1, ..., n-1}con repeticiones.

Por ejemplo, para n=5y k=3, tenemos:

[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4), (0, 1, 1), ( 0, 1, 2), (0, 1, 3), (0, 1, 4), (0, 2, 2), (0, 2, 3), (0, 2, 4), (0, 3, 3), (0, 3, 4), (0, 4, 4), (1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 1, 4), (1, 2, 2), (1, 2, 3), (1, 2, 4), (1, 3, 3), (1, 3, 4), (1, 4, 4) , (2, 2, 2), (2, 2, 3), (2, 2, 4), (2, 3, 3), (2, 3, 4), (2, 4, 4), ( 3, 3, 3), (3, 3, 4), (3, 4, 4), (4, 4, 4)]

  • Una parte es una lista de conjuntos múltiples con la propiedad de que el tamaño de la intersección de todos los conjuntos múltiples en la parte es al menos k-1. Es decir, tomamos todos los conjuntos múltiples y los intersectamos (usando la intersección de conjuntos múltiples) de una vez. Como ejemplos, [(1, 2, 2), (1, 2, 3), (1, 2, 4)]es una parte ya que su intersección es de tamaño 2, pero [(1, 1, 3),(1, 2, 3),(1, 2, 4)]no lo es, porque su intersección es de tamaño 1.

Tarea

Su código debe tomar dos argumentos ny k. A continuación, debe pasar con avidez por estos conjuntos múltiples en orden ordenado y generar las partes de la lista. Para el caso n=5, k=3, la partición correcta es:

(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4)
(0, 1, 1), (0, 1, 2), (0, 1, 3), (0, 1, 4)
(0, 2, 2), (0, 2, 3), (0, 2, 4)
(0, 3, 3), (0, 3, 4)
(0, 4, 4)
(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 1, 4)
(1, 2, 2), (1, 2, 3), (1, 2, 4)
(1, 3, 3), (1, 3, 4)
(1, 4, 4)
(2, 2, 2), (2, 2, 3), (2, 2, 4)
(2, 3, 3), (2, 3, 4)
(2, 4, 4)
(3, 3, 3), (3, 3, 4)
(3, 4, 4), (4, 4, 4)

Aquí hay otro ejemplo para n = 4, k = 4.

(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)
(0, 0, 1, 1), (0, 0, 1, 2), (0, 0, 1, 3)
(0, 0, 2, 2), (0, 0, 2, 3)
(0, 0, 3, 3)
(0, 1, 1, 1), (0, 1, 1, 2), (0, 1, 1, 3)
(0, 1, 2, 2), (0, 1, 2, 3)
(0, 1, 3, 3)
(0, 2, 2, 2), (0, 2, 2, 3)
(0, 2, 3, 3), (0, 3, 3, 3)
(1, 1, 1, 1), (1, 1, 1, 2), (1, 1, 1, 3)
(1, 1, 2, 2), (1, 1, 2, 3)
(1, 1, 3, 3)
(1, 2, 2, 2), (1, 2, 2, 3)
(1, 2, 3, 3), (1, 3, 3, 3)
(2, 2, 2, 2), (2, 2, 2, 3)
(2, 2, 3, 3), (2, 3, 3, 3)
(3, 3, 3, 3)

Aclaración de lo que significa codicioso: para cada conjunto múltiple, a su vez, buscamos si se puede agregar a la parte existente. Si puede, lo añadimos. Si no puede, comenzamos una nueva parte. Observamos los conjuntos múltiples en orden ordenado como en el ejemplo dado anteriormente.

Salida

Puede generar la partición en cualquier formato sensible que desee. Sin embargo, los conjuntos múltiples deben escribirse horizontalmente en una línea. Es un conjunto múltiple individual que no debe escribirse verticalmente ni distribuirse en varias líneas. Puede elegir cómo separar la representación de las partes en la salida.

Supuestos

Podemos suponer eso n >= k > 0.

Jonathan Allan
fuente
@LuisMendo Acabo de cometer un error. Quise decir que los multisets deberían escribirse horizontalmente en una línea.
En el primer caso de prueba, ¿ (0, 4, 4)por qué está solo? Dada su descripción, creo que su "parte" sería (0, 4, 4), (1, 4, 4), (2, 4, 4), (3, 4, 4), (4, 4, 4). Del mismo modo para (0, 0, 3, 3)en el segundo caso de prueba.
Greg Martin
@GregMartin Debido a la avaricia del método. Tienes razón en que, en general, será subóptimo. El número mínimo de piezas, usted puede obtener por un método no codicioso es una interesante pregunta si dura,
Oh, literalmente quiere decir que una vez que el próximo término no coincide con la parte "activa", esa parte se cierra para siempre. Okay.
Greg Martin

Respuestas:

4

Jalea , 26 25 bytes

œ&µL‘<⁴ȧ⁹ȯ
œċµç\L€=⁴œṗµḊ’

Programa completo que imprime una representación de una lista de listas, cada una de las cuales es una parte, por ejemplo, para n = 5, k = 3:

[[[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 0, 4]], [[0, 1, 1], [0, 1, 2], [0, 1, 3], [0, 1, 4]], [[0, 2, 2], [0, 2, 3], [0, 2, 4]], [[0, 3, 3], [0, 3, 4]], [0, 4, 4], [[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4]], [[1, 2, 2], [1, 2, 3], [1, 2, 4]], [[1, 3, 3], [1, 3, 4]], [1, 4, 4], [[2, 2, 2], [2, 2, 3], [2, 2, 4]], [[2, 3, 3], [2, 3, 4]], [2, 4, 4], [[3, 3, 3], [3, 3, 4]], [[3, 4, 4], [4, 4, 4]]]

Nota: la representación utilizada elimina las listas redundantes [ y ] redondas de longitud 1.

Pruébalo en línea! o vea una bonita versión impresa (costo 3 bytes)

¿Cómo?

œ&µL‘<⁴ȧ⁹ȯ - Link 1, conditional multi-set intersection: list x, list y
œ&         - multi-set intersection(x, y)
  µ        - monadic chain separation (call that i)
   L       - length(i)
    ‘      - increment
     <     - less than?:
      ⁴    -     2nd program input, k
       ȧ   - logical and with:
        ⁹  -     link's right argument, y (y if i is too short, else 0)
         ȯ - logical or (y if i is too short, else i)

œċµç\L€=⁴œṗµḊ’ - Main link: n, k
œċ             - combinations with replacement(n, k) (sorted since n implies [1,n])
  µ            - monadic chain separation (call that w)
         œṗ    - partition w at truthy indexes of:
   ç\          -     reduce w with last link (1) as a dyad
     L€        -     length of €ach
        ⁴      -     2nd program input, k
       =       -     equal (vectorises)
           µ   - monadic chain separation
            Ḋ  - dequeue (since the result will always start with an empty list)
             ’ - decrement (vectorises) (since the Natural numbers were used by œċ)
Jonathan Allan
fuente
Esto es genial. Gracias.
3

MATLAB, 272 bytes

function g(n,k);l=unique(sort(nchoosek(repmat(0:n-1,1,k),k),2),'rows');p=zeros(0,k);for i=1:size(l,1)p=[p;l(i,:)];a=0;for j=1:size(p,1)for m=1:size(p,1)b=0;for h=1:k if(p(j,h)==p(m,h))b=b+1;end;end;if(b<k-1)a=1;end;end;end;if(a)fprintf('\n');p=l(i,:);end;disp(l(i,:));end;

Salida:

>> g(5,3)
 0     0     0

 0     0     1

 0     0     2

 0     0     3

 0     0     4


 0     1     1

 0     1     2

 0     1     3

 0     1     4


 0     2     2

 0     2     3

 0     2     4


 0     3     3

 0     3     4


 0     4     4


 1     1     1

 1     1     2

 1     1     3

 1     1     4


 1     2     2

 1     2     3

 1     2     4


 1     3     3

 1     3     4


 1     4     4


 2     2     2

 2     2     3

 2     2     4


 2     3     3

 2     3     4


 2     4     4


 3     3     3

 3     3     4


 3     4     4

 4     4     4

>> g(4,4)
 0     0     0     0

 0     0     0     1

 0     0     0     2

 0     0     0     3


 0     0     1     1

 0     0     1     2

 0     0     1     3


 0     0     2     2

 0     0     2     3


 0     0     3     3


 0     1     1     1

 0     1     1     2

 0     1     1     3


 0     1     2     2

 0     1     2     3


 0     1     3     3


 0     2     2     2

 0     2     2     3


 0     2     3     3

 0     3     3     3


 1     1     1     1

 1     1     1     2

 1     1     1     3


 1     1     2     2

 1     1     2     3


 1     1     3     3


 1     2     2     2

 1     2     2     3


 1     2     3     3

 1     3     3     3


 2     2     2     2

 2     2     2     3


 2     2     3     3

 2     3     3     3


 3     3     3     3

Dos líneas vacías entre diferentes partes.

Sin golf:

function g(n,k);
l=unique(sort(nchoosek(repmat(0:n-1,1,k),k),2),'rows');
p=zeros(0,k);
for i=1:size(l,1)
    p=[p;l(i,:)];
    a=0;
    for j=1:size(p,1)
        for m=1:size(p,1)
            b=0;
            for h=1:k
                if(p(j,h)==p(m,h))
                    b=b+1;
                end;
            end;
                if(b<k-1)
                    a=1;
                end;
        end;
    end;
    if(a)
        fprintf('\n');
        p=l(i,:);
    end;
    disp(l(i,:));
end;

Explicación:

Primero encontramos todos los multisets con fuerza bruta:

l=unique(sort(nchoosek(repmat(0:n-1,1,k),k),2),'rows');

repmat(0:n-1, 1, k)repite el vector de valores de 0a n-1 kveces.

nchoosek(x, k) devuelve una matriz que contiene todas las combinaciones k del vector repetido.

sort(x, 2)ordena todas las combinaciones de k y luego unique(x, 'rows')elimina todos los duplicados.

p=zeros(0,k);crea una matriz vacía con kcolumnas. Lo usaremos como una pila. En cada iteración del outernmost forbucle, primero agregamos el conjunto múltiple de corriente a dicha pila: p=[p;l(i,:)];.

Luego verificamos si la intersección de todos los conjuntos múltiples en la pila es al menos k-1larga con el siguiente código (no podemos usar el intersectcomando de MATLAB para verificar las intersecciones, porque devuelve un conjunto, pero necesitamos un conjunto múltiple):

a=0;
for j=1:size(p,1)
    for m=1:size(p,1)
        b=0;
        for h=1:k 
            if(p(j,h)==p(m,h))
                b=b+1;
            end;
        end;
        if(b<k-1)
            a=1;
        end;
    end;
end;

Ahora, si la intersección es lo suficientemente larga a == 0, de lo contrario a == 1.

Si la intersección no es lo suficientemente larga, imprimimos una nueva línea y vaciamos la pila:

if(a)
    fprintf('\n');
    p=l(i,:); % Only the current multiset will be left in the stack.
end;

Luego simplemente imprimimos el multiset actual:

disp(l(i,:));
Steadybox
fuente
¡Parece que lo descifraste! ¿Podrías explicar tu método?
@Lembik agregué una explicación.
Steadybox
3

MATL , 34 bytes

vi:qiZ^!S!Xu!"@!&vt1&dXasq?0&Y)0cb

Las partes están separadas por una línea que contiene espacios en blanco.

Pruébalo en línea!

Explicación

Descargo de responsabilidad: este método parece funcionar (y funciona en los casos de prueba), pero no tengo pruebas de que siempre funcione

Los conjuntos múltiples se ordenan, tanto internamente (es decir, cada conjunto múltiple tiene entradas que no disminuyen) como externamente (es decir, el conjunto múltiple M viene antes del conjunto múltiple N si M precede a N lexicográficamente).

Para calcular la intersección de múltiples conjuntos, los múltiples conjuntos ordenados se ordenan como filas de una matriz y se calculan las diferencias consecutivas a lo largo de cada columna. Si todas las columnas, excepto como máximo una, tienen todas las diferencias iguales a cero, los conjuntos múltiples pertenecen a la misma parte.

Esta prueba daría un resultado falso negativo para multisets como (1,2,3)y (2,3,4): incluso si 2,3 son entradas comunes, que no serían detectados como tales porque están en columnas no coincidentes.

Sin embargo, esto no parece ser un problema, al menos en los casos de prueba. Parece que una prueba entre múltiples conjuntos gusta 1,2,3y 2,3,4nunca tiene que hacerse, porque algunos conjuntos múltiples intermedios dieron un resultado negativo y, por lo tanto, ya están en diferentes partes. Si esto es cierto, la razón sin duda tiene que ver con el hecho de que los multisets están ordenados.

Sin embargo, no tengo una prueba de esto. Simplemente parece funcionar.

v           % Concatenate stack vertically: gives an empty array. This will
            % grow into the first part
i:q         % Input n. Push [0 1 ... n-1]
i           % Input k
Z^          % Cartesian power. Each Cartesian tuple is on a row
!S!         % Sort each row
Xu          % Unique rows. This gives all multisets, sorted, each on a row
!           % Transpose
"           % For each column
  @!        %   Push current multiset as a row
  &v        %   Vertically concatenate with the part so far
  t         %   Duplicate
  1&d       %   Consecutive differences along each column
  Xas       %   Number of columns that contain at least one non-zero entry
  q?        %   If that number is not 1 (this means that the current 
            %   multiset should begin a new part)
    0&Y)    %     Push last row, then the array with the remaining rows.
            %     Said array is a part, which we now know is complete
    0c      %     Push character 0. This will be shown as a line containing 
            %     a space. This is used as a separator between parts.
    b       %     Bubble up. This moves the loose row to the top. This row 
            %     is the beginning of a new part
            %   Implicitly end if
            % Implicitly end for
            % Implicitly display
Luis Mendo
fuente
Es muy impresionante.
Estoy tratando de entender si el método que describe siempre funcionará. Veo que, en el n=k=4caso de que comencemos con una nueva parte (0, 0, 3, 3), la diferencia consecutiva vectorizada de eso y el conjunto múltiple anterior (0, 0, 2, 3)solo tiene una diferencia, entonces, ¿cómo hace que la "parte hasta ahora" funcione? (o equivalente, ¿cuál fue el resultado del paso anterior que se usó en lugar de (0, 0, 2, 3)?)
Jonathan Allan
Ah, te veo realizar una diferencia consecutiva. Sí, esto siempre debería funcionar! Literalmente, está buscando los puntos en los que cambia más de un elemento, pero en lugar de una intersección de varios conjuntos, simplemente intersección vectorizada, que funcionará ya que los conjuntos múltiples se ordenan para comenzar.
Jonathan Allan
@ JonathanAllan Sí, es una diferencia consecutiva en lugar de una intersección. Todavía no veo claro que siempre funcionará, pero si lo dices ... :-)
Luis Mendo
1

PHP, 245 bytes

for(;$i<($n=$argv[1])**$m=$argv[2];$i++){for($a=[],$v=$i;$v|count($a)<$m;$v=$v/$n^0)array_unshift($a,$v%$n);sort($a);in_array($a,$r)?:$r[]=$a;}foreach($r as$k=>$v)$k&&count(array_diff_assoc($x[$c][0],$v))<2?$x[$c][]=$v:$x[++$c][]=$v;print_r($x);

Pruébalo en línea!

Expandido

for(;$i<($n=$argv[1])**$m=$argv[2];$i++){ # loop till $argv[1]**$argv[2]
    for($a=[],$v=$i;$v|count($a)<$m;$v=$v/$n^0) 
    array_unshift($a,$v%$n); # create base n array
    sort($a); #sort array
    in_array($a,$r)?:$r[]=$a; # if sorted array is not in result add it
}    
foreach($r as$k=>$v)
    $k&& # > first item and
    count(array_diff_assoc($x[$c][0],$v))<2 # if difference is only 1 item between actual item and first item in last storage item
    ?$x[$c][]=$v # add item in last storage array
    :$x[++$c][]=$v; # make a new last storage array
print_r($x); # Output as array

Salida como cadena

foreach($x as$y){$p=[];
foreach($y as$z){$p[]=$o="(".join(",",$z).")";}
    echo join(", ",$p)."\n";
}

n> 15 para mayor precisión

for($i=0;$i<bcpow($argv[1],$argv[2]);$i=bcadd($i,1)){
    for($a=[],$v=$i;$v|count($a)<$argv[2];$v=bcdiv($v,$argv[1]))
    array_unshift($a,bcmod($v,$argv[1]));
    sort($a);
    in_array($a,$r)?:$r[]=$a;
}
Jörg Hülsermann
fuente
Esto parece funcionar! ¿Pero qué quieres decir con más precisión?
@Lembik la versión corta da vuelta 0para (16**16-1)%16y el tiempo de trabajo sólo en la versión con la precisión que se necesita para n>15 bcmod(bcsub(bcpow(16,16),1),16)es 15 php.net/manual/en/ref.bc.php
Jörg Hülsermann