Dividi avidamente l'elenco delle combinazioni con ripetizione


10

Innanzitutto, alcune definizioni:

  • Dato ne k, considera l'elenco ordinato dei multiset , dove per ogni multiset scegliamo knumeri {0, 1, ..., n-1}con ripetizioni.

Ad esempio, per n=5e k=3, abbiamo:

[(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 è un elenco di multiset con la proprietà che è almeno la dimensione dell'intersezione di tutti i multiset nella parte k-1. Cioè prendiamo tutti i multiset e li interseciamo (usando l'intersezione multiset) tutti in una volta. Ad esempio, [(1, 2, 2), (1, 2, 3), (1, 2, 4)]è una parte in quanto la sua intersezione è di dimensione 2, ma [(1, 1, 3),(1, 2, 3),(1, 2, 4)]non lo è, poiché la sua intersezione è di dimensione 1.

Compito

Il tuo codice dovrebbe prendere due argomenti ne k. Dovrebbe quindi passare avidamente attraverso questi multiset in ordine ordinato e produrre le parti dell'elenco. Per il caso n=5, k=3, il partizionamento corretto è:

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

Ecco un altro esempio per 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)

Chiarimento su ciò che significa avido: per ogni multiset a sua volta cerchiamo di vedere se può essere aggiunto alla parte esistente. Se è possibile, lo aggiungiamo. In caso contrario, iniziamo una nuova parte. Esaminiamo i multiset in ordine ordinato come nell'esempio sopra riportato.

Produzione

È possibile produrre il partizionamento in qualsiasi formato sensibile che ti piace. Tuttavia, i multiset devono essere scritti in orizzontale su una riga. Questo è un singolo multiset non deve essere scritto verticalmente o distribuito su più righe. È possibile scegliere come separare la rappresentazione delle parti nell'output.

ipotesi

Possiamo supporre che n >= k > 0.


@LuisMendo Ho appena fatto un errore. Volevo dire che i multiset dovrebbero essere scritti in orizzontale su una riga.

Nel primo caso di test, perché è (0, 4, 4)da solo? Data la tua descrizione, penso che la sua "parte" sarebbe (0, 4, 4), (1, 4, 4), (2, 4, 4), (3, 4, 4), (4, 4, 4). Allo stesso modo per (0, 0, 3, 3)il secondo caso di test.
Greg Martin,

@GregMartin A causa dell'avidità del metodo. Hai ragione, in generale non sarà ottimale. Il numero minimo di parti che puoi ottenere con un metodo non avido è una domanda interessante se difficile,

Oh, intendi letteralmente che una volta che il termine immediatamente successivo non corrisponde alla parte "attiva", quella parte è chiusa per sempre. Ok.
Greg Martin,

Risposte:


4

Gelatina , 26 25 byte

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

Programma completo che stampa una rappresentazione di un elenco di liste, ciascuna delle quali fa parte, ad es. Per 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 rappresentazione utilizzata rimuove gli elenchi ridondanti [ e ] attorno alla lunghezza 1.

Provalo online! o vedi una versione stampabile (costo 3 byte)

Come?

œ&µ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 œċ)

Questo è fantastico Grazie.

3

MATLAB, 272 byte

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;

Produzione:

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

Due linee vuote tra parti diverse.

Ungolfed:

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;

Spiegazione:

Innanzitutto troviamo tutti i multiset con forza bruta:

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

repmat(0:n-1, 1, k)ripete il vettore di valori da 0a n-1 kvolte.

nchoosek(x, k) restituisce una matrice contenente tutte le combinazioni k del vettore ripetuto.

sort(x, 2)ordina tutte le combinazioni k, quindi unique(x, 'rows')rimuove tutti i duplicati.

p=zeros(0,k);crea una matrice vuota con kcolonne. Lo useremo come una pila. Su ogni iterazione del outernmost forciclo, aggiungere prima il multinsieme corrente di detta pila: p=[p;l(i,:)];.

Quindi controlliamo se l'intersezione di tutti i multiset nello stack è almeno k-1lunga con il seguente codice (non possiamo usare il intersectcomando MATLAB per verificare le intersezioni, perché restituisce un set, ma abbiamo bisogno di un multiset):

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;

Ora, se l'intersezione è abbastanza lunga a == 0, altrimenti a == 1.

Se l'intersezione non è abbastanza lunga, stampiamo una nuova riga e svuotiamo la pila:

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

Quindi stampiamo il multiset corrente:

disp(l(i,:));

Sembra che tu l'abbia rotto! Potresti spiegare il tuo metodo?

@Lembik ho aggiunto una spiegazione.
Steadybox

3

MATL , 34 byte

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

Le parti sono separate da una linea contenente spazi bianchi.

Provalo online!

Spiegazione

Disclaimer: questo metodo sembra funzionare (e lo fa nei casi di test), ma non ho una prova che lo fa sempre

I multiset sono ordinati, sia internamente (cioè ogni multiset ha voci non decrescenti) che esternamente (ovvero multiset M viene prima del multiset N se M precede N lessicograficamente).

Per calcolare l'intersezione multiset, i multiset ordinati sono disposti come righe di una matrice e le differenze consecutive vengono calcolate lungo ciascuna colonna. Se tutte le colonne tranne una hanno tutte le differenze uguali a zero, i multiset appartengono alla stessa parte.

Questo test darebbe un risultato falso negativo per multiset come (1,2,3)e (2,3,4): anche se 2, 3sono voci comuni, non verrebbero rilevati come tali perché si trovano in colonne non corrispondenti.

Tuttavia, questo non sembra essere un problema, almeno nei casi di test. Sembra che un test tra multiset non piaccia 1,2,3e 2,3,4non debba mai essere effettivamente eseguito, poiché alcuni multiset intermedi hanno dato un risultato negativo e quindi sono già in parti diverse. Se questo è effettivamente vero, la ragione senza dubbio ha a che fare con il fatto che i multiset sono ordinati.

Non ne ho una prova, però. Sembra solo funzionare.

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

È davvero impressionante.

Sto cercando di capire se il metodo che descrivi funzionerà sempre. Vedo che nel n=k=4caso in cui abbiamo una nuova parte iniziata (0, 0, 3, 3), la differenza consecutiva vettorializzata di quella e del multi-set precedente, (0, 0, 2, 3)ha solo una differenza, quindi come funziona la "parte finora"? (o equivalentemente qual è stato il risultato del passaggio precedente che è stato utilizzato al posto di (0, 0, 2, 3)?)
Jonathan Allan,

Ah, vedo che esegui una differenza consecutiva. Sì, questo dovrebbe sempre funzionare! Stai letteralmente cercando i punti in cui cambia più di un elemento, ma piuttosto che un'intersezione multi-set, semplicemente intersezione vettorializzata - che funzionerà poiché i set multipli sono ordinati per cominciare.
Jonathan Allan,

@JonathanAllan Sì, è la differenza consecutiva piuttosto che l'intersezione. Continuo a non vedere chiaramente che funzionerà sempre, ma se lo dici tu ... :-)
Luis Mendo

1

PHP, 245 byte

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

Provalo online!

allargato

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

Uscita come stringa

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

n> 15 per una maggiore precisione

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

Questo sembra funzionare! Ma cosa intendi con maggiore precisione?

@Lembik la versione corta dà indietro 0per (16**16-1)%16e il lungo lavoro solo versione con la precisione che è necessario per n>15 bcmod(bcsub(bcpow(16,16),1),16)è 15 php.net/manual/en/ref.bc.php
Jörg Hülsermann
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.