我的一个朋友在排球联赛上打球并给我一个有趣的问题:
以下每个字母代表一对玩家
; player pairs (24)
'(a b c d e f g h i j k l m n o p q r s t u v w x)
创建匹配,其中3对被分组以组成一个团队(团队中有6名玩家)
; teams
'((a b c) (d e f) (g h i) (j k l) (m n o) (p q r) (s t u) (v w x))
团队组合将构成一场比赛
; matches
'(((a b c) (d e f))
((g h i) (j k l))
((m n o) (p q r))
((s t u) (v w x)))
在一个特定的夜晚,他们可以打8到12场比赛,但在每场比赛之前,这对球员将随机化。促进者的意图是尽可能地改组球队,但往往结果远非良好的分配。 'a
与'b
等配对会'(a b c)
配对太多次。
在'a
组成团队的情况下,在理想情况下,'b
将无法再次与'c
或'b
一起发挥作用。同样,'c
如果可能,也不会再次使用'((a b c)
(a b d)
(a b e)
...)`
。
只需计算C组合(24选3),就有2,024个可能的球队......
'(a b c) ; team1
'(a w x) ; team2
我以为我可以过滤掉非完全独特的团队组合,但这并不能让我更接近我的解决方案。
考虑以下两个
'a
这是两个完全独特的团队组合,但当然我们无法将它们放在一个匹配中,因为对; player pairs (26)
'(a b c d e f g h i j k l m n o p q r s t u v w x y z)
; teams
'((a b c) (d e f) (g h i) (j k l) (m n o) (p q r) (s t u) (v w x))
; matches
'(((a b c) (d e f))
((g h i) (j k l))
((m n o) (p q r))
((s t u) (v w x)))
; sit-out this game
'(y z)
不可能同时在两个团队中进行。所以这些团队组合永远不应该在单一的匹配解决方案中提供。
另一个问题是,玩家对可能无法被6整除。
X choose Y
问题1:
如何生成由独特团队组合组成的所有可能匹配的列表?
问题2:
如何扩展算法以适应“坐下”玩家。如果玩家对的列表不能被6整除,则每个玩家对必须以相等的匹配次数进行游戏/静坐。
-
所以我没有太多的代码可以证明这一点,因为我一直在追求死胡同。我得到的最远的是<div class="profile"<?php print $attributes; ?>>
<?php print render($user_profile); ?>
<?php var_dump($user); ?>
</div>
实施,以便我列出所有可能的团队组合。我在滤除非完全独特的组合时遇到了麻烦。即使我在这方面取得成功,我也不知道如何将完全独特的组合组合成比赛。
答案不必包含完整的实现,但指向正确的方向会有很多帮助。我对这种计算没有太多经验。
答案 0 :(得分:3)
您可以使用combinations
函数和一些额外的过滤为问题1构建解决方案。首先是一些数据定义:
;; A Player-Pair is a Symbol.
;; A Team is a (List Player-Pair Player-Pair Player-Pair).
;; A Match is a (List Team Team) where the teams are disjoint.
;; Player-Pairs : (Listof Player-Pair)
(define player-pairs '(a b c d e f g h i j k l m n o p q r s t u v w x))
所以,我们想要的是找到匹配,但要做到这一点,我们需要从3个玩家对的所有组合中找到一组团队,然后过滤它以使其满足你的约束,即两个玩家 - 对不应该在一个团队中彼此不止一次。
我还不知道该怎么做,但听起来很复杂,所以要把它作为一个名为filter-team-combinations
的辅助函数。
;; find-teams : (Listof Player-Pair) -> (Listof Team)
(define (find-teams player-pairs)
(filter-team-combinations (combinations player-pairs 3)))
;; filter-team-combinations : (Listof Team) -> (Listof Team)
;; Filters out teams where a player-pair would on a team with another
;; player-pair for the second time.
(define (filter-team-combinations teams) ....)
嗯。要知道给定的Team
是否有两个已经在团队中的玩家对,我也需要跟踪它。它可能是递归的,并且已经在一起的玩家对的对将从一个递归调用变为下一个。随着我们添加到结果列表中,对对列表将变得更大。所以我们需要将它添加为一个参数,它以空列表开头。
;; find-teams : (Listof Player-Pair) -> (Listof Team)
(define (find-teams player-pairs)
(filter-team-combinations (combinations player-pairs 3) (list)))
;; A Pair-Pair is a (List Player-Pair Player-Pair)
;; filter-team-combinations : (Listof Team) (Listof Pair-Pair) -> (Listof Team)
;; Filters out teams where a player-pair would on a team with another
;; player-pair for the second time.
;; pair-pairs is an accumulator that stores the pair-pairs that we've
;; seen so far.
(define (filter-team-combinations teams pair-pairs) ....)
filter-team-combinations
函数处理团队列表,列表可以为空或第一个团队与其他团队合并:
(define (filter-team-combinations teams pair-pairs)
(cond [(empty? teams) ....]
[else .... (first teams) .... (rest teams) ....]))
对于基本情况,如果没有要过滤的团队,我们将返回空列表。对于递归情况,它必须查看第一个团队包含的玩家对的对,检查它们是否与现有的pair-pairs
发生冲突,并对其进行分支:
(define (filter-team-combinations teams pair-pairs)
(cond [(empty? teams) (list)]
[else
(define new-pair-pairs (combinations (first teams) 2))
(cond [(pair-pairs-conflict? new-pair-pairs pair-pairs)
.... (first teams) .... (rest teams) ....]
[else
.... (first teams) .... (rest teams) ....])]))
;; pair-pairs-conflict? : (Listof Pair-Pair) (Listof Pair-Pair) -> Boolean
(define (pair-pairs-conflict? as bs) ....)
因此,假装pair-pairs-conflict?
做正确的事情,我们会填写....
来完成filter-team-combinations
。在他们发生冲突的情况下,我们应该放弃第一个团队并重复其余部分。如果他们没有冲突,我们应该让第一支队伍参与其中。
(define (filter-team-combinations teams pair-pairs)
(cond [(empty? teams) (list)]
[else
(define new-pair-pairs (combinations (first teams) 2))
(cond [(pair-pairs-conflict? new-pair-pairs pair-pairs)
;; This team has a pair-pair that a previous team already had,
;; so don't include this team in the result; recur on the rest.
(filter-team-combinations (rest teams) pair-pairs)]
[else
;; Cons this team onto something.
(cons (first teams)
....)])]))
对于最后一个....
,我们需要重复其余部分,但我们还需要确保递归调用知道第一个团队中的玩家对不应该是相同的团队了。为此,我们可以将它们附加到pair-pairs
参数上。
;; filter-team-combinations : (Listof Team) (Listof Pair-Pair) -> (Listof Team)
;; Filters out teams where a player-pair would on a team with another
;; player-pair for the second time.
;; pair-pairs is an accumulator that stores the pair-pairs that we've
;; seen so far.
(define (filter-team-combinations teams pair-pairs)
(cond [(empty? teams) (list)]
[else
(define new-pair-pairs (combinations (first teams) 2))
(cond [(pair-pairs-conflict? new-pair-pairs pair-pairs)
;; This team has a pair-pair that a previous team already had,
;; so don't include this team in the result; recur on the rest.
(filter-team-combinations (rest teams) pair-pairs)]
[else
;; Add this team and add the new pair-pairs.
(cons (first teams)
(filter-team-combinations (rest teams)
(append new-pair-pairs pair-pairs)))])]))
所以现在我们需要实现pair-pairs-conflict?
谓词。
;; pair-pairs-conflict? : (Listof Pair-Pair) (Listof Pair-Pair) -> Boolean
;; A team must be made up of sets of player-pairs that haven't been on the
;; same team yet. This function takes two lists of player-pair pairs.
;; Each pair-pair in the first list has two player-pairs that would now be
;; on the same team.
;; Each pair-pair in the second list has two player-pairs that have been
;; on the same team already.
;; This function returns true iff any player-pair would be on the same
;; team with anyone they have already been on the same team with.
(define (pair-pairs-conflict? as bs) ....)
需要在as
中检查每对配对并检查它是否在bs
中,如果a
中有任何bs
,则会发生冲突。一种方法是使用ormap
,另一种方法是使用for/or
。
(define (pair-pairs-conflict? as bs)
(for/or ([a (in-list as)])
(member a bs)))
但是这有一个问题。配对(list 'a 'b)
应视为与配对(list 'b 'a)
相同。所以我们需要一个member
函数,它不关心这个有序的东西。幸运的是,member
可以使用第三个参数将其用作等式谓词。
(define (pair-pairs-conflict? as bs)
(for/or ([a (in-list as)])
(member a bs pair-pair=?)))
;; pair-pair=? : Pair-Pair Pair-Pair -> Boolean
(define (pair-pair=? a b)
(match-define (list a1 a2) a)
(match-define (list b1 b2) b)
(or (and (equal? a1 b1) (equal? a2 b2))
(and (equal? a1 b2) (equal? a2 b1))))
现在,我们拥有了查找所有有效团队所需的一切。
(define teams (find-teams player-pairs))
要查找比赛,我们需要两支球队的组合,但我们需要对其进行过滤,以确保球队不相交,以便球员对永远不会对抗自己。
;; find-matches : (Listof Team) -> (Listof Match)
(define (find-matches teams)
(filter match-has-disjoint-teams? (combinations teams 2)))
;; match-has-disjoint-teams? : Match -> Boolean
(define (match-has-disjoint-teams? match)
(teams-disjoint? (first match) (second match)))
;; teams-disjoint? : Team Team -> Boolean
(define (teams-disjoint? team-1 team-2) ....)
要实施teams-disjoint?
,我们需要将team-1
中的每个玩家对与team-2
中的每个玩家对进行匹配,并确保其中任何一个都不相等。一种方法是使用cartesian-product
和andmap
,但另一种方法是使用for*/and
。
;; teams-disjoint? : Team Team -> Boolean
(define (teams-disjoint? team-1 team-2)
(for*/and ([p1 (in-list team-1)]
[p2 (in-list team-2)])
(not (equal? p1 p2))))
使用find-matches
:
> (find-matches (list (list 'a 'b) (list 'b 'c) (list 'c 'd) (list 'd 'a)))
(list (list (list 'a 'b) (list 'c 'd))
(list (list 'b 'c) (list 'd 'a)))
> (find-matches (list (list 'a 'b 'c)
(list 'c 'd 'e)
(list 'e 'f 'g)
(list 'g 'h 'i)))
(list (list (list 'a 'b 'c) (list 'e 'f 'g))
(list (list 'a 'b 'c) (list 'g 'h 'i))
(list (list 'c 'd 'e) (list 'g 'h 'i)))
我尝试解决问题1的方法是将find-matches
与find-teams
:
(find-matches (find-teams player-pairs))
有24个不同的玩家对,这会产生1,624个不同的对战。
虽然,虽然两个玩家对永远不会在不同的团队中,但这包括与他们在不同的比赛中所处的相同团队的匹配。
这可能不是你想要的。它可能会帮助你到达那里,所以无论如何我都会发布它。