智能生成组合的组合

发布于 2024-08-08 10:42:57 字数 381 浏览 3 评论 0原文

假设我有一个 30 名学生的班级,想要生成各种可能的方式将他们分成 5 人一组(顺序无关)。

我知道如何找到学生的所有组合以单独组成一个小组(http://www.merriampark. com/comb.htm)。通过使用该迭代器和一些递归,我可以找到可能的组组合的排列。但是,选择组的顺序并不相关,我想最大限度地减少执行时间。那么我如何找到可能组的独特组合呢?

上面的算法使用字典顺序来避免生成重复的组合...有没有一种方法可以让我在组而不是对象上使用这个想法?

我很了解 Ruby,但不太了解 Java/Python。预先感谢您的任何建议!

Let's say I have a class of 30 students and want generate every possible way in which they can be partitioned into groups of 5 (order is irrelevant).

I know how to find all the combinations of students to form one group individually (http://www.merriampark.com/comb.htm). By using that iterator and some recursion, I can find PERMUTATIONS of the possible group combinations. However, order in which the groups are selected isn't relevant and I'd like to minimize my execution time. So how do I find the unique COMBINATIONS of the possible groups?

The above algorithm uses lexicographical ordering to avoid generating duplicate combinations... is there a way that I can use that idea on groups instead of on objects?

I know Ruby well and Java/Python less well. Thanks in advance for any advice!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

兔小萌 2024-08-15 10:42:57

嗯,有 (30C5*25C5*20C5*15C5*10<子>C5*5C5)/6! = 30!/(6!*5!6) = 123,378,675,083,039,376 个不同的分区,每组 30 个,每组 5 个,因此无论使用什么方法,生成它们都需要一些时间。

不过,一般来说,选择此类分区的一个好方法是对元素使用某种排序,并找到最高未分组元素的分组,然后对其余元素进行分组。

     find_partition = lambda do |elts|
        if elts.empty?
         [[]]
        else
         highest = elts.pop
         elts.combination(4).map do |others|
            find_partition[elts - others].map { |part| part << [highest,*others] }
         end.inject(:+)
        end
     end
     find_partition[(1..30).to_a]

这样您只需生成每个分区一次

Well, there's (30C5*25C5*20C5*15C5*10C5*5C5)/6! = 30!/(6!*5!6) = 123,378,675,083,039,376 different partitons of 30 into groups of 5, so generating them all will take some time, no matter what method you use.

In general, though, a good method to selecting such a partition is to use some ordering on the elements, and find the grouping for the highest ungrouped element, and then group the rest.

     find_partition = lambda do |elts|
        if elts.empty?
         [[]]
        else
         highest = elts.pop
         elts.combination(4).map do |others|
            find_partition[elts - others].map { |part| part << [highest,*others] }
         end.inject(:+)
        end
     end
     find_partition[(1..30).to_a]

This way you're only generating each partition once

风月客 2024-08-15 10:42:57

这是一个老问题,但无论如何,为了记录,这就是我在 Ruby 中的方式:

class Array
  def groups_of_size(n)
    Enumerator.new do |yielder|
      if self.empty?
        yielder.yield([])
      else
        self.drop(1).combination(n-1).map { |vs| [self.first] + vs }.each do |values|
          (self - values).groups_of_size(n).each do |group|
            yielder.yield([values] + group)
          end   
        end
      end
    end
  end
end

我使用枚举器,因为输出可以快速增长,严格的输出(例如数组)不会有用。一个使用示例:

>> pp [0, 1, 2, 3, 4, 5].groups_of_size(3).to_a
=> 
[[[0, 1, 2], [3, 4, 5]],
 [[0, 1, 3], [2, 4, 5]],
 [[0, 1, 4], [2, 3, 5]],
 [[0, 1, 5], [2, 3, 4]],
 [[0, 2, 3], [1, 4, 5]],
 [[0, 2, 4], [1, 3, 5]],
 [[0, 2, 5], [1, 3, 4]],
 [[0, 3, 4], [1, 2, 5]],
 [[0, 3, 5], [1, 2, 4]],
 [[0, 4, 5], [1, 2, 3]]]

This is an old question, but anyway, for the record, that's how I would it in Ruby:

class Array
  def groups_of_size(n)
    Enumerator.new do |yielder|
      if self.empty?
        yielder.yield([])
      else
        self.drop(1).combination(n-1).map { |vs| [self.first] + vs }.each do |values|
          (self - values).groups_of_size(n).each do |group|
            yielder.yield([values] + group)
          end   
        end
      end
    end
  end
end

I use an enumerator because the output can grow very quickly, a strict output (an array for example) wouldn't be useful. A usage example:

>> pp [0, 1, 2, 3, 4, 5].groups_of_size(3).to_a
=> 
[[[0, 1, 2], [3, 4, 5]],
 [[0, 1, 3], [2, 4, 5]],
 [[0, 1, 4], [2, 3, 5]],
 [[0, 1, 5], [2, 3, 4]],
 [[0, 2, 3], [1, 4, 5]],
 [[0, 2, 4], [1, 3, 5]],
 [[0, 2, 5], [1, 3, 4]],
 [[0, 3, 4], [1, 2, 5]],
 [[0, 3, 5], [1, 2, 4]],
 [[0, 4, 5], [1, 2, 3]]]
吹泡泡o 2024-08-15 10:42:57

您可以对排列进行一些后处理。一些伪代码(用您选择的语言实现......):

// We have a list of lists called 'permutations'
// combinations is an (empty) list of lists
for each permutation in permutations
{
   sortedPermutation = permutation.sort()
   if (! combinations.find(sortedPermutation) )
   {
       combinations.add(sortedPermutation);
   }
}

可能不是最有效的;我会添加排序 &与亲自生成排列的代码进行比较。

You could do some post-processing on the permutations. Some pseudo-code (implement in the language of your choice...):

// We have a list of lists called 'permutations'
// combinations is an (empty) list of lists
for each permutation in permutations
{
   sortedPermutation = permutation.sort()
   if (! combinations.find(sortedPermutation) )
   {
       combinations.add(sortedPermutation);
   }
}

Probably not the most efficient; I'd add the sort & compare to the code that generates the permutations personally.

东走西顾 2024-08-15 10:42:57

一种可能性是找到所有组合来形成一个单独的组,然后遍历并生成不包含该单独组的成员的组合。类似于:

List<List<Student>> combinations=Combinations(students);
public void GenerateCombinations(int startingIndex, List<List<Student>> currentGroups, int groupsLeft)
{
    if(groupsLeft==0) ProcessCombination(currentGroups);

    for(int i=startingIndex; i<combinations.Count; i++)
    {
        if combinations[i] does not contain a student in current groups
            GenerateCombinations(i+1, currentGroups + combinations[i], groupsLeft -1);
    }
}

这不是最有效的方法,但它应该生成所有组的组合。我怀疑如果您要生成临时组合列表,其中所有不能出现的组都被删除,那么可以获得更好的性能,但这会更复杂一些。

顺便说一句,30 名学生组成 5 人一组,应该有 142,506 种组合。我的<讽刺>太棒了数学技能建议应该有大约 10^17 = 100 万亿个学生组组合 (30!/((5!^6)*6!); 30! 学生的排序,6 组 5 人的排序并不重要,并且这 6 组的顺序并不重要)。您可能会坐在那里等待这一切完成。

One possibility would be to find all combinations to form an individual group, then go through and generate combinations that don't contain members of that individual group. Something like:

List<List<Student>> combinations=Combinations(students);
public void GenerateCombinations(int startingIndex, List<List<Student>> currentGroups, int groupsLeft)
{
    if(groupsLeft==0) ProcessCombination(currentGroups);

    for(int i=startingIndex; i<combinations.Count; i++)
    {
        if combinations[i] does not contain a student in current groups
            GenerateCombinations(i+1, currentGroups + combinations[i], groupsLeft -1);
    }
}

It won't be the most efficient method to go about it, but it should generate all combinations of groups. I suspect better performance could be had if you were to generate temporary lists of combinations, where in all groups that can't occur were removed, but that would be a bit more complex.

As a slight aside, there should be 142,506 combinations of 30 students to form a single group of 5. My <sarcasm> awesome </sarcasm> math skills suggest that there should be about 10^17 = 100 quadrillion combinations of groups of students (30!/((5!^6)*6!); 30! orderings of students, ordering of 6 groups of 5 does not matter, and ordering of those 6 groups doesn't matter). You might be sitting there a while waiting for this to finish.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文