组合:构建10组100个元素,同时元素保持排序

时间:2009-08-29 16:30:15

标签: php combinatorics combinations

我遇到了有关组合学的问题。不幸的是,我无法抽象地描述它,所以我试着把它解释为一个故事。 :)

问题:

  1. 校园里有100个孩子。
  2. 假设值为100-199cm,它们都具有独特的高度。
  3. 你想建立10个小组,每个小组由1-99个孩子组成。
  4. 如何根据孩子的身高对孩子进行分类,如何建立所有群体?
  5. 我需要为这些群体提供所有可能的解决方案,因为找到一个星座并不难。
  6. 简单易行:

    所有100名儿童并排站立。您只需决定将它们分组到哪里并找到所有解决方案。

    示例(值是高度):

    [120 ... 190 ... 199] ... [126 ... 137 ... 144 ... 188] 是不可能的

    [101] ... [104 ... 105 ... 112 ... 149] ... [169 ... 189] 是可能的

    我希望你能帮助我。非常感谢你提前!

    PS:这不是功课。 ;)通常,我需要一个用数字执行此操作的函数。但我无法将其描述为“在所有数字排序时构建k组数字”。我以为你不会这样理解。 :) PHP中的解决方案是最好的,但我很高兴看到其他语言的解决方案。 :)

2 个答案:

答案 0 :(得分:4)

据我所知,你实际上是在想方法将区间[100,199]分成10个部分,即你想找到数字x [0],...,x [10],这样:

x[0] = 100 < x[1] < x[2] < ... < x[9] < x[10] = 199

定义一个递归函数partition(intervalSize, pieces),它计算分区给定间隔的方法数。你在partition(100, 10)之后。

以下Java代码对分区进行计数(抱歉,不熟悉PHP):

public class Partitions
{
    static long[][] partitions = new long[100][10];

    private static long partition(int intervalSize, int pieces)
    {
        if (partitions[intervalSize-1][pieces-1] != 0) {
            return partitions[intervalSize-1][pieces-1];
        }
        long partition = 0L;
        if (pieces == 1) {
            partition = 1L;
        } else {
            for (int i = 1; i <= intervalSize - 1; i++) {
                partition += partition(intervalSize - i, pieces - 1);
            }
        }
        partitions[intervalSize-1][pieces-1] = partition;
        return partition;
    }

    public static void main(String[] args)
    {
        System.out.println(partition(100, 10));     
    }

}

以下Java代码打印出实际分区。因为(100,10)的分区数量很高,我正在解释(5,3)的答案:

public class Partitions2
{
    private static void showPartitions(int sizeSet, int numPartitions)
    {
        showPartitions("", 0, sizeSet, numPartitions);
    }

    private static void showPartitions(String prefix, int start, int finish,
            int numLeft)
    {
        if (numLeft == 0 && start == finish) {
            System.out.println(prefix);
        } else {
            prefix += "|";
            for (int i = start + 1; i <= finish; i++) {
                prefix += i + ",";
                showPartitions(prefix, i, finish, numLeft - 1);
            }
        }
    }

    public static void main(String[] args)
    {
        showPartitions(5, 3);
    }
}

输出结果为:

|1,|2,|3,4,5,
|1,|2,3,|4,5,
|1,|2,3,4,|5,
|1,2,|3,|4,5,
|1,2,|3,4,|5,
|1,2,3,|4,|5,

答案 1 :(得分:0)

  

我需要所有可能的解决方案   这些团体既然不难   找到一个星座。

通常,有100个!如何排列100个项目,但由于您保留了订单,因此可以将问题大小缩小 。你所描述的是integer partitioning problem。例如,假设您将数字5划分为所有可能的整数子集,最多可加5,您将得到集合{5},{4,1},{3,2},{3,1,1 ,},{2,2,1},{2,1,1,1},{1,1,1,1,1}。

整数分区的数量随整数的大小呈指数增长,但指数增长足够慢,您可以枚举n = 100的所有分区,因为它们只有190,569,292。这里的附加约束是你想过滤包含10个项目的所有分区,这很容易通过使用Ferrer图来枚举。

我可以通过将数字20划分为3个桶来演示Ferrer图:从20 col x 3行网格开始,如下所示:

 12345678901234567890
1******************
2*
3*

所以,第一个分区是{18,1,1}

现在将项目从堆栈顶部移动到下一个插槽中:

 12345678901234567890
1*****************
2**
3*

我们的新分区是{17,2,1}。我们可以从一个堆栈到另一个堆栈中的另一个项目:

 12345678901234567890
1****************
2***
3*

现在我们有{16,3,1}。你继续这种方式,直到你列举所有的排列(由你来决定{17,1,2}是否是{17,2,1}的独特分区。)

从这一点开始,您应该能够设想所需算法的大纲 - 也就是说,如果您想要从头开始编写此函数的挑战。其他人已经written lots of efficient functions轻松解决问题。