在相同大小的桶中生成非重复对

时间:2015-09-08 13:24:23

标签: java algorithm

我有一个从此代码生成的对的列表

int n = listTeam.size();
for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
        if (i != j)
            listTeamPairs.add(new Team[] { listTeam.get(i), listTeam.get(j) });
        }
    }
}

如果有6支队伍,这将正确生成这些对。

[0 1 , 0 2 , 0 3 , 0 4 , 0 5 , 1 2 , 1 3 , 1 4 , 1 5 , 2 3 , 2 4 , 2 5 , 3 4 , 3 5 , 4 5 ]

我遇到的问题是将这些对选择为相同大小的圆(桶)(在这种情况下为三个)。

第一轮将成为

0-1, 2-3, 4-5

问题出现在第二轮

0-2, 1-3 <- swap order of these matches. 
This leaves only team 4-5 again. Which is not valid.

生成轮次而没有无效条目但不是完整存储桶的代码

private boolean generateRound(Team[] teamInRound, List<Team[]> roundTeams) {
    Team team1 = teamInRound[0];
    Team team2 = teamInRound[1];
    Optional<Team[]> t = roundTeams.stream().filter(p -> p[0].getName().contentEquals(team1.getName()) || 
                    p[1].getName().contentEquals(team2.getName()) || p[0].getName().contentEquals(team2.getName()) ||  
                    p[1].getName().contentEquals(team1.getName())).findAny();
            if (t.isPresent())
                return false;
            roundTeams.add(teamInRound);
            tmpTeamPairs.remove(teamInRound);

            return true;
}

private void generateRounds(List<Team[]> teams) {

    for (int i = 0; i < listTeam.size() / 2;i++) {
        System.out.println("Reamining pairs");
        tmpTeamPairs.stream().forEach(p -> System.out.println(p[0].getName() + " - " + p[1].getName()));
        if (i == 0) {
            teams.add(tmpTeamPairs.get(0));
            tmpTeamPairs.remove(0);

            continue;
        }
        for (Team[] pair : tmpTeamPairs) {
            boolean b = generateRound(pair, teams); 
            if (b) {
                break;
            }
        }
    }       
}

看看提议的答案,似乎并没有生成所需的桶。

scheduled team 0 against team 4
scheduled team 2 against team 3 <-----
scheduled team 5 against team 1
Array order
0
2 <---
5
4
3 <---
1
Lag 5 - Lag3
Lag1 - Lag2 <-----
Lag4 - Lag 6
-----------------------------------------------
scheduled team 0 against team 5
scheduled team 1 against team 4
scheduled team 2 against team 3 <----
Array order
0
1
2 <---
5
4
3 <---
Lag 5 - Lag4
Lag 6 - Lag3
Lag1 - Lag2 <-----

2 个答案:

答案 0 :(得分:2)

你想要达到的目标是制作锦标赛计划。这可以使用循环调度算法Round robin scheduling

来完成

这可以通过固定其中一个竞争对手来实现,假设为0.其余的将顺时针旋转以产生新的组合。

实现此目的的代码可能如下所示:

public static void generateRoundRobinPairs(List<Integer> teams) {
    Integer fixedElement = teams.get(0);
    List<Integer> teamsWithoutFirst = teams.subList(1,teams.size());
    for (int i = 0; i < teamsWithoutFirst.size(); i++) {
        List<Integer> toSchedule = new ArrayList<>();
        toSchedule.add(fixedElement);
        teamsWithoutFirst = buildNewRotation(teamsWithoutFirst);
        toSchedule.addAll(teamsWithoutFirst);
        scheduleRound(toSchedule);
    }
}

public static void scheduleRound(List<Integer> teams) {
    for (int i = 0; i < teams.size() / 2; i++) {
        // here create your data structure
        String template ="scheduled team %s against team %s";
        System.out.println(String.format(template, teams.get(i), teams.get(i + teams.size() / 2)));
    }
}

public static List<Integer> buildNewRotation(List<Integer> l) {
    List<Integer> newList = new ArrayList<>();
    newList.add(l.get(l.size() / 2));

    for (int i = 0; i < l.size() / 2 - 1; i++) {
        newList.add(l.get(i));
    }
    for (int i = 0; i < l.size() / 2; i++) {
        newList.add(l.get(i + 1 + l.size() / 2));
    }

    newList.add(l.get(l.size() / 2 - 1));
    return newList;
}

答案 1 :(得分:2)

这是循环计划的另一个实现。

它返回一对桶的列表。虽然您可以创建自定义类,但是只使用List实现了对。

每个存储桶都是Set,因为只要团队配对已知,存储桶中的订单就无关紧要了。

计划本身是List,因为订单在计划中很重要。

该方法是通用的,因此您可以将这些小组放在IntegerString中,看看它是否运作正常,或使用完整的Team类。

public static <T> List<Set<List<T>>> roundRobin(List<T> teams) {

    int numTeams = teams.size();

    // For a proper league, we only allow even number of teams.
    if ( numTeams % 2 != 0 ) {
        throw new IllegalArgumentException("Number of teams not even " + numTeams);
    }

    List<Set<List<T>>> result = new ArrayList<>(numTeams - 1);


    // Implement the round robin by rotating the right side of the list
    // every time, then pairing opposite teams. Note that the first
    // item is not part of the rotation.
    for ( int i = 0; i < numTeams - 1; i++ ) {
        Collections.rotate(teams.subList(1,numTeams), 1);
        Set<List<T>> bucket = new HashSet<>();
        for ( int j = 0; j < numTeams / 2; j++ ) {
            bucket.add(Arrays.asList(teams.get(j), teams.get(numTeams - j - 1)));
        }
        result.add(bucket);
    }
    return result;
}

这里的“技巧”是在Collections.rotate结果上使用subList()。这意味着旋转实际上反映在原始列表中,因此我们最终会得到一个包含冻结团队的完整列​​表,这样可以更容易地循环使用。

首先匹配最后一对,倒数第二匹配等等。

使用此main

运行此操作
    List<String> teams = Arrays.asList(
                             "Manchester Utd.",
                             "Chelsea",
                             "Tottenham",
                             "Liverpool",
                             "Arsenal",
                             "West Ham Utd." );

    for ( Set<List<String>> bucket : roundRobin(teams)) {
        for ( List<String> pair : bucket) {
            System.out.println(pair);
        }
        System.out.println();
    }

结果:

[West Ham Utd., Liverpool]
[Chelsea, Tottenham]
[Manchester Utd., Arsenal]

[Manchester Utd., Liverpool]
[Arsenal, Tottenham]
[West Ham Utd., Chelsea]

[Liverpool, Chelsea]
[Manchester Utd., Tottenham]
[Arsenal, West Ham Utd.]

[Liverpool, Arsenal]
[Tottenham, West Ham Utd.]
[Manchester Utd., Chelsea]

[Manchester Utd., West Ham Utd.]
[Tottenham, Liverpool]
[Chelsea, Arsenal]

请注意,在真实的体育赛事中,赛程表还反映了谁在主场比赛以及谁在比赛中出场。在这里,冷冻团队可以在家里玩所有游戏,至少在第一轮比赛中。如果您需要公平的家庭安排,您可以更改行:

bucket.add(Arrays.asList(teams.get(j), teams.get(numTeams - j - 1)));

为:

Set<List<T>> pair = Arrays.asList(teams.get(j), teams.get(numTeams - j - 1));
if ( i % 2 == 1 && j == 0 ) {
    Collections.reverse(pair);
}
bucket.add(pair);