Pouze tento týden sleva až 80 % na e-learning týkající se C# .NET. Zároveň využij akci až 30 % zdarma při nákupu e-learningu - Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
discount week 30

Diskuze: Náhodné vylosování soupeře

Aktivity
Avatar
BuBu6
Člen
Avatar
BuBu6:11.6.2020 14:40

Ahoj, potřeboval bych poradit, jak rozlosovat jednotlivé hráče do vzájemných zápasů.
Resp. mám v tabulce1 (mysql) 10 hráčů (10 řádků). A nyní bych potřeboval, aby se mi do jiné tabulky2 náhodně přiřadil soupeř.

Zkusil jsem: Zkoušel jsem jen toto:

INSERT INTO `tab2` (`id_player_one`) SELECT (`id_player`) FROM `tab1` ORDER BY RAND()

To mi do tab2 zapíše všechny hráče na jednotlivé řádky v náhodném pořadí, ale nevím, jak udělat příkaz pro můj požadavek.

Chci docílit: Snažím se dosáhnout toho, aby z tabulky1 (10 hráčů) vzniklo v tabulce2 náhodně vybraných 5 zápasů (id_player_one, id_player_two)

 
Odpovědět
11.6.2020 14:40
Avatar
Odpovídá na BuBu6
Martin Konečný (pavelco1998):11.6.2020 15:09

Ahoj, podle mě by mělo stačit na konec dotazu přidat LIMIT 5, tedy:

INSERT INTO `tab2` (`id_player_one`) SELECT (`id_player`) FROM `tab1` ORDER BY RAND() LIMIT 5;
Nahoru Odpovědět
11.6.2020 15:09
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
BuBu6
Člen
Avatar
Odpovídá na Martin Konečný (pavelco1998)
BuBu6:11.6.2020 15:19

To jasně není problém, ale pak mi to vypíše a doplní do tab2 pouze id_player_one a pouze 5 hráčů, ale už mi to k nim nepřiřadí do sloupce id_player_two soupeře. Ta druhá tabulka by měla vypadat takto:
Sloupec 1: id_player_one = id_player
Sloupec 2: id_player_two = id_player
A takto bych měl při 10 hráčích vytvořeno 5 řádků.
Pozn.: Navíc 10 hráčů je příklad, ale množství je proměnlivé.

 
Nahoru Odpovědět
11.6.2020 15:19
Avatar
Alesh
Překladatel
Avatar
Odpovídá na Martin Konečný (pavelco1998)
Alesh:11.6.2020 15:22

No, jestli ho dobře chápu, tak chce vylosovat 5 dvojic, nikoli jen 5 náhodných hráčů. To co navrhuješ mu dá 5 hráčů (bez soupeře).

 
Nahoru Odpovědět
11.6.2020 15:22
Avatar
Martin Konečný (pavelco1998):11.6.2020 15:34

BuBu6 Alesh Jasné, už asi rozumím. Z hlavy takový dotaz bohužel nevymyslím, mám ale pocit, že by to mělo jít pomocí JOIN na stejnou tabulku, něco ve smyslu:

INSERT INTO `tab2` (`id_player_one`, `id_player_two`)
SELECT tab1.id_player, tab1_2.id_player
FROM tab1
JOIN tab1 AS tab1_2
WHERE tab1.id_player != tab1_2.id_player
ORDER BY RAND()
LIMIT 5;

Pokud sem nepřijde někdo chytřejší než já, tak si to pak zkusím, jestli mě nenapadne správné řešení :D

Editováno 11.6.2020 15:34
Nahoru Odpovědět
11.6.2020 15:34
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
BuBu6
Člen
Avatar
Odpovídá na Martin Konečný (pavelco1998)
BuBu6:11.6.2020 16:08

Super, tohle funguje :) Jen to má drobnou, v tomto případě dost podstatnou, vadu.
V tom rozlosování mi to opakuje některé hráče, tzn., že tam nejsou všichni a někteří jsou tam zase dvakrát. Toto lze nějak ošetřit? Díky.

 
Nahoru Odpovědět
11.6.2020 16:08
Avatar
Odpovídá na BuBu6
Martin Konečný (pavelco1998):11.6.2020 17:20

Určitě to ošetřit jde, ale bohužel mě nenapadá jak :D zkoušel jsem i udělat podmínku, aby to vybralo jen ty, kteří již v tabulce "tab2" nejsou. Problém ale je, že prvně běží WHERE a teprve pak ty INSERTy, takže to nebere v úvahu ty, které se přidaly tím příkazem.

Také by mě zajímalo, zda se to dá pořešit jedním INSERT dotazem.

Nahoru Odpovědět
11.6.2020 17:20
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:12.6.2020 9:01

Kdyby slo o to ziskat jen pary, tak

  • vemes seznam hracu A, B, C, D
  • zamichas ho C B A D
  • rozdelis na pulky C B | A D
  • a spojis dvojice CA BD

Jenze tento postup nemuzes pouzit pro dalsi kolo, pokud se ti nema opakovat par. Ani ty postupy, ktere tu zminujete :)
ORDER BY RAND() neni nahodne promichani, ale prirazeni nahodneho cisla a serazeni. Kvalita toho zavisi na tom, jak nahodne cislo to je a kolik ma deset. mist. Protoze treba rantom 1-10 znamena, ze se muzou cisla opakovat, a to treba xx1x111x11 a order to pak seradi tak, ze jednicky pujdou po sobe, cili cisla na pozici 5, 6, 7 budou po sobe. Coz se pri nahodnem zamichani muze stat, ano. Ale vam se to bude stavat mnohem casteji, nez aby to byla nahoda.
Cili, ja bych to seradil nejdriv DESC, pak RAND, pak DESC (mysleno jako obracene poradi posledniho vysledku, ne radit to podle sloupce) a opet RAND.

 
Nahoru Odpovědět
12.6.2020 9:01
Avatar
BuBu6
Člen
Avatar
BuBu6:14.6.2020 12:44

Díky, něco vyzkouším.
V podstatě mi jde jen o los prvního kola, další už jsou dle odehraných kritérií z prvního kola, tzv. Švýcarský systém. Šlo mi opravdu jen o ten náhodný los prvního kola.

 
Nahoru Odpovědět
14.6.2020 12:44
Avatar
BuBu6
Člen
Avatar
BuBu6:22.6.2020 5:26

Nějak jsem se do toho zamotal.
Mám tabulku odehraných zápasů a tabulku všech možných zápasů (každý s kadým).
Dále následující select:

SELECT DISTINCT vsichni.id_hrac_a, vsichni.id_hrac_b FROM vsichni
        LEFT JOIN zapasy
        ON vsichni.id_hrac_a = zapasy.id_hrac_a AND vsichni.id_hrac_b = zapasy.id_hrac_b
        WHERE zapasy.id_hrac_a IS NULL
        ORDER BY RAND() LIMIT 4

Jak mám docílit toho, aby se mi neopakovali v jednom losování soupeři? Resp jako hráč A aby byl jednou v levém sloupci, stejně tak hráč 2 v pravém sloupci a aby nebyl stejný hráč i křížem?
Studoval jsem příkaz DISTINCT, ale nějak mi to nefunguje :(

 
Nahoru Odpovědět
22.6.2020 5:26
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:22.6.2020 8:00

A co zkusit upravit sql dotaz pro me reseni?
Vsechny zamichas, pole rozdelis napul. A muzes to udelat pres sql nebo php.

$list <- 'SELECT * FROm players'
shuffle($list);
$list_sude = array()
$list_liche = array()
foreach ($list as $key=>$value)
if ($key&1==0) {$list_sude[] = $value;} else  {$list_liche[] = $value;}
//if ($key and 0x01==0) {$list_sude[] = $value;} else  {$list_liche[] = $value;}
//if ($key%2==0) {$list_sude[] = $value;} else  {$list_liche[] = $value;}
//if ($a=$key/2 && floor($a)==$a) {$list_sude[] = $value;} else  {$list_liche[] = $value;}
Editováno 22.6.2020 8:01
 
Nahoru Odpovědět
22.6.2020 8:00
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:22.6.2020 8:20
SELECT
    GROUP_CONCAT (`p2`.`id`) -- id1, id2
FROM
    (
    SELECT `id`, FLOOR((COUNT(*)+1)/2) `num` FROM
         (SELECT `id` FROM `players` ORDER BY RAND()) `p1`
    ) `p2`
GROUP BY `p2`.`num`
  1. Vyberu celou tabulku
  2. Nahodne ji seradim
  3. priradim kazdemu radku cislo 1, 2, 3
  4. toto cislo upravim floor((x+1)/2)
(1+1)/2 = 1; floor = 1
(2+1)/2 = 1.5; floor = 1
(3+1)/2 = 2; floor = 2
(4+1)/2 = 2.5; floor = 2
(5+1)/2 = 3; floor = 3
(6+1)/2 = 3.5; floor = 3

(docilim tim 2 po sobe jdouci id jako skupiny se stejnym cislem)
5. pak to zgrupuji podle toho cisla
Samozrejme si k tomu muzes pridat jine sloupce pres LEFT JOIN :)
Priklady na pouzite sql prikazy se daji vygooglovat.
Priklad toho nahodneho promichani mozna take.

 
Nahoru Odpovědět
22.6.2020 8:20
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:22.6.2020 8:35

Nebo by to slo pres programovy kod a ukladani do pameti, ale to sem zatim nikdo nepouzil. Takze tohle je spis pseudokod, nez, ze by to mohlo fungovat :)

@a = SELECT `id` FROM `players` ORDER BY RAND()
@b = SELECT `id`, FLOOR((COUNT(*)+1)/2) `num` @a
@a = null;
SELECT b.id, c.id
FROM
    @b b
    LEFT JOIN @b c ON c.num=b.num AND c.id<>b.id
WHERE FLOOR(COUNT()/2)==COUNT()/2
  1. Ulozis si random serazene do pameti
  2. Pridas k tomu upravene cislo radku
  3. Pak si to selectem vytahnes a pripojis k tomu tu samou tabulku pres LEFT JOIN, ale pojmenujes ji aliasem c. A zadas podminku, ze se num=num, ale id<>id.

Protoze stejne cislo maji pouze 2 radky, tak rodilne id jen jeden, tak se ti naparuji prave ty 2 id.
4. Ale vznikne to pro kazdy radek, takze je nutne odfiltrovat kazdy druhy radek, WHERE FLOOR(COUNT()/2)==CO­UNT()/2


DISTINCT odstranuje duplicitni radky, pokud jsou vsechny hodnoty stejne

1, 2
2, 1 nejsou duplicity

1, 2
1, 2 jsou duplicity
 
Nahoru Odpovědět
22.6.2020 8:35
Avatar
BuBu6
Člen
Avatar
Odpovídá na Peter Mlich
BuBu6:23.6.2020 22:34

Já moc děkuji za rozsáhlou odpověď, ale já jsme úplně v koncích, protože prostě nevím, jak to přesně do toho začlenit. Pořád mi to píše, že GROUP_CONCAT neexistuje :(

Syntax error or access violation: 1630 FUNCTION soutez.GROUP_CONCAT does not exist.

Asi to vzdávám, zřejmě je to pro mě jako pro amatéra dost složité.
Ale děkuji za pomoc.

 
Nahoru Odpovědět
23.6.2020 22:34
Avatar
Odpovídá na BuBu6
Martin Konečný (pavelco1998):24.6.2020 2:37

A je potřeba nutně to udělat přes jeden SQL dotaz?

Nahoru Odpovědět
24.6.2020 2:37
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
BuBu6
Člen
Avatar
Odpovídá na Martin Konečný (pavelco1998)
BuBu6:24.6.2020 3:23

Klidně bych to udělal i na třeba 10 dotazů, ale nevím jak, nějak jsem to nepochopil :(

 
Nahoru Odpovědět
24.6.2020 3:23
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:24.6.2020 7:52

Psal jsi, ze mas MySql. Podle online dokumentace ta funkce existuje. Pokud mas jiny sql, tak se ta funkce muze jmenovat jinak.
https://dev.mysql.com/…nctions.html
https://www.mysqltutorial.org/…roup_concat/
https://oracle-base.com/…n-techniques
https://docs.oracle.com/…tions003.htm#…

mysql: GROUP_CONCAT
oracle: RTRIM(LISTAGG(RE­FERENCE || ', ') WITHIN GROUP(ORDER BY 1),', ') REFERENCE
mssql: STRING_AGG, WITHIN GROUP

Ta funkce ale dela jen to, ze z radku, ktere mas zgrupovane, veme hodnoty pro sloupec a spoji je do stringu. Grupovat to nemusis. Jen proste jedno id bude na jednom radku a druhe na jinem. Tak misto toho pouzijes ORDER BY p2.num, abys mel ty cisla po sobe.

Nic, no, kdyz se ti nechce premyslet, ani z vysvetleni nejsi schopen poskladat kod. Ani php kod se ti nelibi. Ani nejsi schopen s sql kodem a s pomoci googlu si ten dotaz doladit... Co s tebou? Poradi nekdo jiny :) Takoveho studenta informatiky bych teda nechtel.

Editováno 24.6.2020 7:53
 
Nahoru Odpovědět
24.6.2020 7:52
Avatar
BuBu6
Člen
Avatar
Odpovídá na Peter Mlich
BuBu6:24.6.2020 8:56

Ano mám MySql.
Nevím sice, proč by si takového studenta nechtěl, ale proseděl jsem nad tím (ještě i předtím, než jsem poslal dotaz), nocí a miliony kombinací a řešení a googlil kde mohl, ale nikdy jsem neobjevil konkrétní odpověď. Za Tvé odpovědi Ti samozřejmě děkuji a vážím si toho, ale vzhledem k tomu, že programuji pouze pro zábavu a nejsem v tom moc učený, tak jsem prostě jen nepochopil, jak se s Tvou nápovědou poprat, protože mi to buď hází nějaké chyby nebo nefunguje jak má.

 
Nahoru Odpovědět
24.6.2020 8:56
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:24.6.2020 10:32

Jsem ti popsal princip, kod si muzes poskladat sam.

  • vyberes hrace
  • zamichas (treba php shuffle, priklady na funkci najdes google nebo v dokmentaci)
  • rozdelis na 2 poloviny
  • a propojis hodnoty na stejnem radku obou polovin

Pokud na tohle nedokazes napsat kod v php, tak nevim, co ti vic poradit. Tohle umi stredoskolak po prvni hodine, treba v pascalu.

1. A B C D
2. C B A D (zamichano)
3. leva = C B, prava = A D (polovina pole)
4. vysledek = [
    [ leva[0], prava[0] ],
    [ leva[1], prava[1] ],
    [ leva[2], prava[2] ]...
    ]
[C, A], [B, D]

Chapu, ze si mozna neporadis s sql, ale napsat to v php, to snad zvladnes.

Editováno 24.6.2020 10:33
 
Nahoru Odpovědět
24.6.2020 10:32
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:24.6.2020 10:36

A nez tam v sql do toho zacnes michat jmena, tak si to nejdriv vyres s id. Tve pokusy jsi nezverejnil, takze ti neporadime, kde delas chybu. Jenom, ze ti to pise chybu. Coz bez celeho sql prikazu se da nekdy tezko odhadnout proc. Treba tam nemas nekde zpetne apostrofy.

 
Nahoru Odpovědět
24.6.2020 10:36
Avatar
Odpovídá na BuBu6
Martin Konečný (pavelco1998):24.6.2020 12:43

Napadají mě tři potenciální možnosti:

  1. Jestli víš, kolik těch insertů potřebuješ, tak jej můžeš 5x nakopírovat a pokaždé vybrat jen ty hráče, kteří ještě nejsou v té tabulce pro zápas, něco jako
INSERT INTO zapas (player1, player2)
SELECT tab1.id_player, tab1_2.id_player
FROM tab1
JOIN tab1 AS tab1_2
WHERE tab1.id_player != tab1_2.id_player AND NOT EXISTS(
    SELECT 1
    FROM zapas
    WHERE player1 = tab1.id_player OR player2 = tab1.id_player OR player1 = tab2.id_player OR player2 = tab2.id_player
)
ORDER BY RAND()
LIMIT 1;

-- a takhle třeba 5x
  1. Abys nemusel duplikovat dotaz s těmi inserty, myslím, že by mělo být možné na to udělat proceduru, která umí i cyklus. Něco ve smyslu
CREATE PROCEDURE createOpponents()
BEGIN

        DECLARE index INT;
        SET index = 1;

        cyklus: LOOP

                IF index = 5 THEN
                        LEAVE cyklus;
                END IF;

                INSERT INTO zapas (player1, player2)
                SELECT tab1.id_player, tab1_2.id_player
                FROM tab1
                JOIN tab1 AS tab1_2
                WHERE tab1.id_player != tab1_2.id_player AND NOT EXISTS(
                        SELECT 1
                        FROM zapas
                        WHERE player1 = tab1.id_player OR player2 = tab1.id_player OR player1 = tab2.id_player OR player2 = tab2.id_player
                )
                ORDER BY RAND()
                LIMIT 1;

                SET index = index + 1;

        END LOOP;

END;

-- to pak zavoláš jednoduše CALL createOpponents();

Vycházel jsem z: https://www.mysqltutorial.org/…s-loop.aspx/

  1. Nemít to jako databázový dotaz/skript, ale udělat si to v nějakém jazyku, např. PHP, pokud v něm děláš. Tam můžeš dělat cyklus, unset atd. o něco jednodušeji a nemusí se to hned ukládat někam do tabulky.
Nahoru Odpovědět
24.6.2020 12:43
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Radovan Dřímal:23. května 11:21

Asi už je to mimo mísu , ale chtěl jsem si vyřešit tento problém a trochu se u toho něco naučit :)

sp: BEGIN
    DECLARE counter INT DEFAULT 1;
    DECLARE pocet INT DEFAULT 0;

    -- nastavení čítače
    SET counter  = 1;

        --  výpočet poloviny počtu hráčů (tabulka hráči by měla obsahovat alespoň sloupec id_hrace)
         SELECT COUNT(id_hrace)/2 INTO pocet FROM hraci;

        -- pokud nejsou hráči ukonči proceduru
        IF pocet = 0 THEN
                LEAVE sp;
        END IF;

        -- Vytvoření tabulky turnaj pokud neexistuje
        CREATE TABLE IF NOT EXISTS  `turnaj` (
        `id_turnaj` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
        `id_hrac1` INT(11) UNSIGNED NULL DEFAULT NULL,
        `id_hrac2` INT(11) UNSIGNED NULL DEFAULT NULL,
        PRIMARY KEY (`id_turnaj`) USING BTREE)
                COLLATE='utf8_unicode_ci'
                ENGINE=InnoDB;

        -- Pokud existuje její vyčištění  (reset auto incrementu)
        TRUNCATE TABLE turnaj;

        -- vyber náhodně první polovinu hráčů a insertuj do tabulky turnaj
        INSERT INTO turnaj
        SELECT NULL, id_hrace, NULL FROM hraci
        ORDER BY RAND()
        LIMIT pocet;


         -- Doplň tabulku turnaj náhodnými hráči z druhé půlky tabulky hráči
         disp: LOOP

                -- Vytvoř pomocnou tabulku pro výběr ( vyber všechny již použité hráče)
                CREATE TABLE tmp AS
                        (SELECT id_hrac1 FROM turnaj
                                UNION
                        SELECT id_hrac2 FROM turnaj);

                -- Doplň vždy po jednom hráči náhodně do tabulky turnaj hráče kteří ještě nebyli použiti
                UPDATE turnaj AS tu  SET tu.id_hrac2 =
                        (SELECT h.id_hrace  FROM hraci AS h
                        LEFT JOIN tmp AS t ON h.id_hrace=t.id_hrac1
                        WHERE   t.id_hrac1 IS NULL
                        ORDER BY RAND()
                        LIMIT 1)
                WHERE  tu.id_turnaj = counter ;

                -- odstraň tabulku tmp pro nový běh a ukliď na konci :)
                DROP TABLE tmp;

                -- ukonči  smyčku pokud  byli doplněni všichni hráči
                IF counter = pocet THEN
                        LEAVE disp;
                END IF;

                -- inkrementace čítače
                SET counter = counter + 1;
        END LOOP;
END
 
Nahoru Odpovědět
23. května 11:21
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 22 zpráv z 22.