我一直在搜索各地,包括Stack Overflow档案,以获得如何做到这一点的答案,我尝试自己动手,但是我已经做了很短的事情,所以我决定在这里发布我的请求。
我需要在数组中获取任意(偶数)个项目,并返回项目与数组中的另一个项目配对。我需要代码的输出与我在下面包含的输出示例相同。
输入:
('A'..'H').to_a
输出:
[[['A','H'], ['B','G'], ['C','F'], ['D','E']], [['A','G'], ['B','F'], ['C','E'], ['D','H']], [['A','F'], ['B','E'], ['C','D'], ['G','H']], [['A','E'], ['B','D'], ['C','H'], ['F','G']], [['A','D'], ['B','C'], ['E','G'], ['F','H']], [['A','C'], ['B','H'], ['D','G'], ['E','F']], [['A','B'], ['C','G'], ['D','F'], ['E','H']]]
有什么想法吗?
这是我到目前为止所做的。它有点脏,并没有按照我需要的顺序返回。
items = ('A'..'H').to_a combinations = [] 1.upto(7) do |index| curitems = items.dup combination = [] 1.upto(items.size / 2) do |i| team1 = curitems.delete_at(0) team2 = curitems.delete_at(curitems.size - index) || curitems.delete_at(curitems.size - 1) combination << [team1, team2] end combinations << combination end pp combinations
输出结束,但顺序不正确:
[[["A", "H"], ["B", "G"], ["C", "F"], ["D", "E"]], [["A", "G"], ["B", "F"], ["C", "E"], ["D", "H"]], [["A", "F"], ["B", "E"], ["C", "D"], ["G", "H"]], [["A", "E"], ["B", "D"], ["C", "H"], ["F", "G"]], [["A", "D"], ["B", "C"], ["E", "G"], ["F", "H"]], [["A", "C"], ["B", "H"], ["D", "E"], ["F", "G"]], [["A", "B"], ["C", "G"], ["D", "H"], ["E", "F"]]]
你会注意到我的代码给了我两个D&lt; - &gt; H组合(最后一行和第二行),但这不起作用。
我对OP要求的理解[FM]:
N
个团队(例如,8
团队:A..H
)。R
轮比赛组成(7
在我们的例子中)和G
游戏(28英寸)
我们的例子)。A-H
,是
被拒绝作为重复,所以第一场比赛将会
是A-G
,而H
将坐在后面
刻录机,与D
配对
最后一场比赛)。E-H
;
然而,这创造了一个场景
第四场比赛将重演
(F-G
)。所以算法需要
回溯,拒绝E-H
配对
而是在第3天去E-G
游戏。答案 0 :(得分:5)
你似乎想要一个循环计划。原则很简单:
如果你从这个设置开始(上排的队伍对阵相应的较低队伍):
A B C D
H G F E
您将一个团队设置为固定(例如,A)并旋转其余团队(例如顺时针):
A H B C A G H B A F G H A E F G A D E F A C D E
G F E D F E D C E D C B D C B H C B H G B H G F
Voilà,7轮,每支球队互相比赛。
编辑:我更改了此示例中的枚举顺序以反映您的示例输出,但这只会使A
的反对者正确。
答案 1 :(得分:5)
好吧,我可以让你的8队示例正确,但我不知道如何概括调整。但也许这会让你思考......
games = (1...teams.size).map do |r|
t = teams.dup
(0...(teams.size/2)).map do |_|
[t.shift,t.delete_at(-(r % t.size + (r >= t.size * 2 ? 1 : 0)))]
end
end
答案 2 :(得分:4)
我为此代码的Python-ness道歉。运气好的话,有人会翻译。
def tourney(teams):
N = len(teams)
R = N-1 # rounds
M = N/2 # matches per round
sched = [[None] * M for i in range(R)]
played = set()
def fill(i, t):
# Replenish t at the start of each round.
if i % M == 0:
t = teams[:]
# Pick out the highest-seeded team left in t.
topseed = t.pop(min(range(len(t)), key=lambda i: teams.index(t[i])))
# Try opponents in reverse order until we find a schedule that works.
for j, opp in reversed(list(enumerate(t))):
match = topseed, opp
if match not in played:
# OK, this is match we haven't played yet. Schedule it.
sched[i // M][i % M] = match
played.add(match)
# Recurse, if there are any more matches to schedule.
if i + 1 == R * M or fill(i + 1, t[j+1:]+t[:j]):
return True # Success!
# If we get here, we're backtracking. Unschedule this match.
played.remove(match)
return False
if not fill(0, []):
raise ValueError("no schedule exists")
return sched
答案 3 :(得分:2)
根据FM的规范,这是ruby 1.8.6中的一个实现,为8个团队提供了正确的输出(非常感谢FM的出色工作!):</ p>
#!/usr/bin/env ruby
require 'pp'
require 'enumerator'
class Array
# special round robin scheduling
def schedule
res, scheduled = [], []
(length-1).times { dup.distribute(scheduled, []) }
# convert list of games to list of rounds
scheduled.each_slice(length/2) {|x| res.push x}
aux = res.inject {|a, b| a+b}
raise if aux.uniq.length != aux.length
res
end
# pair the teams in self and backburner and add games to scheduled
def distribute(scheduled, backburner)
# we are done if list is empty and back burners can be scheduled
return true if empty? && backburner.empty?
return backburner.distribute(scheduled, []) if empty?
# take best team and remember if back burner list offered alternatives
best, alternatives = shift, !backburner.empty?
# try each team starting from the last
while other = pop do
# add team to the back burner list if best played it already
if scheduled.include? [best, other]
backburner.unshift(other)
next
end
# schedule the game
scheduled.push [best, other]
# try if rest can be scheduled
return true if dup.distribute(scheduled, backburner.dup)
# if not unschedule game and add other to back burner list
scheduled.pop
backburner.unshift(other)
end
# no possible opponent was found, so try alternatives from back burners list
return alternatives && backburner.unshift(best).distribute(scheduled, [])
end
end
pp %w{ A B C D E F G H }.schedule
__END__
Output:
[[["A", "H"], ["B", "G"], ["C", "F"], ["D", "E"]],
[["A", "G"], ["B", "F"], ["C", "E"], ["D", "H"]],
[["A", "F"], ["B", "E"], ["C", "D"], ["G", "H"]],
[["A", "E"], ["B", "D"], ["C", "H"], ["F", "G"]],
[["A", "D"], ["B", "C"], ["E", "G"], ["F", "H"]],
[["A", "C"], ["B", "H"], ["D", "G"], ["E", "F"]],
[["A", "B"], ["C", "G"], ["D", "F"], ["E", "H"]]]
答案 4 :(得分:2)
我创建了一个你可能觉得有用的宝石round_robin_tournament。
跑步
students = %w(John Paul Ringo George)
teams = RoundRobinTournament.schedule(students)
teams
将是每天的数组,每天都是一对夫妻。
答案 5 :(得分:1)
怎么样
[*'A'..'H'].permutation(2).to_a
=> [["A", "B"], ["A", "C"], ["A", "D"], ["A", "E"], ["A", "F"], ["A", "G"], ["A", "H"], ["B", "A"], ["B", "C"], ["B", "D"], ["B", "E"], ["B", "F"], ["B", "G"],....
编辑:只是注意到输出不是您想要的格式,但它可能对其他人有用。
答案 6 :(得分:1)
我终于有时间再看一遍。这是Jason答案的Ruby版本,只有一些简化和一些来自jug答案的好主意。
require 'pp'
def tournament (teams)
teams.reverse!
# Hash of hashes to keep track of matchups already used.
played = Hash[ * teams.map { |t| [t, {}] }.flatten ]
# Initially generate the tournament as a list of games.
games = []
return [] unless set_game(0, games, played, teams, nil)
# Convert the list into tournament rounds.
rounds = []
rounds.push games.slice!(0, teams.size / 2) while games.size > 0
rounds
end
def set_game (i, games, played, teams, rem)
# Convenience vars: N of teams and total N of games.
nt = teams.size
ng = (nt - 1) * nt / 2
# If we are working on the first game of a round,
# reset rem (the teams remaining to be scheduled in
# the round) to the full list of teams.
rem = Array.new(teams) if i % (nt / 2) == 0
# Remove the top-seeded team from rem.
top = rem.sort_by { |tt| teams.index(tt) }.pop
rem.delete(top)
# Find the opponent for the top-seeded team.
rem.each_with_index do |opp, j|
# If top and opp haven't already been paired, schedule the matchup.
next if played[top][opp]
games[i] = [ top, opp ]
played[top][opp] = true
# Create a new list of remaining teams, removing opp
# and putting rejected opponents at the end of the list.
rem_new = [ rem[j + 1 .. rem.size - 1], rem[0, j] ].compact.flatten
# Method has succeeded if we have scheduled the last game
# or if all subsequent calls succeed.
return true if i + 1 == ng
return true if set_game(i + 1, games, played, teams, rem_new)
# The matchup leads down a bad path. Unschedule the game
# and proceed to the next opponent.
played[top][opp] = false
end
return false
end
pp tournament(ARGV)
答案 7 :(得分:1)
我最近写了一个宝石,帮助生成循环计划。你可以give it a try。
答案 8 :(得分:0)
这里选择的答案给了我麻烦。似乎与delete_at方法有关,您在团队阵列上向后移动。 Inevitbaly两支球队之前不止一次比赛。当我去16支球队时我才注意到它,但我认为它也发生在8支球队......
所以我编写了Svante的算法,这个算法非常聪明,可以与任意数量的团队合作。我也是逆时针旋转,不是顺时针
假设团队在这里是模型对象,而num_teams是团队数量
@tms = teams.all
matchups_play_each_team_once = (0...num_teams-1).map do |r|
t = @tms.dup
first_team = t.shift
r.times do |i|
t << t.shift
end
t = t.unshift(first_team)
tms_away = t[0...num_teams/2]
tms_home = t[num_teams/2...num_teams].reverse
(0...(num_teams/2)).map do |i|
[tms_away[i],tms_home[i]]
end
end
答案 9 :(得分:0)
根据this link中的这些信息,我使用以下Ruby代码生成循环调度:
def round_robin(teams)
raise "Only works for even number of teams" unless teams.length.even?
first = teams.shift # Put one team in the middle, not part of the n-gon
size = teams.length # The size of the n-gon without one team
pairs = (1..(size/2)).map{ |i| [i,size-i].sort } # The 'lines' that connect vertices in the n-gon
(0...size).map{
teams.unshift( teams.pop ) # Rotate the n-gon
# Combine the special case with the vertices joined by the lines
[ [ first, teams[0] ], *pairs.map{ |a,b| [ teams[a], teams[b] ] } ]
}
end
teams = ('A'..'H').to_a
schedule = round_robin(teams)
puts schedule.map{ |round| round.map{ |teams| teams.join }.join(' ') }
#=> AH BG CF DE
#=> AG HF BE CD
#=> AF GE HD BC
#=> AE FD GC HB
#=> AD EC FB GH
#=> AC DB EH FG
#=> AB CH DG EF