Partage goulûment la liste des combinaisons avec répétition

10

Tout d'abord, quelques définitions:

  • Étant donné net k, considérons la liste triée de multisets , où pour chaque multiset nous choisissons des knombres {0, 1, ..., n-1}avec des répétitions.

Par exemple, pour n=5et k=3, nous avons:

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

  • Une pièce est une liste de multisets avec la propriété que la taille de l'intersection de tous les multisets de la pièce est au moins k-1. C'est-à-dire que nous prenons tous les multisets et les croisons (en utilisant l'intersection multiset) en une seule fois. Par exemple, [(1, 2, 2), (1, 2, 3), (1, 2, 4)]est une pièce car son intersection est de taille 2, mais [(1, 1, 3),(1, 2, 3),(1, 2, 4)]ne l'est pas, car son intersection est de taille 1.

Tâche

Votre code doit prendre deux arguments net k. Il doit ensuite parcourir goulûment ces multisets dans un ordre trié et sortir les parties de la liste. Pour le cas n=5, k=3, le partitionnement correct est:

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

Voici un autre exemple pour 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)

Clarification de ce que signifie gourmand: Pour chaque multiset tour à tour nous cherchons à voir s'il peut être ajouté à la partie existante. Si c'est le cas, nous l'ajouterons. Si ce n'est pas possible, nous commençons une nouvelle partie. Nous regardons les multisets dans l'ordre trié comme dans l'exemple donné ci-dessus.

Production

Vous pouvez sortir le partitionnement dans n'importe quel format sensible que vous aimez. Cependant, les multisets doivent être écrits horizontalement sur une seule ligne. C'est-à-dire qu'un multiset individuel ne doit pas être écrit verticalement ou réparti sur plusieurs lignes. Vous pouvez choisir la façon dont vous séparez la représentation des pièces dans la sortie.

Hypothèses

Nous pouvons supposer cela n >= k > 0.

Jonathan Allan
la source
@LuisMendo Je viens de faire une erreur. Je voulais dire que les multisets devraient être écrits horizontalement sur une seule ligne.
Dans le premier cas de test, pourquoi tout (0, 4, 4)seul? Compte tenu de votre description, je pense que sa "partie" serait (0, 4, 4), (1, 4, 4), (2, 4, 4), (3, 4, 4), (4, 4, 4). De même pour (0, 0, 3, 3)le deuxième cas de test.
Greg Martin
@GregMartin En raison de la cupidité de la méthode. Vous avez raison, il sera en général sous-optimal. Le nombre minimal de pièces que vous pouvez obtenir par une méthode non gourmande est une question intéressante mais difficile,
Oh, vous voulez dire littéralement qu'une fois que le terme suivant ne correspond pas à la partie "active", alors cette partie est fermée pour toujours. D'accord.
Greg Martin

Réponses:

4

Gelée , 26 25 octets

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

Programme complet qui imprime une représentation d'une liste de listes, chaque liste faisant partie, par exemple pour 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]]]

Remarque: la représentation utilisée supprime les listes redondantes [ et ] autour de longueur 1.

Essayez-le en ligne! ou voir une jolie version imprimée (coût 3 octets)

Comment?

œ&µ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
la source
C'est bien. Je vous remercie.
3

MATLAB, 272 octets

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;

Production:

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

Deux lignes vides entre différentes parties.

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

Explication:

On trouve d'abord tous les multisets avec force brute:

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

repmat(0:n-1, 1, k)répète le vecteur de valeurs de 0à n-1 kfois.

nchoosek(x, k) renvoie une matrice contenant toutes les k-combinaisons du vecteur répété.

sort(x, 2)trie toutes les k-combinaisons, puis unique(x, 'rows')supprime tous les doublons.

p=zeros(0,k);crée une matrice vide avec des kcolonnes. Nous allons l'utiliser comme une pile. A chaque itération de la outernmost forboucle, nous avons d' abord ajouter le multiset courant à ladite pile: p=[p;l(i,:)];.

Ensuite, nous vérifions si l'intersection de tous les multisets de la pile est au moins k-1longue avec le code suivant (nous ne pouvons pas utiliser la intersectcommande de MATLAB pour vérifier les intersections, car elle renvoie un ensemble, mais nous avons besoin d'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;

Maintenant, si l'intersection est suffisamment longue a == 0, sinon a == 1.

Si l'intersection n'est pas assez longue, nous imprimons une nouvelle ligne et vider la pile:

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

Ensuite, nous imprimons simplement le multiset actuel:

disp(l(i,:));
Steadybox
la source
On dirait que tu l'as craqué! Pourriez-vous expliquer votre méthode?
@Lembik J'ai ajouté une explication.
Steadybox
3

MATL , 34 octets

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

Les pièces sont séparées par une ligne contenant des espaces.

Essayez-le en ligne!

Explication

Avertissement: cette méthode semble fonctionner (et elle le fait dans les cas de test), mais je n'ai pas de preuve qu'elle fonctionne toujours

Les multisets sont triés, à la fois en interne (c'est-à-dire que chaque multiset a des entrées non décroissantes) et en externe (c'est à dire que le multiset M précède le multiset N si M précède N lexicographiquement).

Pour calculer l'intersection multisets, les multisets triés sont disposés sous forme de lignes d'une matrice et des différences consécutives sont calculées le long de chaque colonne. Si toutes les colonnes sauf au plus une ont toutes les différences égales à zéro, les multisets appartiennent à la même partie.

Ce test donne un faux résultat négatif pour multijeux comme (1,2,3)et (2,3,4): même si 2, 3sont entrées communes, ils ne seraient pas détectés comme tels parce qu'ils sont dans des colonnes qui ne correspondent pas .

Cependant, cela ne semble pas être un problème, du moins dans les cas de test. Il semble qu'un test entre multisets comme 1,2,3et 2,3,4jamais ne doive être fait, car certains multisets intermédiaires ont donné un résultat négatif et ils sont donc déjà dans des parties différentes. Si c'est effectivement le cas, la raison tient sans doute au fait que les multisets sont triés.

Mais je n'en ai pas la preuve. Cela semble fonctionner.

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
la source
C'est très impressionnant.
J'essaie de comprendre si la méthode que vous décrivez fonctionnera toujours. Je vois que dans le n=k=4cas où nous avons un nouveau début de pièce (0, 0, 3, 3), la différence consécutive vectorisée de cela et le multi-ensemble précédent, (0, 0, 2, 3)n'a qu'une seule différence, alors comment la "pièce jusqu'à présent" fait-elle fonctionner cela? (ou de manière équivalente quel était le résultat de l'étape précédente qui a été utilisé à la place (0, 0, 2, 3)?)
Jonathan Allan
Ah, je vois que vous effectuez une différence consécutive. Oui, cela devrait toujours fonctionner! Vous recherchez littéralement les points auxquels plus d'un élément change, mais plutôt que l'intersection multi-ensembles, simplement l'intersection vectorisée - qui fonctionnera puisque les multi-ensembles sont triés pour commencer.
Jonathan Allan
@JonathanAllan Oui, c'est une différence consécutive plutôt qu'une intersection. Je ne vois toujours pas clairement que cela fonctionnera toujours, mais si vous le dites ... :-)
Luis Mendo
1

PHP, 245 octets

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

Essayez-le en ligne!

Étendu

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

Sortie sous forme de chaîne

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

n> 15 pour plus de précision

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
la source
Cela semble fonctionner! Mais qu'entendez-vous par plus de précision?
@Lembik la version courte rend 0pour (16**16-1)%16et le long travail de la version uniquement avec la précision qui est nécessaire pour n>15 bcmod(bcsub(bcpow(16,16),1),16)se 15 php.net/manual/en/ref.bc.php
Jörg Hülsermann