如何编码某个数学​​算法

时间:2016-06-09 15:22:46

标签: sql sql-server sql-server-2012

我已经很好地给了这个算法来帮助我在SQL中创建一个fixture列表,但是将它作为SQL代码应用,我不知道该怎么做。是否有人可以指导我如何将其应用于代码?

下面是我的表架构,下面是算法:

联赛:

[LeagueID] TINYINT IDENTITY(1,1) NOT NULL PRIMARY KEY, 
[LeagueName] VARCHAR(30) UNIQUE

小组:

[TeamID] TINYINT IDENTITY(1,1) NOT NULL PRIMARY KEY, 
[TeamAbbreviation] CHAR(3) UNIQUE, 
[TeamName] VARCHAR(50) UNIQUE, 
[LeagueID] TINYINT CONSTRAINT FK_Team_League FOREIGN KEY REFERENCES League(LeagueID) 

夹具:

[FixtureID] INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
[WeekNumber] INT NOT NULL,
[FixtureDate] DATE NULL,
[HomeTeamID] TINYINT NULL,
[AwayTeamID] TINYINT NULL,
[LeagueID] TINYINT CONSTRAINT FK_Fixture_League FOREIGN KEY REFERENCES League(LeagueID)

算法:

让我们根据N队的有序列表l(对应于N-1多边形顶点+多边形中心)翻译名为round robin scheduling的算法:

  • l通过从列表中对抗最后一个队列来定义赛程,第二个队伍对阵前一个队伍,等等。
    也就是说,对于0 ≤ x < N,您可以扮演团队l[x]与团队l[N-1-x]

  • 要生成下一组灯具,请旋转列表中的N-1个第一个元素。
    那是l = l[1] + l[2] + ... + l[N-2] + l[0] + l[N-1]

  • 完成全套N-1轮换后,请再次进行,但交换主队和客队:与队l[N-1-x]对阵队l[x]而不是相反

如果您从数字排序列表0..N-1开始,那么在第i轮,列表为:l = [(i + 0) % (N-1)] + [(i + 1) % (N-1)] + ... + [(i + N-2) % (N-1)] + [N-1]

也就是说,灯具位于i

  • i vs N-1
  • 对于0 < x < (N-1) / 2 (x + i) % (N-1) vs (N-1 - x + i) % (N-1)

现在有一个技巧,因为这个仅适用于偶数。否则最后一支球队总是在第i轮比赛中对阵球队i,而自然每一轮有一支球队无法上场。这意味着第4队比其他球队多出一场比赛。

要解决此问题,我们添加虚拟团队,以便我们拥有N = 6和第i轮的5个团队:

  • i vs 5(虚拟团队)
  • (i + 1) % 4 vs (4 + i) % 4
  • (i + 2) % 4 vs (3 + i) % 4

现在你已经知道了,你可以生成一个函数,它会根据整数为你提供灯具。它应该输出以下内容:

  第0轮比赛0:0比1,比4比2比2比3   1:1轮比赛,2比0比3比3比4   第2轮:2轮比赛,3比1比4比0比0   第3轮:3轮比赛,4比2比0比1比赛   4:4轮比赛,0比3比1比2比赛2比赛

请注意,公式ix + i代替N-1 - x + i,您可以使用任意多个m * i(因此x + m * iN-1 - x + m * i)只要mN-1以及relatively prime。此处N - 1 = 5是素数,因此您可以使用任何您想要的m

更新

如下所示,首先是联盟表的测试数据,第二个是team表(按顺序匹配表模式列)

联赛:

1, 'English Premiership'
2, 'English Division 1'

队:

1, 'BCN', 'FC Barcelona', 1
2, 'MAD', 'Real Madrid', 1
3, 'ATH', 'Athletico Madrid', 1
4, 'ESP', 'Espanyol', 1
5, 'MAN', 'Manchester United', 2
6, 'BOL', 'Bolton', 2
7, 'CHE', 'Chelsea', 2
8, 'ARS', 'Arsenal', 2

球队在主客场比赛中互相比赛,只能对阵同一联赛的球队(因此不同的联盟ID)

每轮比赛应该是这样的:

League 1:

Round 1- 01/05/2016:
1v4, 2v3

Round 2- 08/05/2016:
1v2, 3v4

Round 3- 15/05/2016:
3v1, 4v2

Round 4- 22/05/2016:
4v1, 3v2

Round 5- 29/05/2016:
2v1, 4v3

Round 6- 05/06/2016:
1v3, 2v4


League 2:

Round 1- 01/05/2016:
5v8, 6v7

Round 2- 08/05/2016:
5v6, 7v8

Round 3- 15/05/2016:
3v1, 4v2

Round 4- 22/05/2016:
8v5, 7v6

Round 5- 29/05/2016:
6v5, 8v7

Round 6- 05/06/2016:
5v7, 6v8
  • 联赛号码是'LeagueID'
  • 回合数是'WeekNumber'
  • 日期为'夹具日期'
  • 主队名为'HomeTeamID'
  • 客场球队号码是'AwayTeamID'

这应该全部插入'Fixture'表中。

2 个答案:

答案 0 :(得分:1)

以下似乎得到了你想要的东西 - 但这是Oracle SQL(我目前无法访问SQL服务器数据库)。但我相信你需要改变的是MOD函数,文本连接以及对DUAL的引用,以使它在SQL Server上运行:

WITH TEAMS(TEAMID,TEAMNAME) AS (
SELECT 0,'TEAM A' FROM DUAL UNION
SELECT 1,'TEAM B' FROM DUAL UNION
SELECT 2,'TEAM C' FROM DUAL UNION
SELECT 3,'TEAM D' FROM DUAL UNION
SELECT 4,'TEAM E' FROM DUAL 
),
TEAMSX(TEAMID,TEAMNAME) AS (
SELECT * FROM TEAMS
UNION
SELECT (SELECT COUNT(*) FROM TEAMS) TEAMID,'DUMMY' FROM DUAL WHERE MOD((SELECT COUNT(*) FROM TEAMS),2)<>0),
TEAM_FIX(ROUND,TEAMID,FIXID) AS (
SELECT 0,TEAMID,TEAMID FROM TEAMSX
UNION ALL
SELECT ROUND+1,TEAMID,
  CASE WHEN TEAMID=(SELECT COUNT(*)-1 FROM TEAMSX) THEN (SELECT COUNT(*)-1 FROM TEAMSX) ELSE 
  MOD(TEAMID+(ROUND+1),(SELECT COUNT(*)-1 FROM TEAMSX)) END FROM TEAM_FIX
WHERE ROUND < (SELECT COUNT(*)-2 FROM TEAMSX))
SELECT ROUND, TXT FROM (
SELECT TEAM1.ROUND,TEAM1.FIXID TM1,TEAM2.FIXID TM2, 
CASE WHEN TX.TEAMNAME='DUMMY' THEN TEAM1.FIXID || ' rests' ELSE TEAM1.FIXID || ' vs ' || TEAM2.FIXID END TXT FROM 
TEAM_FIX TEAM1
JOIN TEAM_FIX TEAM2 ON (TEAM1.ROUND=TEAM2.ROUND AND TEAM1.TEAMID+TEAM2.TEAMID=(SELECT COUNT(*)-1 FROM TEAMSX) AND TEAM1.TEAMID < TEAM2.TEAMID)
JOIN TEAMSX TX ON (TEAM2.TEAMID=TX.TEAMID)
ORDER BY TEAM1.ROUND,TEAM1.TEAMID)

第一个WITH表(TEAMS)就是创建一些样本数据。

如果队员数量奇数,第二张桌子(TEAMSX)将创建一个虚拟团队。

第三个表(TEAM_FIX)是一个递归CTE,它使用你给出的函数为每一轮生成正确的列表排序。

然后在主查询中,我们为每一轮加入TEAM_FIX,以创建团队对决。

答案 1 :(得分:1)

另一个Oracle解决方案。

<强>设置

CREATE TABLE League (
  LeagueID   INT PRIMARY KEY, 
  LeagueName VARCHAR(30) UNIQUE
);

CREATE TABLE Team (
  TeamID           INT PRIMARY KEY, 
  TeamAbbreviation CHAR(3) UNIQUE, 
  TeamName         VARCHAR(50) UNIQUE, 
  LeagueID         INT CONSTRAINT FK_Team_League REFERENCES League(LeagueID) 
);

CREATE TABLE Fixture (
  FixtureID   INT PRIMARY KEY,
  WeekNumber  INT NOT NULL,
  FixtureDate DATE NULL,
  HomeTeamID  INT NULL,
  AwayTeamID  INT NULL,
  LeagueID    INT CONSTRAINT FK_Fixture_League REFERENCES League(LeagueID)
);

INSERT INTO League VALUES ( 1, 'League 1' );
INSERT INTO League VALUES ( 2, 'League 2' );

INSERT INTO Team VALUES ( 1, 'AAA', 'Team A', 1 );
INSERT INTO Team VALUES ( 2, 'BBB', 'Team B', 1 );
INSERT INTO Team VALUES ( 3, 'CCC', 'Team C', 1 );
INSERT INTO Team VALUES ( 4, 'DDD', 'Team D', 1 );
INSERT INTO Team VALUES ( 5, 'EEE', 'Team E', 2 );
INSERT INTO Team VALUES ( 6, 'FFF', 'Team F', 2 );
INSERT INTO Team VALUES ( 7, 'GGG', 'Team G', 2 );
INSERT INTO Team VALUES ( 8, 'HHH', 'Team H', 2 );
INSERT INTO Team VALUES ( 9, 'III', 'Team I', 2 );

插入 - 灯具

INSERT INTO Fixture
WITH league_teams ( id, leagueid, idx, is_fake, num_teams, num_fake ) AS (
  -- Generate a unique-per-league index for each team that is between 0
  -- and the (number of teams - 1) and calculate the number of teams
  -- and if this is an odd number then generate a fake team as well.
  SELECT TeamID,
         LeagueID,
         ROW_NUMBER() OVER ( PARTITION BY LeagueID ORDER BY TeamID ) - 1,
         0,
         COUNT(1) OVER ( PARTITION BY LeagueID ),
         MOD( COUNT(1) OVER ( PARTITION BY LeagueID ), 2 )
  FROM Team
  UNION ALL
  SELECT NULL,
         LeagueID,
         COUNT(1),
         1,
         COUNT(1),
         1
  FROM   Team
  GROUP BY LeagueID
  HAVING MOD( COUNT(1), 2 ) > 0
),
cte ( home_idx, away_idx, week_number, leagueID, num_teams, num_fake ) AS (
  -- Start by calculating the round 1 games
  SELECT idx,
         num_teams + num_fake - 1 - idx,
         1,
         LeagueID,
         num_teams,
         num_fake
  FROM   league_teams
  WHERE  2 * idx < num_teams
UNION ALL
  -- Then generate the successive rounds with the two cases when the
  -- away team has the maximum index or otherwise.
  SELECT CASE away_idx
           WHEN num_teams + num_fake - 1
           THEN home_idx + 1
           ELSE MOD( home_idx + 1, num_teams + num_fake -1 )
           END,
         CASE away_idx
           WHEN num_teams + num_fake - 1
           THEN away_idx
           ELSE MOD( away_idx + 1, num_teams + num_fake - 1 )
           END,
        week_number + 1,
        LeagueID,
        num_teams,
        num_fake
  FROM  cte
  WHERE week_number < num_teams + num_fake - 1
)
-- Finally join the cte results back to the League_Teams table to convert
-- the indexes used in calculation back to the actual team ids.
SELECT rn,
       week_number,
       NULL,
       h.id,
       a.id,
       c.leagueid
FROM   (
         -- This step isn't necessary but it keeps the results in a nice order.
         SELECT ROWNUM AS rn,
                t.*
         FROM   (
           -- Duplicate the results swapping home and away.
           SELECT week_number,
                  home_idx,
                  away_idx,
                  LeagueId
           FROM   cte
           UNION ALL
           SELECT week_number + num_teams + num_fake - 1,
                  away_idx,
                  home_idx,
                  LeagueId
           FROM   cte
         ) t
       ) c
       INNER JOIN League_Teams h
       ON ( c.home_idx = h.idx AND c.leagueId = h.leagueID )
       INNER JOIN League_Teams a
       ON ( c.away_idx = a.idx AND c.leagueId = a.leagueID )
ORDER BY rn;

<强>输出

SELECT * FROM fixture;

 FIXTUREID WEEKNUMBER FIXTUREDATE         HOMETEAMID AWAYTEAMID   LEAGUEID
---------- ---------- ------------------- ---------- ---------- ----------
         1          1                              1          4          1 
         2          1                              2          3          1 
         3          1                              5                     2 
         4          1                              6          9          2 
         5          1                              7          8          2 
         6          2                              2          4          1 
         7          2                              3          1          1 
         8          2                              6                     2 
         9          2                              7          5          2 
        10          2                              8          9          2 
        11          3                              3          4          1 
        12          3                              1          2          1 
        13          3                              7                     2 
        14          3                              8          6          2 
        15          3                              9          5          2 
        16          4                              8                     2 
        17          4                              9          7          2 
        18          4                              5          6          2 
        19          5                              9                     2 
        20          5                              5          8          2 
        21          5                              6          7          2 
        22          4                              4          1          1 
        23          4                              3          2          1 
        24          6                                         5          2 
        25          6                              9          6          2 
        26          6                              8          7          2 
        27          5                              4          2          1 
        28          5                              1          3          1 
        29          7                                         6          2 
        30          7                              5          7          2 
        31          7                              9          8          2 
        32          6                              4          3          1 
        33          6                              2          1          1 
        34          8                                         7          2 
        35          8                              6          8          2 
        36          8                              5          9          2 
        37          9                                         8          2 
        38          9                              7          9          2 
        39          9                              6          5          2 
        40         10                                         9          2 
        41         10                              8          5          2 
        42         10                              7          6          2 

(注意:FixtureDateNULL,因为不清楚您希望如何生成这个,但您应该能够获取周数并将其用作从开头的偏移量生成日期的季节