代码之家  ›  专栏  ›  技术社区  ›  LandonSchropp

老师的算法

  •  4
  • LandonSchropp  · 技术社区  · 12 年前

    这个问题

    我妈妈是一名教师。她最近让我写一个脚本,从学生列表中生成一组学生对。每个学生都应该与其他学生配对,并且没有学生与同一个学生一起工作超过一次。

    例如,假设她有四个学生: "Bob" , "Lisa" , "Duke" "Tyrone" 。脚本应产生以下输出:

    { { "Bob", "Lisa" }, { "Duke", "Tyrone" } }
    { { "Bob", "Duke" }, { "Lisa", "Tyrone" } }
    { { "Bob", "Tyrone" }, { "Lisa", "Duke" } }
    

    天真的尝试

    我原以为这将是一个简单的项目,但我意识到,在编写过程中,我有点无法编写一个高效的算法来生成列表。最初,我用Ruby编写了这个天真的实现:

    # the list of students
    CLASS_LIST = ("A".."H").to_a
    
    # add an extra element to the class list if the class list length is odd
    CLASS_LIST << nil if CLASS_LIST.length.odd?
    
    # determine all possible permutations of the class lists
    permutations = CLASS_LIST.permutation
    
    # convert all of the permutations into pairs
    permutations = permutations.map { |permutation| permutation.each_slice(2).to_a }
    
    puts "PERMUTATIONS LENGTH: " + permutations.length.to_s
    
    # iterate through the permutations and remove all subsequent permutations that contain a matching
    # pair
    i = 0
    
    while i < permutations.length
    
      # remove any subsequent permutations that contain pairs already in the current permutation
      permutations.delete_if do |permutation|
    
        # return true if the current permutation has any elements in common with the other permutation
        permutations.index(permutation) > i and permutations[i].any? do |p1| 
          permutation.any? do |p2| 
            (p1[0] == p2[0] and p1[1] == p2[1]) or (p1[0] == p2[1] and p1[1] == p2[0])
          end
        end
      end
    
      # increment i
      i += 1
    end
    
    permutations.each do |permutation|
      p permutation
    end
    

    这种实施效率极低。当我描述它时,4名学生用了0.093秒,6名学生用0.376秒,8名学生用35分32秒。此外,排列的总数是无法配对的。4个学生有24个排列,6个有720个,8个有40320个。

    渐进地 while 循环在O(n!)中运行 delete_if 循环在O(n!)中运行 permutations.index permutations.any? 循环在O(n!)和内部 permutation.any? 循环在O(n)时间内运行,这意味着 整个算法在O(n(n!)^3)! 显然,这个解决方案是行不通的。

    更好的方法

    我很早就意识到,我不需要在每一双鞋上都打圈。由于每个学生与其他学生只合作过一次,因此统一结果集应该会产生每个唯一的可能配对。我决定从生成这个集合开始。首先,我查看了所有可能的组合。

         A    B    C    D    E    F
    A  A,A  A,B  A,C  A,D  A,E  A,F
    B  B,A  B,B  B,C  B,D  B,E  B,F
    C  C,A  C,B  C,C  C,D  C,E  C,F
    D  D,A  D,B  D,C  D,D  D,E  D,F
    E  E,A  E,B  E,C  E,D  E,E  E,F
    F  F,A  F,B  F,C  F,D  F,E  F,F
    

    然后,我把学生们自己动手的那几对去掉了。

         A    B    C    D    E    F
    A       A,B  A,C  A,D  A,E  A,F
    B  B,A       B,C  B,D  B,E  B,F
    C  C,A  C,B       C,D  C,E  C,F
    D  D,A  D,B  D,C       D,E  D,F
    E  E,A  E,B  E,C  E,D       E,F
    F  F,A  F,B  F,C  F,D  F,E     
    

    最后,我删除了重复的配对。

         A    B    C    D    E    F
    A       A,B  A,C  A,D  A,E  A,F
    B            B,C  B,D  B,E  B,F
    C                 C,D  C,E  C,F
    D                      D,E  D,F
    E                           E,F
    F                              
    

    在Ruby中生成对是相当琐碎的。

    # generate the set of all possible pairs
    UNIQUE_PAIRS = (0..(CLASS_LIST.length - 2)).to_a.map do |row|
      ((row + 1)..(CLASS_LIST.length - 1)).to_a.map do |column|
        [ row, column ]
      end
    end.flatten(1)
    

    接下来,我试图弄清楚如何将这些独特的配对转化为我想要的结果集。我的想法是为每个列表创建一组所有可能的对,然后消除不能使用的对,因为对被添加到每个列表中。在我的第一次尝试中,我试图在开始下一次之前填写每个列表:

    STEP 0:
    
    LIST 1: { }
    LIST 2: { }
    LIST 3: { }
    LIST 4: { }
    LIST 5: { }
    
    AVAILABLE 1: { { A, B }, { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 2: { { A, B }, { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 3: { { A, B }, { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 4: { { A, B }, { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 5: { { A, B }, { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    
    
    
    STEP 1:
    
    LIST 1: { { A, B } }
    LIST 2: { }
    LIST 3: { }
    LIST 4: { }
    LIST 5: { }
    
    AVAILABLE 1: { { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 2: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 3: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 4: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 5: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    
    
    
    STEP 2:
    
    LIST 1: { { A, B }, { C, D } }
    LIST 2: { }
    LIST 3: { }
    LIST 4: { }
    LIST 5: { }
    
    AVAILABLE 1: { { E, F } }
    AVAILABLE 2: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 3: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 4: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 5: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    
    
    
    STEP 3:
    
    LIST 1: { { A, B }, { C, D }, { E, F } }
    LIST 2: { }
    LIST 3: { }
    LIST 4: { }
    LIST 5: { }
    
    AVAILABLE 1: { }
    AVAILABLE 2: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F } }
    AVAILABLE 3: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F } }
    AVAILABLE 4: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F } }
    AVAILABLE 5: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F } }
    
    
    
    STEP 4:
    
    LIST 1: { { A, B }, { C, D }, { E, F } }
    LIST 2: { { A, C } }
    LIST 3: { }
    LIST 4: { }
    LIST 5: { }
    
    AVAILABLE 1: { }
    AVAILABLE 2: { { B, D }, { B, E }, { B, F }, { D, E }, { D, F } }
    AVAILABLE 3: { { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F } }
    AVAILABLE 4: { { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F } }
    AVAILABLE 5: { { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F } }
    
    
    
    STEP 5:
    
    LIST 1: { { A, B }, { C, D }, { E, F } }
    LIST 2: { { A, C }, { B, D } }
    LIST 3: { }
    LIST 4: { }
    LIST 5: { }
    
    AVAILABLE 1: { }
    AVAILABLE 2: { }
    AVAILABLE 3: { { A, D }, { A, E }, { A, F }, { B, C }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F } }
    AVAILABLE 4: { { A, D }, { A, E }, { A, F }, { B, C }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F } }
    AVAILABLE 5: { { A, D }, { A, E }, { A, F }, { B, C }, { B, E }, { B, F }, { C, E }, { C, F }, { D, E }, { D, F } }
    

    这在步骤6失败,因为没有可能的配对可供使用。接下来,我尝试在另一个方向上运行算法:

    STEP 1:
    
    LIST 1: { { A, B } }
    LIST 2: { }
    LIST 3: { }
    LIST 4: { }
    LIST 5: { }
    
    AVAILABLE 1: { { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 2: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 3: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 4: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 5: { { A, C }, { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    
    
    
    STEP 2:
    
    LIST 1: { { A, B } }
    LIST 2: { { A, C } }
    LIST 3: { }
    LIST 4: { }
    LIST 5: { }
    
    AVAILABLE 1: { { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 2: { { B, D }, { B, E }, { B, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 3: { { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 4: { { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 5: { { A, D }, { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    
    
    
    STEP 3:
    
    LIST 1: { { A, B } }
    LIST 2: { { A, C } }
    LIST 3: { { A, D } }
    LIST 4: { }
    LIST 5: { }
    
    AVAILABLE 1: { { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 2: { { B, D }, { B, E }, { B, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 3: { { B, C }, { B, E }, { B, F }, { C, E }, { C, F }, { E, F } }
    AVAILABLE 4: { { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 5: { { A, E }, { A, F }, { B, C }, { B, D }, { B, E }, { B, F }, { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    
    
    
    STEP 4:
    
    LIST 1: { { A, B } }
    LIST 2: { { A, C } }
    LIST 3: { { A, D } }
    LIST 4: { { A, E } }
    LIST 5: { { A, F } }
    
    AVAILABLE 1: { { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 2: { { B, D }, { B, E }, { B, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 3: { { B, C }, { B, E }, { B, F }, { C, E }, { C, F }, { E, F } }
    AVAILABLE 4: { { B, C }, { B, D }, { B, F }, { C, D }, { C, F }, { D, F } }
    AVAILABLE 5: { { B, C }, { B, D }, { B, E }, { C, D }, { C, E }, { D, E } }
    
    
    
    STEP 5:
    
    LIST 1: { { A, B } }
    LIST 2: { { A, C } }
    LIST 3: { { A, D } }
    LIST 4: { { A, E } }
    LIST 5: { { A, F } }
    
    AVAILABLE 1: { { C, D }, { C, E }, { C, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 2: { { B, D }, { B, E }, { B, F }, { D, E }, { D, F }, { E, F } }
    AVAILABLE 3: { { B, C }, { B, E }, { B, F }, { C, E }, { C, F }, { E, F } }
    AVAILABLE 4: { { B, C }, { B, D }, { B, F }, { C, D }, { C, F }, { D, F } }
    AVAILABLE 5: { { B, C }, { B, D }, { B, E }, { C, D }, { C, E }, { D, E } }
    
    
    
    STEP 6:
    
    LIST 1: { { A, B }, { C, D } }
    LIST 2: { { A, C }, { B, D } }
    LIST 3: { { A, D } }
    LIST 4: { { A, E } }
    LIST 5: { { A, F } }
    
    AVAILABLE 1: { { E, F } }
    AVAILABLE 2: { { E, F } }
    AVAILABLE 3: { { B, C }, { B, E }, { B, F }, { C, E }, { C, F }, { E, F } }
    AVAILABLE 4: { { B, C }, { B, D }, { B, F }, { C, F }, { D, F } }
    AVAILABLE 5: { { B, C }, { B, D }, { B, E }, { C, E }, { D, E } }
    

    在步骤6之后,很明显该算法将失败。

    接下来是什么?

    很明显,我遗漏了一些数学原理。做这件事的正确方法是什么?感谢您提前提供的帮助!

    1 回复  |  直到 12 年前
        1
  •  13
  •   Christopher Creutzig    12 年前

    循环通过所有配对的经典算法如下:

    1. 让每个人成对排队(这里的成对是1-5、2-6等):

      1 2 3 4

      5 6 7 8

    2. 要进入下一对,让人1站着不动,其他人向右旋转一个位置:

      1 3 4 8

      2 5 6 7

    重复第2步,直到你用完新的配对或需要它们,无论什么先到。

    这一切的美妙之处在于它非常简单,你可以在体育课上做得很好。当然,也可以用卡片或其他代币。