2016-06-09 37 views
9

Sono stato gentilmente fornito questo algoritmo per aiutarmi a creare un elenco di fixture in SQL, ma applicandolo come codice SQL, non ho idea di come fare. C'è un modo in cui qualcuno può guidarmi su come applicarlo con il codice?Come codificare un determinato algoritmo matematico

Qui di seguito è il mio schema di tabelle e sotto che è l'algoritmo:

League:

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

della squadra:

[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) 

Fixture:

[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) 

ALGORITMO:

Consente a tradurre l'algoritmo, chiamato round robin scheduling, in termini di una lista ordinata l delle squadre N (che corrispondono a N-1 poligono vertici + il centro del poligono):

  • l definisce infissi giocando la prima squadra dalla lista contro l'ultima, la seconda contro la prima prima, ecc.
    Cioè, per 0 ≤ x < N, si gioca la squadra l[x] contro la squadra l[N-1-x].

  • Per generare il set successivo di dispositivi, si ruotano i primi elementi dell'elenco N-1.
    Questo è l = l[1] + l[2] + ... + l[N-2] + l[0] + l[N-1]

  • Una volta fatto il set completo di N-1 rotazioni, farlo di nuovo, ma lo scambio di casa e in trasferta le squadre: giocherà l[N-1-x] vs squadra l[x] anziché il contrario.

Se si inizia con l'elenco numerico ordinato 0..N-1 poi alla rotonda i la lista è:
l = [(i + 0) % (N-1)] + [(i + 1) % (N-1)] + ... + [(i + N-2) % (N-1)] + [N-1]

Cioè, infissi sono a turno i:

  • i vs N-1
  • Per 0 < x < (N-1)/2, (x + i) % (N-1) vs (N-1 - x + i) % (N-1)

Ora c'è un trucco, in quanto questo funziona solo per i numeri pari. Altrimenti l'ultima squadra gioca sempre (contro la squadra i al turno i) mentre naturalmente ogni turno ha una squadra che non può giocare. Ciò significherebbe che la squadra 4 gioca una partita in più rispetto alle altre squadre.

Per risolvere questo problema, abbiamo aggiungiamo un team fittizio, quindi per 5 squadre abbiamo N = 6, e al giro i:

  • i vs 5 (la squadra fittizio)
  • (i + 1) % 4 vs (4 + i) % 4
  • (i + 2) % 4 vs (3 + i) % 4

Ora che lo sai, puoi generare una funzione che ti darà fixture basate sul numero tondo. Esso deve emettere il seguente:

turno 0: 0 resti, 1 vs 4, 2 vs 3
rotonda 1: 1 resti, 2 vs 0, 3 vs 4
rotondo 2: 2 pause, 3 vs 1, 4 vs 0
rotondo 3: 3 resti, 4 vs 2, 0 vs 1
rotondo 4: 4 resti, 0 vs 3, 1 vs 2


Nota che invece di i in le formule x + i e N-1 - x + i è possibile utilizzare qualsiasi multiplo m * i (quindi x + m * i e N-1 - x + m * i) fino a m e N-1 e relatively prime. Qui N - 1 = 5 è primo, quindi puoi utilizzare qualsiasi m che desideri.

UPDATE:

Come necessario sotto è te testare dati per prima la classifica e secondo la tabella squadre (corrispondenti colonne di schema tabella in ordine)

campionato:

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

Squadre:

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 

squadre giocano tra loro casa e in trasferta e può giocare solo contro le squadre che si trovano nella stessa lega (da qui le varie LeagueIDs)

Le partite dovrebbero essere come questo per ogni turno:

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 
  • League numero è numero tondo 'LeagueID'
  • è 'WeekNumber'
  • Data è 'Data Fixture'
  • numero squadra di casa è 'HomeTeamID'
  • Il numero di squadra assente è "AwayTeamID"

Questo dovrebbe essere inserito nella tabella "Fixture".

+4

Ora che è come un domanda dovrebbe essere posta. –

+0

semplicemente collegandoti alla tua domanda precedente: http://stackoverflow.com/questions/37610060/creating-a-fixture-list-logic – Tanner

+0

@Tanner Cool, sì il problema con quell'approccio precedente era anche se mostrava correttamente le squadre , ha avuto problemi a visualizzare i numeri settimanali corretti per round di partite. In questo modo l'utilizzo di un algoritmo aiuterà a risolvere il problema della settimana, ma è un percorso completamente nuovo e più matematico. –

risposta

1

Un'altra soluzione Oracle.

Setup:

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 - Prossime:

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; 

uscita:

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 

(Nota: FixtureDate è NULL in quanto non è chiaro come si desidera che questo ha generato, ma si dovrebbe essere in grado di prendere il numero della settimana e utilizzare questo come l'offset dall'inizio della stagione per generare le date)

+0

Cool MT0, darò questa possibilità quando avrò la possibilità e ti farò sapere il risultato. Se riesco a farlo funzionare, pubblicherò la soluzione SQL Server. Grazie –

+0

Ciao Mt0, data la tua migliore risposta siccome credo che la tua versione oracolo funzionerà. Sono state provate le ultime due ore per convertire utilizzando SQL Server ma non hanno avuto successo. Pubblicherò un'altra domanda per vedere se qualcuno può convertirlo in codice SQL Server e collegare questa domanda a quella domanda per aiutare chi si occupa di SQL Server. Grazie mille :) –

+0

La risposta di SQL Server può essere trovata qui: http://stackoverflow.com/questions/37746092/converting-oracle-code-to-sql-server-for-a-fixture-list, grazie per tutto il tuo aiuto :) –

1

Quanto segue sembra ottenere quello che vuoi, ma questo è Oracle SQL (al momento non ho accesso a un DB del server SQL). Ma credo che tutto ciò che serve per cambiare è la funzione MOD, la concatenazione di testo, e il riferimento alla DUAL per farlo lavorare su 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) 

La prima con tavolo (squadre) è solo per creare un po 'di esempio dati.

La seconda tabella (TEAMSX) creerà un team fittizio se abbiamo un numero dispari di squadre.

La terza tabella (TEAM_FIX) è una CTE ricorsiva che utilizza la funzione che hai dato per generare la lista ordinamento adeguato per ogni turno.

Poi nella query principale, ci uniamo TEAM_FIX contro se stessa per ogni turno per creare matchups squadra.

+0

fresco Mike, mi darà un andare quando ho tempo e ti faccio sapere il risultato. Grazie –