簡體   English   中英

兩組組合的排列

[英]Permutations of combinations of two groups

在C#中,我嘗試編寫一種算法來平衡給定每個球員的球員評分的兩支球隊。

數據集如下所示:

Player 1:  1330
Player 2:  1213
Player 3:  1391
Player 4:  1192
Player 5:  1261
Player 6:  1273
Player 7:  1178
Player 8:  1380
Player 8:  1200
Player 10: 1252

我想組建兩組,每組五名球員,為了公平的比賽,兩支球隊的得分差異盡可能小。

現在要做的是,我想生成所有團隊排列(每個排列是由5個玩家組成的兩個團隊)。 但是所有c#排列示例都是用於組合冪集(而不是團隊)之類的。

最有效的方法是什么?

您需要組合 ,而不是排列。 使用標准公式,我們知道有252種可能的組合,每組10個玩家有5個。 有一種非常簡單的方式來生成組合,vib在他的回答中提到了這一點,我將在這里進行擴展。

有10位玩家。 如果將播放器視為10位數字,則每個播放器對應於該數字中的一位。 任何設置了5位的10位數字都是有效的組。 因此0101010101是有效的團隊,但是0011000100不是有效的團隊。

此外,任何有效的團隊都只有一個相對的團隊。 也就是說,給定10位玩家和5位成員的團隊,那么只有5個人可以選擇。 因此,團隊0101010101與團隊1010101010配對。

2 ^ 10是1024。所以我們只需要檢查1024個可能的組合。 實際上,我們只需要檢查512,因為我們知道任何號碼大於511的球隊都將擁有最高編號的玩家(即,最后一位被設置),而任何少於512的數字都將沒有該玩家。

所以這個想法是,對於每個小於512的數字:

  1. 如果數字中沒有設置五位,請轉到下一位
  2. 倒數。 這會給你對方的球隊
  3. 計算球隊和對方球隊的得分

簡單的C#代碼可以做到這一點:

private readonly int[] _playerRatings = new[] {1330, 1213, 1391, 1192, 1261, 1273, 1178, 1380, 1200, 1252};

private int CalculateTeamScore(int team)
{
    var score = 0;
    for (var i = 0; i < 10; ++i)
    {
        if ((team & 1) == 1)
        {
            score += _playerRatings[i];
        }
        team >>= 1;
    }
    return score;
}

private bool IsValidTeam(int team)
{
    // determine how many bits are set, and return true if the result is 5
    // This is the slow way, but it works.
    var count = 0;
    for (var i = 0; i < 10; ++i)
    {
        if ((team & 1) == 1)
        {
            ++count;
        }
        team >>= 1;
    }
    return (count == 5);
}

public void Test()
{
    // There are 10 players. You want 5-player teams.

    // Assign each player a bit position in a 10-bit number.
    // 2^10 is 1024.
    // Start counting at 0, and whenever you see a number that has 5 bits set,
    // you have a valid 5-player team.
    // If you invert the bits, you get the opposing team.

    // You only have to count up to 511 (2^9 - 1), because any team after that
    // will already have been found as the opposing team.

    for (var team = 0; team < 512; ++team)
    {
        if (IsValidTeam(team))
        {
            var opposingTeam = ~team;
            var teamScore = CalculateTeamScore(team);
            var opposingTeamScore = CalculateTeamScore(opposingTeam);
            var scoreDiff = Math.Abs(teamScore - opposingTeamScore);
            Console.WriteLine("{0}:{1} - {2}:{3} - Diff = {4}.", 
                team, teamScore, opposingTeam, opposingTeamScore, scoreDiff);
        }
    }
}

您必須提供從團隊編號中提取球員編號的代碼。 從設置的位中輸出位數很簡單。 您可以修改分數計算代碼來做到這一點。

請注意,我用來查找設置多少位的代碼並不是最佳選擇。 但這有效。 如果您想要更快的方法,請查看BitHacks頁面 ,該頁面有許多不同的方法。

您實際上並不需要生成所有排列。 查看0到2 ^ 10-1之間的所有整數i,並查看整數中有多少位設置為1。 只要這是5,就可以有效地將您的10個團隊分成兩組,每組五個。

您可以使用Linq解決您的問題

在這個例子中是兩個人的兩個團隊

使用我對Jim Mischel答案的理解

.net提琴手運行

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    class Player
    {
        public int PlayerId { get; set; }
        public int PlayerBit { get; set; }
        public int PlayerScore { get; set; }

        public override string ToString()
        {
            return string.Format("Player: {0} Score: {1}\n",PlayerId,PlayerScore);
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            const int maxDiff = 15;

            var players = new List<Player> { new Player() {PlayerId = 1, PlayerBit = 1<<0, PlayerScore = 1330},
                                             new Player() {PlayerId = 2, PlayerBit = 1<<1, PlayerScore = 1213},
                                             new Player() {PlayerId = 3, PlayerBit = 1<<2, PlayerScore = 1391},
                                             new Player() {PlayerId = 4, PlayerBit = 1<<3, PlayerScore = 1192},
                                             new Player() {PlayerId = 5, PlayerBit = 1<<4, PlayerScore = 1261},
                                             new Player() {PlayerId = 6, PlayerBit = 1<<5, PlayerScore = 1273},
                                             new Player() {PlayerId = 7, PlayerBit = 1<<6, PlayerScore = 1178},
                                             new Player() {PlayerId = 8, PlayerBit = 1<<7, PlayerScore = 1380},
                                             new Player() {PlayerId = 9, PlayerBit = 1<<8, PlayerScore = 1200},
                                             new Player() {PlayerId = 10, PlayerBit = 1<<9, PlayerScore = 1252}};

            var maxTeam = players.Max(x => x.PlayerBit);
            var maxBit = maxTeam * 2 - 1;

            var team = from t1 in Enumerable.Range(0, maxTeam) where getBitCount(t1) == 5 select t1;

            var match = team.Select(x => new { t1 = x, t2 = maxBit - x });

            foreach (var m in match)
            {
                var t1 = players.Where(x => (x.PlayerBit & m.t1) == x.PlayerBit);
                var t2 = players.Where(x => (x.PlayerBit & m.t2) == x.PlayerBit);
                var t1Score = t1.Sum(x => x.PlayerScore);
                var t2Score = t2.Sum(x => x.PlayerScore);

                if (Math.Abs(t1Score - t2Score) < maxDiff)
                {
                    Console.WriteLine("Team 1 total score {0} Team 2 total score {1}", t1Score, t2Score);
                    Console.WriteLine("{0} versu \n{1}\n\n", string.Join("", t1.Select(x => x.ToString()).ToArray()), string.Join("", t2.Select(x => x.ToString()).ToArray()));
                }
            }

            Console.Read();
        }

        private static int getBitCount(int bits)
        {
            bits = bits - ((bits >> 1) & 0x55555555);
            bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333);
            return ((bits + (bits >> 4) & 0xf0f0f0f) * 0x1010101) >> 24;
        }
    }
}

它基本上是NP難題的分區問題的優化版本

但是,由於n = 10(非常小),您仍然可以找到所有排列並找到答案,對於更大的n,您也可以使用快速且易於實現的貪婪近似,該近似也顯示在Wiki頁面上。 下面我僅顯示示例代碼,其中蠻力為n = 10的情況下找到答案。 盡管它是用C ++編寫的,但內部沒有什么特別的地方,所有的運算符/數組在C#中都是相同的,您應該自己進行翻譯,復雜度為O(2 ^ 10 * 10)

 #include<bits/stdc++.h> using namespace std; int a[10] = {1330,1213,1391,1192,1261,1273,1178,1380,1200,1252}; vector<int> team1, team2; int ans = 1<<28, T1, T2; int bits(int x){ int cnt = 0; while(x){ cnt += x&1; x>>=1;} return cnt; } int main(){ for(int i=0; i< 1<<10; i++){ if(bits(i) == 5){ int t1 = 0, t2 = 0; for(int x = i,y=(1<<10)-1-i, j=0; x; x>>=1,y>>=1, j++) { t1 += (x&1)*a[j]; t2 += (y&1)*a[j]; } if(ans > abs(t1-t2)){ ans = abs(t1-t2); T1 = i; T2 = (1<<10)-1-i;} } } for(int i=1; T1 || T2; T1>>=1, T2>>=1, i++) { if(T1&1) team1.push_back(i); if(T2&1) team2.push_back(i); } printf("Team 1: "); for(int i=0; i<5;i++) printf("%d ", team1[i]); puts(""); printf("Team 2: "); for(int i=0; i<5;i++) printf("%d ", team2[i]); puts(""); printf("Difference: %d\\n", ans); return 0; } 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM