Skip to content

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

時間割自動編成

日本の小学校・中学校・高等学校の週時間割を整数計画法で自動提案し、 人が画面上でドラッグ&ドロップで微調整して仕上げるためのWebアプリケーション。

  • 学習指導要領の標準授業時数から、教科・週コマ数・教員・特別教室を自動生成する
  • 制約を満たす時間割を数秒〜数十秒で提案する
  • 提案結果を手で直せる。動かした瞬間に制約違反が赤/黄で表示される
  • 気に入ったコマを**固定(ロック)**して、残りだけを組み直せる

詳しい背景と根拠は 要件定義、実装時に置いた判断は 仮定記録 にまとめてある。


構成

backend/     FastAPI + SQLAlchemy + PuLP(整数計画ソルバー)
frontend/    Vite + React + TypeScript + @dnd-kit
e2e/         Playwright(提案 → 編集 → 再提案 の一連を通す)
docs/        要件定義(requirements.md)と仮定記録(assumptions.md)
benchmarks/  実態規模での求解時間の計測スクリプト
archive/     2019年の強化学習による実験(現在は使用していない)

動かす

前提: Python 3.11 以上、Node.js 20 以上。

# バックエンド
cd backend
pip install -r requirements.txt
python -m uvicorn app.main:app --reload --port 8000

# フロントエンド(別ターミナル)
cd frontend
npm install
npm run dev          # http://127.0.0.1:5173

ブラウザで http://127.0.0.1:5173 を開き、校種と1学年あたりの学級数を選んで 「作成する」→「自動提案」を押す。

テスト

cd backend && python -m pytest          # ユニット/統合 50件
cd frontend && npm run typecheck        # 型チェック
cd e2e && npm install && npx playwright test   # E2E(バックエンドとフロントを自動起動)

設計上の要点

1. 週30枠に29〜30コマを詰める問題である

学習指導要領の標準授業時数を授業週数で割ると、必要な週コマ数はこうなる。

学年 週コマ数 週の枠数(5日×6コマ)
小1 25 30
小4〜6・中1〜3 29 30
高(全日制標準) 30 30

空き枠は週0〜1コマしかない。ほぼ完全充填問題であり、少しでも条件を足すと ハード制約のままでは実行不可能になる。そのため次の設計を採っている。

2. 制約はすべてソフト制約として扱い、「解なし」を返さない

ハード制約(H1〜H6)はすべて「1つも配置しない解」で充足できる形にしてある。 したがってモデルは常に実行可能で、Infeasible は返らない。条件が厳しすぎる場合は 「何コマ足りないか」が S1(週コマ数の未充足)として必ず数値で返る。

利用者の画面に「解が見つかりませんでした」とだけ出る状態を作らないための設計。

3. 制約の定義は1か所にしかない

backend/app/solver/constraints.py の各制約クラスが2つの顔を持つ。

class TeacherOccupancy(Constraint):
    def apply(self, spec, ctx):   # 整数計画モデルに制約を追加する(自動提案)
    def check(self, spec, places): # 既存の割当を検査して違反を返す(手編集の検証)

ソルバーと検証器を別々に書くと「画面上はOKなのに再提案すると弾かれる」というズレが 必ず起きる。同じクラスに両方を持たせることで、構造的にそれを防いでいる。

4. 割当の単位は「学級×教科」ではなく「講座」

高校の選択科目では、1つの学級の生徒が同じ時間帯に物理・化学・生物へ分かれる。 つまり学級と受講集団は一致しない。そこで時間割のセルに入るのは Course(講座)とし、 講座は「担当教員1名・受講学級1つ以上・週コマ数」を持つ。小中は「1学級=1講座」に縮退する。

選択科目群(ParallelGroup)は学級から見れば1コマなので、学級の重複チェック(H1)は 群を1単位として数える。

5. 求解は非同期、検証は同期

実測すると、小・中学校は数秒だが高校は状況により20秒を超える。HTTPリクエストの中で 待たせられないため、求解はジョブID+ポーリングにしている。一方、手編集の検証は ミリ秒で終わるので同期で返す。


実測値

benchmarks/solver_benchmark.py と、アプリが生成する実データでの計測。

インスタンス 講座数 求解時間 未配置 ハード違反
小学校 19学級(3学級/学年+特支) 194 5.3秒 0 0
中学校 13学級(4学級/学年+特支) 156 4.2秒 0 0
高等学校 15学級(5学級/学年) 205 19.8秒 0 0

解の品質は15秒で頭打ちになり、それ以降の時間はすべて最適性の証明に費やされる。 そのため相対ギャップ2%・時間制限30秒で打ち切っている。

実装済みの制約

ハード制約(違反しない)

コード 内容
H1 1つの学級の同一コマに2つ以上の講座を置かない(選択科目群は1コマと数える)
H2 1人の教員が同一コマに2つ以上の講座を担当しない
H3 特別教室の同時使用数が保有数を超えない
H4 配置禁止枠には置かない
H5 教員の不在枠には置かない
H6 固定された割当は動かさない

ソフト制約(重み付きで最小化。違反は画面に黄色で表示される)

コード 内容 重み
S1 講座の週コマ数を満たす 1000
S2 同じ講座を1日に置ける回数の上限(週内分散も兼ねる) 100
S3 教員の週持ちコマ数の上限(既定18コマ) 50
S4 選択科目群を同一コマに揃える 500
S8 実験・実習・体育を2コマ連続で配置する 20
S9 主要教科を午前(1〜4限)に寄せる 5

特別支援学級の交流及び共同学習(要件書の S5)は、制約ではなく 「通常学級の講座に特別支援学級を追加する」というデータ構造で保証している。 教員の空きコマ分散(S7)と担任の自クラス滞在(S10)は未実装。

前提と仮定

「細かく確認せず仮定を置いて進める」方針で実装したため、判断が必要だった箇所は すべて docs/assumptions.md に A-01〜A-31 として記録してある。 各仮定には根拠と「見直し条件」を付けているので、実運用に載せる際はそこから確認できる。

実測の結果として当初の設計から変えた点(A-24〜A-29)も、変えた理由とともに残してある。

経緯

このリポジトリは2019年に強化学習(DQN)で時間割を解く実験として始まったが、 実問題は解けないまま止まっていた。同時期に試された線形計画法のノートブックだけが 最適解に到達していたため、そちらを土台としてWebアプリケーションに作り直した。 当時のコードは archive/ に残してある。

About

強化学習による時間割最適化の実験

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages