2018年8月28日火曜日

トーナメントでチームメイトと出来るだけ当たらないようにしたい

 トーナメントの組み合わせ作成を完全に順位に従わせると、同じチームの人と早い段階でぶつかってしまうことがある。前回作成したプログラムの出力結果のトーナメント表を見てみると、こんなことになっている。

1回戦で同士討ち、勝者は2回戦でチームメイト(しかもシード!)と対戦
世界大会レベルなら仕方ない気もするが、一般的な試合でこういうことは避けるように組み合わせを考えたいので、そのようなものを考えてみる。
①最初にシードを分配(groups_only_ranker())
②参加者が最も多いチームの参加者数を超える最小の2^k個のブロックになるよう空のリストを追加(add_empty_groups())
③各ブロックにどのチームの人を入れるかを順に分配(dist_unranker())

tournament_distribution.py
--------------------------------------

#-*- coding:utf-8 -*-
import numpy as np
import random
def Nseed_group(Nseed, ranker_team, team_participant):
    #Input
    #Nseed: Number of seeds
    #ranker_team(vector): seed_team[i] means the team number of the (i+1)th ranker
    #team_participant: Number of participants of each teams (NumPy vector)
    team_ranker=cal_team_ranker(team_participant.shape[0], ranker_team)
   
    #Output
    #group list
   
    #get group list including only rankers
    group_list=groups_only_ranker(ranker_team)
   
    #add empty groups (unseed blocks) into group list
    group_list=add_empty_groups(group_list, np.max(team_participant))
   
    #unranker distribution
    team_unranker=team_participant-team_ranker    ##Number of unrankers of each teams
    group_list=dist_unranker(group_list, team_unranker, team_participant)
   
    return group_list
   
def cal_team_ranker(n_group, ranker_team):
    team_ranker=np.zeros(n_group)
   
    for i in range(n_group):
        team_ranker[i]=np.sum(ranker_team==(i+1))
       
    return team_ranker
def NumOfDigits_2(x):
    #Input
    #x: number
   
    #Output
    #k: the integer as "2^(k-1) < x <= 2^k"
   
    k=1
    while pow(2, k)<x:
        k+=1
       
    return k

def add_empty_groups(group_list, max_team_participant):
    #add empty groups (unseed blocks)
    #Input
    #group_list: group list with only seed blocks
    #max_team_participant: max number of one team participants (add empty blocks as number of blocks >= 2^k between two seed blocks)
   
    #Output
    #
    k1=pow(2, NumOfDigits_2(max_team_participant))
    k2=len(group_list)
   
    while k2<k1:
        temp=[]
        for i in range(np.int(k2/2)):
            temp.append(group_list[2*i])
            temp.append([])
            temp.append([])
            temp.append(group_list[2*i+1])
       
        group_list=temp
        k2=len(group_list)
       
    return group_list

def groups_only_ranker(ranker_team):
    #Input
    #ranker_team(vector): ranker_team[i] means the team number of the (i+1)th ranker
   
    #Output
    #groups(list):
   
    #get the order of seeds in tournament
    position=np.array([1, 2])
    p=1
    N=ranker_team.shape[0]
    while N>pow(2, p):
        i=0
        while i<pow(2, p-1):
            position=np.r_[position[:4*i+1], np.zeros(2), position[4*i+1:]]
            i+=1
        i=0
        while i<pow(2, p):
            if position[2*i]==0:
                position[2*i]=pow(2, p+1)+1-position[2*i+1]
            else:
                position[2*i+1]=pow(2, p+1)+1-position[2*i]
            i+=1   
        p+=1
   
    #make list of seed groups
    groups=[]
    for i in range(N):
        groups.append([ranker_team[np.int(position[i]-1)]])
       
    return groups

def dist_unranker(group_list, team_unranker, team_participant):
    for i in range(team_unranker.shape[0]):
        group_list=add_teamx(group_list, i+1, np.int(team_unranker[i]), np.int(team_participant[i]))
       
    return group_list

def add_teamx(group_list, x, added_n, group_n):
    digit=NumOfDigits_2(group_n)
    for i in range(added_n):
        group_list=add_teamx_one(group_list, pow(2, digit), x)
       
    return group_list

def add_teamx_one(group_list, group_num, x):
    num_group_teamx=np.zeros(group_num)
    num_group_allteam=np.zeros(group_num)
   
    num_partition=np.int(len(group_list)/group_num)
   
    for i in range(group_num):
        num_group_teamx[i]=np.sum(np.array([b for a in group_list[num_partition*i:num_partition*(i+1)] for b in a])==x)
        num_group_allteam[i]=len([b for a in group_list[num_partition*i:num_partition*(i+1)] for b in a])
       
    few_teamx_group_index=[]
    few_allteam_group_index=[]
   
    for i in range(group_num):
        if num_group_teamx[i]==np.min(num_group_teamx):
            few_teamx_group_index.append(i)
           
        if num_group_allteam[i]==np.min(num_group_allteam):
            few_allteam_group_index.append(i)
           
    index=choice_index(few_teamx_group_index, few_allteam_group_index)
    if num_partition==1:
        group_list[index].append(x)
    else:
        group_list[num_partition*index:num_partition*(index+1)]=add_teamx_one_partly(group_list[num_partition*index:num_partition*(index+1)], x)
    return group_list

def choice_index(few_teamx_group_index, few_allteam_group_index):
    commons=list(set(few_teamx_group_index) & set(few_allteam_group_index))
    if len(commons)==0:
        index=random.choice(few_teamx_group_index)
    elif len(commons)==1:
        index=commons[0]
    else:
        index=random.choice(commons)
       
    return index
   
def add_teamx_one_partly(group_list_partly, x):
    num_participants_in_partly_groups=np.zeros(len(group_list_partly))
    for i in range(num_participants_in_partly_groups.size):
        num_participants_in_partly_groups[i]=len(group_list_partly[i])
       
    few_allteam_partly_group_index=[]
    for i in range(num_participants_in_partly_groups.size):
        if num_participants_in_partly_groups[i]==np.min(num_participants_in_partly_groups):
            few_allteam_partly_group_index.append(i)
           
    index=few_allteam_partly_group_index[np.random.choice(len(few_allteam_partly_group_index))]
    group_list_partly[index].append(x)
    return group_list_partly

--------------------------------------
と関数を作成した。

動作検証で次のようにする。
ranker_team: 1位から順にランカーのチームIDを並べたNumpy配列
team_participant: チームID順に各チームの参加者数を並べたNumpy配列

--------------------------------------

ranker_team=np.array([1, 4, 7, 3, 2, 6, 7, 12, 3, 2, 18, 15, 21, 4, 1, 3])
team_participant=np.array([12, 18, 10, 8, 16, 13, 12, 14, 7, 5, 6, 9, 7, 11, 5, 4, 8, 8, 9, 10, 13, 12])
temp=Nseed_group(16, ranker_team, team_participant)

for i in temp:
    print(i)

--------------------------------------

[1, 2, 6, 8, 14, 20]
[2, 4, 5, 11, 13, 19, 22]
[1, 6, 7, 9, 17, 19]
[3, 5, 8, 10, 14, 18, 21]
[3, 2, 6, 9, 15, 20, 22]
[1, 4, 5, 7, 14, 17, 21]
[1, 2, 6, 11, 13, 18]
[12, 5, 7, 10, 16, 20, 21]
[2, 3, 5, 12, 14, 17, 22]
[1, 4, 7, 8, 16, 18, 20]
[2, 5, 6, 8, 13, 20, 21]
[15, 2, 7, 9, 14, 19, 22]
[21, 3, 7, 12, 17, 20]
[2, 4, 5, 8, 13, 19, 22]
[2, 6, 7, 10, 12, 18]
[3, 5, 8, 9, 15, 19, 21]
[7, 2, 8, 10, 14, 20, 22]
[2, 5, 6, 11, 13, 19, 21]
[1, 2, 6, 9, 15, 18, 21]
[4, 3, 5, 8, 14, 17, 22]
[18, 3, 6, 12, 14, 17, 21]
[1, 4, 5, 8, 16, 19, 22]
[1, 2, 7, 11, 14, 20, 22]
[6, 2, 5, 8, 13, 19]
[7, 2, 8, 10, 12, 20, 22]
[1, 3, 5, 11, 14, 16, 21]
[1, 3, 5, 8, 12, 17, 21]
[2, 4, 6, 9, 13, 18]
[1, 3, 6, 9, 14, 17, 21]
[2, 5, 7, 8, 12, 18, 22]
[1, 5, 6, 8, 12, 19, 22]
[4, 2, 7, 11, 15, 20, 21]

--------------------------------------
 乱数を使うので生成するたびに異なる結果が返ってくるので、これは一例。
 おおむねうまくいっているように見えるが、それでもうまく分配できていない。1行目のブロックを勝ち進んだ人は2行目のブロックを勝ち進んできた人と対戦するが、ここでチームID2の同士討ちが起きる可能性がある。その一方3, 4行目にはチームID2が含まれていない。このことは1, 2行目のどちらかのチームID2が3, 4行目のどちらかにいれば避けられた同士討ちである。また他との兼ね合いの関係でどうしても出来てしまう同士討ちならともかく、少なくとも今の例でいえば4行目の3と交換する分には、他に同士討ちを生じるわけでもない。多分探せば他にもいっぱいありそう。

 頭で考えているアルゴリズムだとそういうことはないはずなので、うーんと頭を抱えている。その一方で、人数がおおむねそろっているのが逆に予想外だった。同士討ちが絶対に最遠になるようにしたはずなので、各ブロックの人数が2人以上離れることもあると思ったが、実際には6か7人で大体そろっている。

 うーん。わからん。

https://github.com/nkknj/magimenalmirai/blob/master/20180828/tournament_distribution.py

2018年8月17日金曜日

トーナメント表の作成

 前回の続きとして、openpyxlでトーナメント表を作成した。とりあえず参加者が一列に並んでいる状態のもの。
 プログラムの途中までは、一昨日のものと全く一緒。その後ろでopenpyxlで作業をしている。手順として、
(1)左に必要な選手情報を並べる。
(2)横に線を引く
(3)上から順次2回戦分の線を引いていく。(条件分岐: シード横の場合1回戦分の線も引く)
(4)線を引くのと同時に順次試合番号の記入
(5)3回戦以降は各位置に対応するインデックスを保持して、インデックス2個ずつで順次線を引くと同時に試合番号の記入

list2tournament.py
--------------------------------------------------------

import sys
import numpy as np
import pandas as pd
import openpyxl
from openpyxl.styles import Alignment, Border, Side
filename=sys.argv[1]
df=pd.read_excel(filename)
df=df.sort_values(by=["前年度ランク"], ascending=True)
rank=df[df["前年度ランク"]>=0]
temp=df[~(df["前年度ランク"]>=0)]
temp=temp.reset_index(drop=True)
K=np.zeros(temp.shape[0])
for i in range(K.shape[0]):
    K[i]=np.sum(df['チーム名']==temp['チーム名'][i])
   
temp['チーム内ランク']=temp['チーム内ランク']/K
temp=temp.sort_values(by=["チーム内ランク", "チームランク"], ascending=True)
rank=pd.concat([rank, temp])
rank=rank.reset_index(drop=True)
rank.index+=1
rank.to_excel('rank_'+filename)
#tournament pairs considering rank
N=rank.shape[0]
position=np.array([1, 4, 3, 2])
p1=2
while N>pow(2, p1):
    i=0
    while i<pow(2, p1-1):
        position=np.r_[position[:4*i+1], np.zeros(2), position[4*i+1:]]
        i+=1
       
    i=0
    while i<pow(2, p1):
        if position[2*i]==0:
            position[2*i]=pow(2, p1+1)+1-position[2*i+1]
        else:
            position[2*i+1]=pow(2, p1+1)+1-position[2*i]
        i+=1   
    p1+=1
   
p2=1
while N-pow(2, p1-1)>pow(2, p2):
    p2+=1
   
position=position[position<=pow(2, p1-1)+pow(2, p2)]
position[position>N]=0

wb=openpyxl.Workbook()
ws=wb.active
ws.title='tournament'
#ヘッダー部を2行ごとに結合
#前6列にID, 選手姓, 選手名, '(', チーム名, ')'
n_header=6
i=1
while i<=position.shape[0]:
    #i行目の処理
   
    #i行目j列の結合と設定
    j=1
    while j<=n_header:
        ws.merge_cells(start_row=2*i-1, start_column=j, end_row=2*i, end_column=j)
        ws.cell(row=2*i-1, column=j).alignment=Alignment(
            wrap_text=False,
            horizontal='center',
            vertical='center',
        )
        j+=1
    #ここで列幅の自動調整をさせたいができていない
    #while j<=n_header:
    #    ws.col_dimensions[j].alignment=Alignment(
    #        ShrinkToFit=True
    #    )
    #    j+=1
   
    #ヘッダー中身書き込み
    ws.cell(row=2*i-1, column=1, value=i)
    ws.cell(row=2*i-1, column=4, value='(')
    ws.cell(row=2*i-1, column=6, value=')')
   
    if position[i-1]==0:
        ws.cell(row=2*i-1, column=2, value='bye')
    else:
        ws.cell(row=2*i-1, column=2, value=rank['選手姓'][position[i-1]])
        ws.cell(row=2*i-1, column=3, value=rank['選手名'][position[i-1]])
        ws.cell(row=2*i-1, column=5, value=rank['チーム名'][position[i-1]])
    i+=1
#1, 2回戦分
n_round1=1
n_round2=1
list_index=[]
n_seed=pow(2, p2)
i=1
while i<position.shape[0]:
    temp=position[i-1:i+2]
    if np.min(temp)==0:
        temp[np.argmin(temp)]=np.max(temp)
       
    if np.min(temp)<=n_seed:
        #連続する3人の中にシードが含まれる場合
        if np.argmin(temp)==0:
            #若番側がシードの場合
            ws.cell(row=2*i, column=n_header+1).border=Border(
                outline=True,
                top=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*i, column=n_header+2).border=Border(
                outline=True,
                top=Side(style='medium', color='FF000000'),
                right=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*(i+1), column=n_header+1).border=Border(
                outline=True,
                top=Side(style='medium', color='FF000000'),
                right=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*(i+1)+1, column=n_header+1).border=Border(
                outline=True,
                bottom=Side(style='medium', color='FF000000'),
                right=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*(i+1), column=n_header+2).border=Border(
                outline=True,
                bottom=Side(style='medium', color='FF000000'),
                right=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*(i+1)-1, column=n_header+2).border=Border(
                outline=True,
                right=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*i, column=n_header+3).border=Border(
                outline=True,
                bottom=Side(style='medium', color='FF000000'),
            )
            ws.cell(row=2*i, column=n_header+2, value='2-'+str(n_round2))
            ws.cell(row=2*(i+1), column=n_header+1, value='1-'+str(n_round1))
            ws.cell(row=2*i, column=n_header+2).alignment=Alignment(
                wrap_text=False,
                horizontal='right',
                vertical='center',
            )
            ws.cell(row=2*(i+1), column=n_header+1).alignment=Alignment(
                wrap_text=False,
                horizontal='right',
                vertical='center',
            )
            n_round1+=1
            n_round2+=1
            list_index.append(2*i)
        else:
            #若番側は1回戦スタートの場合
            ws.cell(row=2*i, column=n_header+1).border=Border(
                outline=True,
                top=Side(style='medium', color='FF000000'),
                right=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*i+1, column=n_header+1).border=Border(
                outline=True,
                bottom=Side(style='medium', color='FF000000'),
                right=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*i+1, column=n_header+2).border=Border(
                outline=True,
                top=Side(style='medium', color='FF000000'),
                right=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*(i+1), column=n_header+2).border=Border(
                outline=True,
                right=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*i+3, column=n_header+1).border=Border(
                outline=True,
                bottom=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*i+3, column=n_header+2).border=Border(
                outline=True,
                bottom=Side(style='medium', color='FF000000'),
                right=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*(i+1), column=n_header+3).border=Border(
                outline=True,
                bottom=Side(style='medium', color='FF000000')
            )
            ws.cell(row=2*i, column=n_header+1, value='1-'+str(n_round1))
            ws.cell(row=2*(i+1), column=n_header+2, value='2-'+str(n_round2))
            ws.cell(row=2*i, column=n_header+1).alignment=Alignment(
                wrap_text=False,
                horizontal='right',
                vertical='center',
            )
            ws.cell(row=2*(i+1), column=n_header+2).alignment=Alignment(
                wrap_text=False,
                horizontal='right',
                vertical='center',
            )
            n_round1+=1
            n_round2+=1
            list_index.append(2*(i+1))
        i+=3
    else:
        #連続する3人にシードがいない場合
        ws.cell(row=2*i, column=n_header+1).border=Border(
            outline=True,
            top=Side(style='medium', color='FF000000')
        )
        ws.cell(row=2*i+1, column=n_header+1).border=Border(
            outline=True,
            bottom=Side(style='medium', color='FF000000')
        )
        ws.cell(row=2*i, column=n_header+2).border=Border(
            outline=True,
            top=Side(style='medium', color='FF000000'),
            right=Side(style='medium', color='FF000000')
        )
        ws.cell(row=2*i+1, column=n_header+2).border=Border(
            outline=True,
            bottom=Side(style='medium', color='FF000000'),
            right=Side(style='medium', color='FF000000')
        )
        ws.cell(row=2*i, column=n_header+3).border=Border(
            outline=True,
            bottom=Side(style='medium', color='FF000000')
        )
        ws.cell(row=2*i, column=n_header+2, value='2-'+str(n_round2))
        ws.cell(row=2*i, column=n_header+2).alignment=Alignment(
            wrap_text=False,
            horizontal='right',
            vertical='center',
        )
        n_round2+=1
        list_index.append(2*i)
        i+=2
#3回戦以降
r=3 #階層数
while len(list_index)>1:
    temp=[] #次のlist_index
    n_roundr=1
    i=0
    while i<len(list_index):
        x=list_index[i]+1
        ws.cell(row=x, column=n_header+r).border=Border(
            outline=True,
            top=Side('medium', color='FF000000'),
            right=Side(style='medium', color='FF000000')
        )
        x+=1
        while x<list_index[i+1]:
            ws.cell(row=x, column=n_header+r).border=Border(
                outline=True,
                right=Side(style='medium', color='FF000000')
            )
            x+=1
        ws.cell(row=x, column=n_header+r).border=Border(
            outline=True,
            bottom=Side('medium', color='FF000000'),
            right=Side(style='medium', color='FF000000')
        )
       
        ws.cell(row=(list_index[i]+list_index[i+1])/2, column=n_header+r, value=str(r)+'-'+str(n_roundr))
        ws.cell(row=(list_index[i]+list_index[i+1])/2, column=n_header+r).alignment=Alignment(
            horizontal='right',
            vertical='center'
        )
        n_roundr+=1
        temp.append((list_index[i]+list_index[i+1])/2)
        i+=2
       
    list_index=temp
    r+=1
ws.cell(row=list_index[0], column=n_header+r).border=Border(
    outline=True,
    bottom=Side('medium', color='FF000000')
)
wb.save('tournament.xlsx')

--------------------------------------------------------

 これで
  python3 list2tournament.py sample.xlsx
tournament.xlsxが出力される。(←これスマホからみたらとんでもないことになってて笑った)
 シート分けやら左右に分けたりする場合、その時々に合わせてシートを何枚にするかなどを事前に決めてからであれば可能そう。

2018年8月15日水曜日

トーナメントの組み合わせ

 必要になったときにサッと用意できるように、もとい暇つぶしとして、トーナメントの組み合わせを作成するプログラムを考えてみる。
 (作成したスクリプトなどを置く場所を作った。https://github.com/nkknj/magimenalmirai)
 まず参加者名簿のようなものをエクセルで作成した。(sample.xlsx)

  チーム名:チーム名
  チームランク:全参加チームの中でのチームとしての順位、団体戦の成績とか
  選手名:選手名
  チーム内ランク:その選手のチームの中での順位
  前年度ランク:前年度のランク保持者はその成績を記入

 まずこの参加者名簿から全選手の順位付けを行う。方針として、
(1)前年度ランク保持者はそれに基づいて昇順で先頭に並べる
(2)チームごとの参加者人数で調整したチーム内ランクで昇順に並べる
(3)調整チーム内ランクが同率の場合、チームランクで昇順に並べる
のようにする。

-----------------------------------------------------------------------

import sys
import numpy as np
import pandas as pd

filename=sys.argv[1]
df=pd.read_excel(filename)

#ランク保持者のみ抽出
df=df.sort_values(by=["前年度ランク"], ascending=True)
rank=df[df["前年度ランク"]>=0]

#ランク非保持者のみ抽出
temp=df[~(df["前年度ランク"]>=0)]
temp=temp.reset_index(drop=True)

#ランク非保持者の調整チーム内ランクを計算
K=np.zeros(temp.shape[0])
for i in range(K.shape[0]):
    K[i]=np.sum(df['チーム名']==temp['チーム名'][i])

temp['チーム内ランク']=temp['チーム内ランク']/K

#ソート
temp=temp.sort_values(by=["チーム内ランク", "チームランク"], ascending=True)

#ランク保持、非保持を結合
rank=pd.concat([rank, temp])

rank=rank.reset_index(drop=True)
rank.index+=1
rank.to_excel('rank_'+filename)

-----------------------------------------------------------------------

 これで参加者名簿から全体の順位付けが完了。(rank_sample.xlsx)
 次にトーナメントの位置決め。紙に書いていくように「端、端、内、内」と埋めていくのを実装するのは非常に面倒臭そうだったので、逆に数字を挿入して広げていく方式をとることにした。具体的には、[1, 4, 3, 2]となっている数列を最初に作った後、先頭から奇数コ目の後ろにゼロを2個ずつ挿入し、のちに2n, 2n+1番目の和が2^k+1になるような数をゼロに代入していく。2^kからの端数は、その端数を超える最小の2^kを求めて、不要な要素の削除、およびbyeとして0を代入する。

-----------------------------------------------------------------------
N=rank.shape[0]
position=np.array([1, 4, 3, 2])
#まずは参加者数を超える最小の2^kの数までの並び順
p1=2
while N>pow(2, p1):
    i=0
    while i<pow(2, p1-1):
        position=np.r_[position[:4*i+1], np.zeros(2), position[4*i+1:]]
        i+=1
       
    i=0
    while i<pow(2, p1):
        if position[2*i]==0:
            position[2*i]=pow(2, p1+1)+1-position[2*i+1]
        else:
            position[2*i+1]=pow(2, p1+1)+1-position[2*i]
        i+=1   
    p1+=1
   
p2=1
while N-pow(2, p1-1)>pow(2, p2):
    p2+=1
   
position=position[position<=pow(2, p1-1)+pow(2, p2)]
position[position>N]=0
print(position)
print(p2)

-----------------------------------------------------------------------

[ 1. 65. 64. 33. 32. 17. 48. 49.  0. 16.  9. 73. 56. 41. 24. 25. 40. 57.
72.  8.  5. 69. 60. 37. 28. 21. 44. 53. 76. 12. 13. 77. 52. 45. 20. 29.
36. 61. 68.  4.  3. 67. 62. 35. 30. 19. 46. 51.  0. 14. 11. 75. 54. 43.
22. 27. 38. 59. 70.  6.  7. 71. 58. 39. 26. 23. 42. 55. 74. 10. 15.  0.
50. 47. 18. 31. 34. 63. 66.  2.]

4

 最後に表示させているp2が4なので、2^4=16番までの下には1回戦があり、その他は全て2回戦スタート。
 この数列と、上で求めた全体の順位を対応させればいいのだが、ではここからトーナメント表を描くとなるとこれはpythonでやるのはとても大変そうである。ググったところturtleというもので再帰的にトーナメントを描くことができるようだが、1回戦の有無などを反映させるのは厳しそう。他にpythonでエクセルの枠線扱えた気がするけど、これも実装やばそうなので却下。つまり表に落とし込むにはエクセルポチポチかね。

 ところで、経験的に第1シード下の1回戦がbyeになっている印象なのだが、上の方法だとN-pow(2, p1-1)~pow(2, p2)番目の横の1回戦のところにbyeが来るんよね。試しに全国公のトーナメント表を眺めてみたら、第1シード下の中シード横の1回戦にbyeがあるので、これは上記と同じ方法をとっていそうである。なのでまぁ、この辺はローカルルールなんかね。