【问题标题】:Next Composition of n into k parts - does anyone have a working algorithm? [closed]Next 将 n 组合成 k 部分 - 有人有有效的算法吗? [关闭]
【发布时间】:2011-01-10 13:04:53
【问题描述】:

nk 部分的组合 - 我想将 n 的所有可能组合列出到 k 部分 - 有没有人有算法(最好在 R 中)?或者知道它是否在任何地方的图书馆?

例如,如果我有 n 个立方体和 k 个袋子,并且想要列出袋子中立方体的所有可能排列方式。例如有 3 种方法可以将 2 个立方体放入 2 个袋子中:

(2, 0) (1, 1) (0, 2)

我找到了新汉对数。我在 Fortran 中找到了它的一个版本 here(第 46 页),但不要在 Fortran 中编码,所以真正了解发生了什么 - 有什么帮助吗?

【问题讨论】:

    标签: combinatorics


    【解决方案1】:

    由于我花了一些精力来阅读其他 c++ 解决方案的意图,这里翻译为 python(也作为生成器结果而不是字符串):

    def weak_compositions(boxes, balls, parent=tuple()):
      if boxes > 1:
        for i in xrange(balls + 1):
          for x in weak_compositions(boxes - 1, i, parent + (balls - i,)):
            yield x
      else:
        yield parent + (balls,)
    

    测试:

    >>> for x in weak_compositions(3, 5): print x
    (5, 0, 0)
    (4, 1, 0)
    (4, 0, 1)
    (3, 2, 0)
    ...
    (0, 1, 4)
    (0, 0, 5)
    

    【讨论】:

    • FWIW,我已将 Python 3 版本的代码添加到 my answer,它使用 itertools.combinations 生成弱分区。
    【解决方案2】:

    您尝试列出的内容称为 k-multicombination。问题经常这样表述:给定 n 个无法区分的 个球和 k 个盒子,列出所有可能的方式来分配盒子中的所有球。此类分布的数量为:

    factorial(n + k - 1) / (factorial(k - 1) * factorial(n))
    

    有关更多背景信息,请参阅Twelve-Fold Way 的方法 4。

    这里是枚举分布的代码(C++):

    string & ListMultisets(unsigned au4Boxes, unsigned au4Balls, string & strOut = string ( ), string strBuild = string ( ))
    {
        unsigned au4;
        if (au4Boxes > 1) for (au4 = 0; au4 <= au4Balls; au4++)
        {
            stringstream ss;
            ss << strBuild << (strBuild.size() == 0 ? "" : ",") << au4Balls - au4;
            ListMultisets (au4Boxes - 1, au4, strOut, ss.str ( ));
        }
        else
        {
            stringstream ss;
            ss << "(" << strBuild << (strBuild.size() == 0 ? "" : ",") << au4Balls << ")\n";
            strOut += ss.str ( );
        }
        return strOut;
    }
    
    
    
    int main(int argc, char * [])
    {    
        cout << endl << ListMultisets (3, 5) << endl;
        return 0;
    }
    

    这是上述程序的输出(5 个球分布在三个盒子上):

    (5,0,0)
    (4,1,0)
    (4,0,1)
    (3,2,0)
    (3,1,1)
    (3,0,2)
    (2,3,0)
    (2,2,1)
    (2,1,2)
    (2,0,3)
    (1,4,0)
    (1,3,1)
    (1,2,2)
    (1,1,3)
    (1,0,4)
    (0,5,0)
    (0,4,1)
    (0,3,2)
    (0,2,3)
    (0,1,4)
    (0,0,5)
    

    【讨论】:

    • 等式不是factorial(n * k * 1) / ( factorial(k - 1) * factorial(n)) 吗?我在问,因为我刚刚了解了弱构图。
    • @MattMunson,方程对于 n-multicombination 是正确的,但我在文本中描述了 k-multicombination。我进行了另一次编辑以反映更改。
    【解决方案3】:

    计算组合(我忽略了这个术语的标准)和组合在某种意义上是等价的。在k + 1 中的k + 1 组合和n 组合到k 部分之间存在双射函数。只需为组合中的每个字母分配一个从 1 到n 的数字,然后根据它们的数字对字母进行排序,然后:

    • 制作一个由连续字母数之间的差异组成的元组
    • 元组的每个条目都减去 1,然后就得到了。

    假设您计算组合的算法产生具有“有序字母”的组合,那么剩下的就是简单的计算。

    在 Python 中:

    from itertools import combinations, tee 
      #see: 
      #http://docs.python.org/library/itertools.html#itertools.combinations
      #http://docs.python.org/library/itertools.html#itertools.tee
    
    

    def diffed_tuple(t): # return a new tuple but where the entries are the differences # between consecutive entries of the original tuple. #make two iterator objects which yield entries from t in parallel t2, t1 = tee(t) # advance first iterator one step for x in t2: break # return a tuple made of the entries yielded by the iterators return tuple(e2 - e1 for e2, e1 in zip(t2, t1))

    # --The Algorithm-- def compositions(n, k): for t in combinations(range(n+k), k+1): # yield the 'diffed tuple' but subtracting 1 from each entry yield tuple(e-1 for e in diffed_tuple(t))

    【讨论】:

    • 我不是专家,所以我不愿编辑答案,但我相信这里给出的算法计算 weak compositions,而不是合成。不同之处在于,零是弱组合的可能元素,而不是组合的可能元素。
    • 其实我觉得这个算法根本行不通。 compositions(2, 3) 返回一个生成器, (0, 0, 0) (0, 0, 1) (0, 1, 0) (1, 0, 0) (0, 0, 0) 这些是 not 2 的组成,其中一个是重复的!
    【解决方案4】:

    我已将原始新汉算法翻译成结构化的 Fortran 和 Java。 Java版本是:

    public void allCombinations(final int n, final int k) {
        final int[] input = new int[k + 1];
        Boolean mtc = Boolean.FALSE;
        int t = n;
        int h = 0;
        do {
            if (mtc) {
                if (t > 1) {
                    h = 0;
                }
                h++;
                t = input[h];
                input[h] = 0;
                input[1] = t - 1;
                input[h + 1]++;
            } else {
                // First permutation is always n00...0 i.e. it's a descending
                // series lexicographically.
                input[1] = n;
                for (int i = 2; i <= k; i++) {
                    input[i] = 0;
                }
            }
            System.out.println(java.util.Arrays.toString(input));
            mtc = input[k] != n;
        } while (mtc);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-27
      • 1970-01-01
      • 2010-09-07
      相关资源
      最近更新 更多