Жадібно розділіть список комбінацій з повторенням


10

По-перше, кілька визначень:

  • Враховуючи nі k, розглянемо відсортований список мультисетів , де для кожного мультисету ми вибираємо kчисла {0, 1, ..., n-1}із повторами.

Наприклад, для 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)]

  • Частина являє собою список мультимножин з тим властивістю , що розмір перетину всіх мультимножин в частині, щонайменше k-1. Тобто ми беремо всі мультисети і перетинаємо їх (використовуючи мультисезонне перехрестя) всі відразу. Як приклад, [(1, 2, 2), (1, 2, 3), (1, 2, 4)]це частина, оскільки її перетин має розмір 2, але [(1, 1, 3),(1, 2, 3),(1, 2, 4)]це не так, оскільки його перетин має розмір 1.

Завдання

Ваш код повинен містити два аргументи nі k. Потім слід жадібно пройти ці мультисети в упорядкованому порядку і вивести частини списку. Для випадку 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)

Ось ще один приклад для 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)

Роз'яснення того, що означає жадібність: Для кожного мультисети по черзі ми дивимось, чи можна його додати до існуючої частини. Якщо ми можемо, ми додамо його. Якщо це не вдається, ми починаємо нову частину. Ми дивимося на мультисети в упорядкованому порядку, як у наведеному вище прикладі.

Вихідні дані

Ви можете вивести розділ у будь-якому розумному форматі, який вам подобається. Однак мультисети слід записати горизонтально на одному рядку. Тобто окремий мультисет не повинен писатися вертикально або розкидатися на кілька рядків. Ви можете вибрати, як ви розділите подання частин у висновку.

Припущення

Ми можемо припустити, що n >= k > 0.


@LuisMendo Я щойно помилився. Я мав на увазі, що мультисети повинні писатися горизонтально на одному рядку.

У першому тестовому випадку, чому саме (0, 4, 4)по собі? З огляду на ваш опис, я думаю, що його "частиною" буде (0, 4, 4), (1, 4, 4), (2, 4, 4), (3, 4, 4), (4, 4, 4). Аналогічно (0, 0, 3, 3)у другому тестовому випадку.
Грег Мартін

@GregMartin Через жадібність методу. Ви праві, що це взагалі буде неоптимальним. Мінімальна кількість деталей, які ви можете отримати не жадібним методом, є цікавим, якщо важким питанням,

О, ви буквально маєте на увазі, що коли наступний термін не відповідає "активній" частині, то ця частина буде закрита назавжди. Добре.
Грег Мартін

Відповіді:


4

Желе , 26 25 байт

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

Повна програма, яка друкує подання списку списків, кожен з яких є частиною, наприклад, для 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]]]

Примітка: використовуване представлення видаляє надлишки [ та ] навколо списків довжиною 1.

Спробуйте в Інтернеті! або переглянути гарну версію для друку (вартість 3 байти)

Як?

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

Це чудово. Дякую.

3

MATLAB, 272 байти

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;

Вихід:

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

Дві порожні лінії між різними частинами.

Безголівки:

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;

Пояснення:

Спочатку ми знаходимо всі мультисети з грубою силою:

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

repmat(0:n-1, 1, k)повторює вектор значень від 0до n-1 kчасу.

nchoosek(x, k) повертає матрицю, що містить усі k-комбінації повторного вектора.

sort(x, 2)сортує всі k-комбінації, а потім unique(x, 'rows')видаляє всі дублікати.

p=zeros(0,k);створює порожню матрицю зі kстовпцями. Ми будемо використовувати його як стек. На кожній ітерації outernmost forциклу ми спочатку додати поточний мультімножество в зазначений стек: p=[p;l(i,:)];.

Потім ми перевіряємо, чи перетин усіх мультисетів у стеку принаймні k-1довгий із наступним кодом (ми не можемо використовувати intersectкоманду MATLAB для перевірки перехресть, оскільки він повертає набір, але нам потрібен мультисет):

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;

Тепер, якщо перехрестя досить довге a == 0, інакше a == 1.

Якщо перехрестя недостатньо довге, ми друкуємо новий рядок і очищаємо стек:

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

Тоді ми просто надрукуємо поточний мультисет:

disp(l(i,:));

Схоже, ти це зламав! Чи можете ви пояснити свій метод?

@Lembik Я додав пояснення.
Steadybox

3

MATL , 34 байти

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

Частини розділені лінією, що містить пробіл.

Спробуйте в Інтернеті!

Пояснення

Відмова: цей метод, здається, працює (і він є в тестових випадках), але у мене немає доказів того, що він завжди

Мультисети сортуються як внутрішньо (тобто кожен мультисет має не зменшувані записи), так і зовні (тобто мультисети M надходять до багатосезонного N, якщо M передує N лексикографічно).

Для обчислення мультисезонного перетину відсортовані мультисети розташовані у вигляді рядків матриці, а послідовні відмінності обчислюються вздовж кожного стовпця. Якщо всі стовпці, крім максимум одного, мають всі відмінності, що дорівнюють нулю, мультисети належать до тієї ж частини.

Цей тест може дати хибний негативний результат для мультимножин , як (1,2,3)і (2,3,4): навіть якщо 2, 3загальні дані, вони не будуть виявлений , як такі , тому що вони знаходяться в незбіжних шпальтах.

Однак це, здається, не є проблемою, принаймні у тестових випадках. Виявляється, тест між мультисетами, як 1,2,3і 2,3,4ніколи насправді не потрібно робити, оскільки деякі проміжні мультисети дали негативний результат, і тому вони вже є в різних частинах. Якщо це дійсно так, то причина без сумніву пов'язана з тим, що мультисети сортуються.

Я не маю доказів цього. Це, здається, працює.

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

Це дуже вражає.

Я намагаюся зрозуміти, чи завжди буде працювати метод, який ви описуєте. Я бачу, що у n=k=4випадку, коли ми починаємо нову частину (0, 0, 3, 3), векторизована послідовна різниця цього та попереднього множинного набору (0, 0, 2, 3)має лише одну різницю, тож як "частина поки що" робить цю роботу? (або рівнозначно, який був результат попереднього кроку, який використовувався замість (0, 0, 2, 3)?)
Джонатан Аллан

Ах, я бачу, ви виконуєте різницю поспіль. Так, це завжди має працювати! Ви буквально шукаєте точки, в яких змінюється більше одного елемента, а не багатонабірне перетин, просто векторизований перетин - який буде працювати з моменту множини множин.
Джонатан Аллан

@JonathanAllan Так, це швидше різниця, ніж перехрестя. Я все ще не бачу зрозуміло, що це завжди буде працювати, але якщо ти так скажеш ... :-)
Луїс Мендо

1

PHP, 245 байт

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

Спробуйте в Інтернеті!

Розширено

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

Виведіть як String

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

n> 15 для більшої точності

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

Це, здається, працює! Але що ви маєте на увазі з більшою точністю?

@Lembik коротка версія повертає 0для (16**16-1)%16і довга версія працює тільки з точністю, яка необхідна для n>15 bcmod(bcsub(bcpow(16,16),1),16)є 15 php.net/manual/en/ref.bc.php
Йорг Hülsermann
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.