終わらない沈黙と、紙の上で崩れる公平性
日曜の午前9時。テニスコート脇のベンチには8人のメンバーが揃い、ラケットを握ったまま運営担当の手元を見つめている。バインダーの紙には修正液の白い跡が幾重にも重なり、鉛筆の線が何度も引き直されている。1つ組み合わせを書き終えるたびに、「あ、さっきも同じペアだった」「この人、2回連続で休みになるな」という矛盾に気づき、また手が止まる。
静まり返ったコートに、遠くのセミの声だけが響く。沈黙の中でこめかみを伝う汗が紙に落ち、余白に小さなシミを作る。前のラウンドの記憶、実力差、休憩回数の偏りが濁流のように頭の中を巡り、どれを優先すべきか判断が揺らぐ。早く試合を始めたい焦燥と、公平性を守りたい責任感が胸の奥で衝突し、胃のあたりがじわりと重くなる。15分経っても表は完成しない。この停滞が毎週続くと思うと、吐き気に似た負荷が積み重なっていった。
この記事では、この「現場の沈黙」を断ち切るために、全探索という甘い罠を捨て、ペナルティスコア方式という現実解にたどり着いた設計判断の全容を語る。
既存ツールの限界と欠点
欠点1: 単純なランダム生成では「現場の公平」に届かない
既存の組み合わせツールは、数字をランダムにシャッフルするだけのものが多い。しかしテニスの現場で求められる公平は、数学的なランダムとは別物だ。
- 特定ペアの再登場
- 休憩の連続
- 出場回数の格差
8人で2面、3ラウンドを作るだけでも、これらをすべて満たす表を偶然引き当てる確率は極めて低い。結局は人間が目視で確認し、不備を見つけては再生成するという無限ループに陥る。
欠点2: 全探索(厳密解)による計算量の爆発
開発初期に検討したのは「すべての組み合わせを列挙して最良を選ぶ」方式だった。しかし、これはすぐに数学的な壁に突き当たった。
8人でダブルスを組む1ラウンドの組み合わせ数は、条件を絞っても最低70通り。 3ラウンド作れば 70^3 = 343,000 通り。
人数が増えれば指数関数的に膨れ上がる。 組合せ爆発の概念はWikipediaが詳しい。
ブラウザのメインスレッドでこれを実行すれば、画面は固まり、スマホは熱を帯びる。現場で数十秒待つのは現実的でない。
欠点3: 実力差というノイズを扱えない
上級者(A)と初級者(B)が混在する練習会では、完全ランダムは破綻する。
- A同士の強打にBが触れられない
- B同士のラリーが続かない
- シングルス希望者が偏る
既存ツールには「Aグループ内での対戦」「Bグループ内での対戦」を切り分ける機能がほぼ存在しなかった。
試行錯誤:設計判断の裏側
失敗案: 再帰アルゴリズムによる最適解の追求
最初に実装したのは、再帰関数を用いた深さ優先探索だった。 しかし人数が増えると処理時間は秒単位から分単位へ、そしてブラウザの「応答なし」へと変わった。
JavaScriptの実行モデルはMDNが詳しいが、シングルスレッドで動作するブラウザ環境では、完璧を求める姿勢そのものが最大のボトルネックだった。
トレードオフ: 厳密解の放棄と近似解の採用
| 評価軸 | 全探索(厳密解) | ペナルティスコア方式(近似解) | |-------|------------------|--------------------------------| | 計算速度 | 人数増加で指数関数的に悪化 | 常に一定(高速) | | 公平性 | 数学的に完璧 | 上位1%の「十分に良い」案 | | UX | 待ち時間が発生 | ボタン押下と同時に完了 | | 拡張性 | 条件追加が困難 | 重み調整で柔軟に対応 |
現場の即時性を優先し、厳密解を捨てるという判断に踏み切った。
最終判断: 100回の試行から最良を射抜く
採用したのは「ペナルティスコア・ランキング方式」だ。
- ランダムに100通りの案を生成
- 各案に以下のペナルティを加算
- 同じペア再登場 +10
- 同じ対戦相手 +5
- 休憩連続 +50
- 出場回数の偏り +20
- 最もスコアが低い案を採用
web.dev のパフォーマンス指標にも合致し、現場で求められる「即時性」と「納得感」を両立できた。
解決策:テニス組み合わせ職人の紹介
こうした試行錯誤の末に完成したのがテニス組み合わせ職人。
使い方は3ステップに絞った。
- 人数とコート数を入力
- A/Bグループを設定
- 「生成」を押す
裏側で100通りの未来を比較していることを感じさせない軽快さにこだわった。 この形に落ち着いた理由は、運営担当を「パズルを解く苦行」から解放するためだ。
よくある質問(FAQ)
Q: AグループとBグループを分けた時の計算ロジックは?
グループ設定が有効な場合は、基本的にA内・B内でペアリングを試行する。端数が出る場合はペナルティを調整し、実力差が極端にならない範囲で混ぜるようにしている。
Q: 特定のメンバーを「今日だけ休み多め」にできる?
現在のロジックは「全員一律の公平」を優先している。個別調整が必要な場合は、生成後に名前を入れ替える運用が最も柔軟だ。
Q: データはサーバーに送信される?
すべてブラウザ内で処理される。サーバーに送信されることはなく、ブラウザを閉じれば入力内容は消える。
Q: 100回の試行で偏りを避けられる?
100通りの案から最良を選ぶだけで、人間が15分かけて考えるより偏りは大幅に減る。実用上の公平性は十分に確保できる。
まとめ
手書きでは解決できなかった公平性の問題を、全探索の限界を認めつつ、ペナルティスコア方式で現実的に解決した。 公平な対戦表を素早く作りたいときはテニス組み合わせ職人を試してみて。
ランダム性が必要な場面では公平な配置係も便利だ。
不具合や要望があれば、お問い合わせページから気軽に教えてほしい。