一种无重复的成对组合组合的有效方法吗?

时间:2019-01-30 18:50:49

标签: ruby combinations combinatorics

在我开始之前,我需要承认我不是数学家,并且在可能的情况下,需要以外行术语进行解释。感谢您的耐心配合。

问题:我想在整个学年中配对n名学生组成的班级。

对的数量为n / 2。我想让与新朋友一起工作的学生尽可能地多,从而用尽所有可能的组合。排列顺序无关紧要-学生1 +学生2与学生2 +学生1相同。

构建所有非重复的组合对的有效方法是什么?

一种方法(由Aleksei Malushkin建议)是找到所有唯一的对,然后通过强行淘汰所有无效的组来找到n / 2对组的所有组合。

在Ruby中查找所有唯一对是微不足道的:[1,2,3,4,5,6,7,8].combination(2).to_a提供了所有2个项对的数组。

但是,我需要的是制作每组4对的所有小组,每小组不重复学生。如果班上有20名学生,我将需要所有10组对。

暴力破解方法会创建所有配对的组合,并丢弃包含重复配对的配对,但这会随着学生人数的增加而迅速消失。

以下是可解决8位学生的红宝石代码:

# Ruby 2.5.3

students = [1,2,3,4,5,6,7,8]
possible_pairs = students.combination(2).to_a  # https://ruby-doc.org/core-2.4.1/Array.html#method-i-combination

puts "Possible pairs: (#{possible_pairs.size}) #{possible_pairs}"
puts "Possible pair combinations: #{possible_pairs.combination(a.size/2).size}"

groups_without_repetition = possible_pairs.
combination(students.size/2).                     # create all possible groups with students.size/2 (4) members each
each_with_object([]) do |group, accumulator|      # with each of these groups, and an "accumulator" variable starting as an empty array

  next unless group.flatten.uniq.length == (students.size)  # skip any group with repeating elements
  next if accumulator.find {|existing_group| existing_group & group != []}      # skip any group that may be found in the accumulator

  accumulator << group  # add any non-skipped group to the accumulator
end # returnn the value of the accumulator and assign it to groups_without_repetition

puts "actual pair combinations without repetition (#{groups_without_repetition.size}):"

groups_without_repetition.each_with_index do |arr, i|
  puts "#{i+1}: #{arr}"
end

运行时,将返回:

Possible pairs: (28) [[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [3, 4], [3, 5], [3, 6], [3, 7], [3, 8], [4, 5], [4, 6], [4, 7], [4, 8], [5, 6], [5, 7], [5, 8], [6, 7], [6, 8], [7, 8]]
Possible pair combinations: 376740
actual pair combinations without repetition (7):
1: [[1, 2], [3, 4], [5, 6], [7, 8]]
2: [[1, 3], [2, 4], [5, 7], [6, 8]]
3: [[1, 4], [2, 3], [5, 8], [6, 7]]
4: [[1, 5], [2, 6], [3, 7], [4, 8]]
5: [[1, 6], [2, 5], [3, 8], [4, 7]]
6: [[1, 7], [2, 8], [3, 5], [4, 6]]
7: [[1, 8], [2, 7], [3, 6], [4, 5]]

但是效率不高。只有12名学生,可能的对是66,可能的对组合是90858768。我希望将此方法应用于80多名参与者的课程,因此这种方法显然行不通。

问题1:构建这些组合的有效方法是什么?

从结果来看,在我看来有效的组数是n / 2-1,因为一个学生只能属于n / 2个可能的对之一。

我的感觉是,仅构造有效的groups_without_repetition 而不是创建所有可能的组并丢弃无效的组会更有效。我不确定如何处理此过程,将不胜感激。

问题2:如何用奇数个学生来解决这个问题?

这可能需要单独讨论,因此除非有已知解决方案,否则我不会担心。

a。如果其中一名学生必须参加两次以容纳奇数

b。如果其中一对成为三重奏

在以上每种情况下,都应将属于上述非常规配对的学生从轮换中排除在进一步的例外情况之外。

1 个答案:

答案 0 :(得分:1)

有一种有效的方法来生成这样的对-round-robin tournament

将所有学生放在两行中。

a  b  c
d  e  f    

所以对是a-d, b-e, c-f

修复第一个并循环旋转其他

a  d  b
e  f  c    

a  e  d
f  c  b    

a  f  e
c  b  d

a  c  f  
b  d  e

因此,生成N-1个巡回,每个巡回具有N / 2个非重复对。

对于奇数,请唯一的学生休息:)

或将他(最上面一行中的右一个)添加到左对中,以获得他与两个伙伴相遇的最长时间

 a  b  c  d
 e  f  g

 a-e-d, b-f, c-g