贪婪地划分重复的组合列表


10

首先,一些定义:

  • 给定nk,考虑多重集的排序列表,对于每个多重集,我们从中选择带有重复的k数字{0, 1, ..., n-1}

例如,对于n=5k=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。

任务

您的代码应带有两个参数nk。然后,它应该按排序的顺序贪婪地遍历这些多集并输出列表的各个部分。对于这种情况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因为方法的贪婪。没错,它通常不是最佳选择。如果不是一个难题,可以通过非贪婪方法获得的零件数量最少是一个有趣的问题

哦,您的意思是,一旦下一个术语与“活动”部分不匹配,那么该部分将永远关闭。好。
格雷格·马丁

Answers:


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列创建一个空矩阵。我们将其用作堆栈。在最外层for循环的每次迭代中,我们首先将当前的多集添加到所述堆栈:p=[p;l(i,:)];

然后k-1,使用以下代码检查堆栈中所有多重集的交点是否至少长(我们无法使用MATLAB的intersect命令检查相交,因为它返回了一个集合,但我们需要一个多重集):

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):即使23是常见的项目,他们就不会这样,因为它们是不匹配的列进行检测。

但是,至少在测试用例中,这似乎不是问题。似乎必须在多个集之间进行测试,1,2,32,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)什么?)
Jonathan Allan

啊,我看到你表现出连续的不同。是的,这应该一直有效!您实际上是在寻找多个项目发生变化的点,而不是多集交集,而是简单的矢量化交集-这将起作用,因为已对多集进行排序。
乔纳森·艾伦

@JonathanAllan是的,它是连续的差异而不是交集。我仍然不清楚它是否会一直有效,但如果您这么说……:-)
Luis Mendo

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

输出为字符串

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.