【问题标题】:Combinatorics: Building 10 groups of 100 elements while elements remain sorted组合学:构建 10 组 100 个元素,同时元素保持排序
【发布时间】:2010-11-24 00:32:11
【问题描述】:

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

问题:

  1. 校园里有 100 个孩子。
  2. 假设值是 100-199 厘米,它们都有独特的高度。
  3. 您要构建 10 个小组,每个小组由 1-99 个孩子组成。
  4. 在孩子们必须按身高排序的情况下,如何构建所有组?
  5. 我需要针对这些群体的所有可能解决方案,因为找到一个星座并不难。

简短易懂:

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

示例(值是高度):

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

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

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

PS:这不是家庭作业。 ;) 通常,我需要一个函数来处理数字。但是我不能像“在对所有数字进行排序的同时构建 k 组数字”那样抽象地描述这一点。我以为你不会这样理解。 :) PHP 的解决方案是最好的,但我也很高兴看到其他语言的解决方案。 :)

【问题讨论】:

  • 最后你不用把100个孩子都放在组里吧?
  • 只是为了检查我理解 - 你的第一个例子是不可能的,因为 190(第一组)大于 126(第二组)?
  • @Bruno Reis:是的,您必须将每个孩子都归为一个组。你是对的,这个例子有点令人困惑。我用“...”更正了它
  • @marco92w:在第二个例子中,孩子 106 在哪里?在 [104, 105, 112, 149] 的左边?还是在它的右边?我不明白为什么这是可能的,而第一个是不可能的。
  • 另一个问题:您是否必须精确地建立 10 个组?还是最多10组?您的问题没有得到很好的解释...

标签: php combinatorics combinations


【解决方案1】:

据我了解,您实际上是在寻求将区间 [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,

【讨论】:

  • 谢谢!这应该是您在 PHP 中的代码:paste.bradleygill.com/index.php?paste_id=15709 不幸的是,它需要很长时间(> 30 秒)或无限运行。代码中的任何地方都有错误吗?也许您可以将您的代码与 Juliet 链接到的代码进行比较?
  • 这是一个递归函数,如果你记忆它会变得更快(即记住你已经计算过的函数的值)。
  • 我同意你对 (25,7) 的结果。我对 (100,10) 的结果是 1731030945644。Java 代码计算它们的速度比 PHP 代码快的原因是记忆;即在返回结果之前将结果缓存在数组中,因此不必重新计算。
  • 你记住的是partition(intervalSize, pieces)的值,否则你必须为每个分区重新计算它(非常慢)。
  • 为了明确说明,3 个有序分区 12|34|5、12|3|45 和 1|23|45 将被计为单个无序分区 (5=2+2+1 )。因为我们多次计算一些无序分区,所以我们的值大于 p(100)。
【解决方案2】:

我需要所有可能的解决方案 这些群体,因为不难 找到一个星座。

通常有 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 个项目的集合,这很容易通过使用费雷尔图进行枚举。

我可以通过将数字 20 划分为 3 个桶来演示费雷尔图:从 20 列 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轻松解决问题。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2011-10-29
  • 2020-02-20
  • 1970-01-01
  • 2019-08-13
  • 2022-06-24
  • 1970-01-01
  • 2020-06-21
  • 2019-09-13
相关资源
最近更新 更多