Dengan rakus membagi daftar kombinasi dengan pengulangan

10

Pertama, beberapa definisi:

  • Diberikan ndan k, pertimbangkan daftar multiset yang diurutkan , di mana untuk setiap multiset kami memilih kangka {0, 1, ..., n-1}dengan pengulangan.

Misalnya, untuk n=5dan k=3, kami memiliki:

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

  • Sebuah bagian adalah daftar multisets dengan properti yang ukuran persimpangan semua multisets di bagian setidaknya k-1. Yaitu kita mengambil semua multiset dan memotongnya (menggunakan persimpangan multiset) sekaligus. Sebagai contoh, [(1, 2, 2), (1, 2, 3), (1, 2, 4)]adalah bagian karena persimpangannya berukuran 2, tetapi [(1, 1, 3),(1, 2, 3),(1, 2, 4)]tidak, karena persimpangannya berukuran 1.

Tugas

Kode Anda harus mengambil dua argumen ndan k. Maka harus dengan rakus pergi melalui multisets ini dalam urutan dan output bagian daftar. Untuk kasus ini n=5, k=3, partisi yang benar adalah:

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

Berikut adalah contoh lain untuk 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)

Klarifikasi tentang apa yang serakah artinya: Untuk setiap multiset pada gilirannya kita melihat apakah itu dapat ditambahkan ke bagian yang ada. Kalau bisa kita tambahkan saja. Jika tidak bisa kita mulai bagian baru. Kami melihat multiset dalam urutan seperti pada contoh di atas.

Keluaran

Anda dapat menampilkan partisi dalam format apa pun yang Anda suka. Namun, multiset harus ditulis secara horizontal pada satu baris. Itu adalah multiset individu tidak boleh ditulis secara vertikal atau tersebar di beberapa baris. Anda dapat memilih bagaimana Anda memisahkan representasi bagian-bagian dalam output.

Asumsi

Kita bisa berasumsi itu n >= k > 0.

Jonathan Allan
sumber
@LuisMendo Saya baru saja membuat kesalahan. Maksud saya multiset harus ditulis secara horizontal pada satu baris.
Pada test case pertama, mengapa (0, 4, 4)dengan sendirinya? Dengan uraian Anda, saya akan berpikir "bagian" akan menjadi (0, 4, 4), (1, 4, 4), (2, 4, 4), (3, 4, 4), (4, 4, 4). Demikian pula untuk (0, 0, 3, 3)pada test case kedua.
Greg Martin
@GregMartin Karena keserakahan metode ini. Anda benar bahwa secara umum akan menjadi suboptimal. Jumlah minimal bagian yang bisa Anda peroleh dengan metode yang tidak serakah adalah pertanyaan yang menarik jika sulit,
Oh, Anda benar-benar berarti bahwa sekali istilah berikutnya tidak cocok dengan bagian "aktif", maka bagian itu ditutup selamanya. Baik.
Greg Martin

Jawaban:

4

Jelly , 26 25 byte

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

Program lengkap yang mencetak representasi dari daftar daftar, setiap daftar menjadi bagian, misalnya untuk 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]]]

Catatan: representasi yang digunakan menghilangkan daftar panjang 1 [ dan sekitar redundan ] .

Cobalah online! atau lihat versi cetak yang cantik (biaya 3 byte)

Bagaimana?

œ&µ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
sumber
Ini bagus. Terima kasih.
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;

Keluaran:

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

Dua garis kosong antara bagian yang berbeda.

Tidak Disatukan:

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;

Penjelasan:

Pertama kita menemukan semua multiset dengan kekuatan kasar:

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

repmat(0:n-1, 1, k)mengulangi vektor nilai dari waktu 0ke n-1 kwaktu.

nchoosek(x, k) mengembalikan sebuah matriks yang berisi semua kombinasi k dari vektor yang diulang.

sort(x, 2)mengurutkan semua kombinasi-k, dan kemudian unique(x, 'rows')menghapus semua duplikat.

p=zeros(0,k);membuat matriks kosong dengan kkolom. Kami akan menggunakannya sebagai tumpukan. Pada setiap iterasi dari outernmost forlingkaran, pertama kita tambahkan multiset saat ini untuk mengatakan stack: p=[p;l(i,:)];.

Kemudian kami memeriksa apakah persimpangan semua multiset dalam tumpukan setidaknya k-1panjang dengan kode berikut (kami tidak dapat menggunakan intersectperintah MATLAB untuk memeriksa persimpangan, karena mengembalikan set, tetapi kami membutuhkan 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;

Sekarang, jika persimpangan cukup panjang a == 0,, sebaliknya a == 1.

Jika persimpangan tidak cukup panjang, kami mencetak baris baru dan mengosongkan tumpukan:

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

Kemudian kita cukup mencetak multiset saat ini:

disp(l(i,:));
Steadybox
sumber
Sepertinya kamu sudah memecahkannya! Bisakah Anda menjelaskan metode Anda?
@Lembik saya menambahkan penjelasan.
Steadybox
3

MATL , 34 byte

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

Bagian-bagian dipisahkan oleh garis yang berisi spasi putih.

Cobalah online!

Penjelasan

Penafian: metode ini tampaknya berhasil (dan memang ada dalam kasus uji), tapi saya tidak punya bukti bahwa itu selalu berhasil

Multiset diurutkan, baik secara internal (yaitu masing-masing multiset memiliki entri yang tidak berkurang) dan secara eksternal (yaitu multiset M datang sebelum multiset N jika M mendahului N secara leksikografis).

Untuk menghitung persimpangan multiset, multiset yang diurutkan disusun sebagai baris matriks dan perbedaan berturut-turut dihitung di sepanjang setiap kolom. Jika semua kolom kecuali paling banyak satu memiliki semua perbedaan sama dengan nol, multiset milik bagian yang sama.

Tes ini akan memberikan hasil negatif palsu untuk multiset seperti (1,2,3)dan (2,3,4): bahkan jika 2, 3entri umum, mereka tidak akan terdeteksi karena mereka berada di kolom yang tidak cocok.

Namun, ini tampaknya tidak menjadi masalah, setidaknya dalam kasus uji. Tampaknya tes antara multiset suka 1,2,3dan 2,3,4tidak pernah benar-benar harus dilakukan, karena beberapa multiset menengah memberikan hasil negatif sehingga mereka sudah berada di bagian yang berbeda. Jika ini memang benar, alasannya pasti ada hubungannya dengan fakta bahwa multisets diurutkan.

Saya tidak punya bukti tentang ini. Sepertinya berfungsi.

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
sumber
Sangat mengesankan.
Saya mencoba memahami jika metode yang Anda gambarkan akan selalu berhasil. Saya melihat bahwa dalam n=k=4kasus kita memiliki bagian baru mulai (0, 0, 3, 3), perbedaan berturut-turut yang di -vektor-kan dari itu dan multi-set sebelumnya, (0, 0, 2, 3)hanya memiliki satu perbedaan, jadi bagaimana "bagian sejauh ini" membuat pekerjaan ini? (atau setara dengan apa hasil langkah sebelumnya yang digunakan alih-alih (0, 0, 2, 3)?)
Jonathan Allan
Ah, saya melihat Anda melakukan perbedaan berturut-turut. Ya ini harus selalu berhasil! Anda benar-benar mencari titik di mana lebih dari satu item berubah, tetapi daripada persimpangan multi-set, hanya persimpangan vektor - yang akan bekerja sejak set mutli diurutkan untuk memulai.
Jonathan Allan
@ Jonathan Allan Ya, ini adalah perbedaan berturut-turut daripada persimpangan. Saya masih tidak melihat dengan jelas bahwa itu akan selalu berhasil, tetapi jika Anda mengatakan demikian ... :-)
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);

Cobalah online!

Diperluas

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

Output sebagai String

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

n> 15 untuk lebih presisi

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
sumber
Ini sepertinya berhasil! Tetapi apa yang Anda maksud dengan lebih presisi?
@Lembik versi pendek memberikan kembali 0untuk (16**16-1)%16dan pekerjaan versi panjang hanya dengan presisi yang diperlukan untuk n>15 bcmod(bcsub(bcpow(16,16),1),16)adalah 15 php.net/manual/en/ref.bc.php
Jörg Hülsermann