文教大学時間割編成の作成支援 情報学部経営情報学科 小 河 智 哉 概要 本論文では、大学の時間割編成の最適解を提案する。Java を用いて、制約式を書いた LP ファイルを出力し、cplex により最適解を求めた。実用的な計算時間で、春・秋学期と もに、すべての制約を満たし、解くことができた。 1 はじめに 大学の時間割編成は、一般に教職員の手作業によって行われ、膨大な時間を要すること が多い。時間をかけて編成した時間割でも、さまざまな問題点を有することが多く、教員 や学生から不満の声が上がることもある。実際に、筆者が大学の時間割の履修が組みにく い現状に不満を感じているため、より改善した時間割を作成できないかと思い、研究して みようと思った。 時間割編成問題の上記現状を改善するために、近年では時間割編成に関する研究が行わ れている。久保田[1]は、タブー探索法を用いた大学時間割編成モデル、計算量を減少させ るような科目モデルの提案を行った。また、遺伝アルゴリズムを用いた時間割作成システ ムと提案モデルにおいて実装を行い、その結果の比較を行ったところ、同程度の解の質で、 計算時間において優位であったことを示している。 また、太田ら[2,3]は、小中高等学校の時間割編成問題を 0-1 混合整数計画問題として定 式化し、最適化ソフトウェアを用いて解くことにより、実用的な時間で時間割編成が可能 であることを示している。このように多くの時間割編成についての研究が行われているが、 まだ各大学に適応できるような汎用性の高いものはあまりない。 本研究では、文教大学情報学部・経営学部の時間割編成問題を定式化し、最適化ソフト ウェアで解を求める。大学全体での時間割作成は大規模になり、解を求めることが難しく なるので、はじめに、情報学部経営情報学科・経営学科のみの時間割を対象とする。将来 的に大学全体や他大学の時間割編成にも利用できることを目的とする。 本論文の構成は以下の通りである。2 節では現在の時間割の状態と問題点を明示し、3 節では本問題を解くための変数を定義し定式化を行い、4 節で計算概要・結果を示す。さい ごに 5 節でまとめと今度の課題について述べる。 計算に使用するデータは,次年度(2015 年度)に情報学部経営情報学科,および経営学 部経営学科で開講が予定されている語学を含む全科目と,共通教育科目全てを用いる。 -1- 2 時間割編成の現状 本節では、時間割編成を行う対象とする文教大学情報学部・経営学部における時間割編 成の現状と問題点について述べる。 文教大学は湘南キャンパスと越谷キャンパスに分かれており、キャンパスごとに時間割 編成を行っている。湘南キャンパスには、情報学部・経営学部・国際学部・健康栄養学部 がある。時間割編成の際には、はじめに共通教育科目の開講曜日時限を決定した後、各学 部において学部・学科科目の時間割編成を行う。 時間割編成を難しくする要因の一つは、頻繁に時間割の調整が必要になるからである。 すでに開講曜日時限が決定した共通教育科目等でも、担当教員の都合などで再調整が必要 となることがある。1 つの科目の開講曜日時限が変更になることで、ほかの多くの科目が影 響を受けることは多々あり、変更や調整があるたびに、時間割編成担当者は試行錯誤を繰 り返しながら、時間割の調整・確認作業に膨大な時間を割いている。 時間割編成を難しくするもう 1 つの要因は、異なるカリキュラムを履修する学生が混在 することである。カリキュラムは概ね 4~5 年に一度の頻度で改正されており、常に 2 つ以 上の異なるカリキュラムを考慮して時間割編成を行う必要がある。 文教大学の時間割を組む上で注意しなければいけない点を下記に示す。 ・時間割を組む前に科目と教員が一対一対応される ・情報学部・経営学部教員は必ず水曜日が出校になる(会議日程確保のため) ・情報・経営学部にオムニバス授業は無い1 ・キャンパスが小さいため、教室割当を科目割当と同時に考慮しないと時間割が組めない ・同理由により、同一曜日同一時限に配置できる PC 演習・CALL・特殊教室(5201 など) 授業が限られる。 3 3.1 定式化 変数の定義 時間割編成問題を定式化するにあたり、以下の記号を定義する。 科目リスト : i ∈ I = {1,2,…} …科目集合 I 教員リスト : p ∈ P = {1,2…} …教員集合 P P1:常勤,P2:非常勤,(P=P1 ∪ P2,P1 ∩ P2 =∅) 曜日 : k ∈ K = {1,2,3,4,5} …曜日集合(1=月曜日,…,5=金曜日) 時限 : h ∈ H = {1,2,3,4,5} …時限集合(1=1 時限目,…,5=5 時限目) 決定変数は以下のように定める。 1 実際にはオムニバス授業は存在するが、その科目の担当教員数とクラス数を同じにし、各クラ スと教員を一対一に対応させ、同一曜日時限に配置することで、オムニバスを実現する -2- 1 0 科目 i を k 曜日 h 時限に配置 . . 1 教員 p は k 曜日に出校 0 . . 3.2 制約・制約式 各制約を 3 つのタイプに位置づける。殆どの大学で共通事項としてあげられる、必ず守 らなければ時間割が組めない制約をハード制約、できるだけ守りたい制約をソフト制約、 文教大学独自の制約を文教制約とする。 A.ハード制約(絶対に守らなければ時間割が組めない制約) B.ソフト制約(可能であれば守りたい制約) C.文教制約(文教大学独自の制約) 以下、各タイプ別の制約。 A.ハード制約 A.1:教員制約 教員 p(担当科目集合 )は同一曜日時限には高々1 教科のみ担当可能 1 ∀ , , ∈ A.2:科目制約 週 1 科目 I1 は、k 曜日 h 時限(5×5=25)に丁度 1 つ割当 1 ∀ ∈ , =週1科目の集合 A.3:科目制約 週 2 科目 I2(2 時限連続科目 I3)は、k 曜日 h 時限(5×5=25)に丁度 2 つ割当 2 ∀ ∈ ∪ , =週 2 科目の集合、 =2 時限連続科目の集合 A.4:科目制約 同一学科同一学年の必修科目(必修科目集合 )は(同一科目別クラス を除き)、別の曜日時限に配置。ここでいう必修科目とは「専門必修科 目」と「共通教育・教養の必修科目」を指す。 1 ∀ , ∈ A.5:科目制約 同一学科同一学年のその他科目(その他科目集合 )は、上記必修科目 とは別の曜日時限に配置。ここでその他科目とは、「専門選択科目、専 門選択必修科目、語学、共通教育・教養の選択必修・選択科目」を指す。 -3- 例えば、必修科目がαの 1 科目,その他科目をβ,γの 2 科目とすると、 1 ∀ , 1 ∀ , また、一部(最初から曜日時限が決まっている科目)は制約式では制御せず、 データを用いて、必修科目と同一曜日同一時限に配置されないように制御した B.ソフト制約 B.1:時間割均等化 各曜日にまんべんなく科目を配置する ∀p, k, h ∈ =目的関数で制御する変数 制御方法は「3.4 目的関数」で後述 B.2:科目制約 同一学科同一学年のその他科目(その他科目集合 Io)は(同一科目別ク ラスを除き) 、なるべくばらけさせる ∑∈ ∀k, h =目的関数で制御する変数 制御方法は「3.4 目的関数」で後述 B.3:科目制約 なるべく同一曜日同一時限に配置したい科目。 例えば、科目α、βの 2 つの科目を同一曜日同一時限に配置した場合、 このように書く ∀ , s =目的関数で制御する変数 制御方法は「3.4 目的関数」で後述 C.文教制約 C.1:教員出校日 教員 p∈P は k 曜日に科目割当されたら出校日となる(出校日なら 1 ~5 限講義可能) 5 ∀ ∈ , C.2:教員出校日 常勤教員 p∈ の出校日は高々3 日(ただし水曜日はなるべく出校) 1 ∀ ∈ 2 ∀ ∈ , , , C.3:科目制約 週 2 科目 I2 は、別曜日同時限に配置 -4- 0 ∀i ∈ , h 1 ∀ ∈ , , , , 1 ∀ ∈ C.4:科目制約 2 時限連続科目 I3 は、同曜日、連続時限に配置(1-2 or 3-4 時限開講の組 み合わせのみ考える) 1 ∀ ∈ C.5:非常勤出校日 1 ∀ ∈ 0 ∀ ∈ 0 ∀ ∈ 非常勤教員はなるべく出校数を減らす(出校曜日に担当科目をまと めて配置する) ∀ ∈ =目的関数で制御する変数 制御方法は「3.4 目的関数」で後述 C.6:教室制約 PC 教室・CALL 教室・特殊教室(5201)は、その教室を使用する科目 を同一曜日同一時限で重ねて配置できる科目の上限を設ける。 例えば、PC 教室を使う科目数が 3(科目番号 1,2,3)、PC 教室数が 2 だ とした場合、下記のように記述する C.7:科目制約 2 ∀k, h なるべく 5 時限目に科目を配置しない ∀ ∈ z =目的関数で制御する変数 制御方法は「3.4 目的関数」で後述 3.3 目的関数 よりよい時間割当を考える。ソフト制約で記述したものを最小化する。 目的関数は以下の通りである。 3.3.1:教員(常勤・非常勤)の希望出校曜日を考慮。常勤教員は、出校したい曜日の係 数を 1、それ以外を 5 として計算する。非常勤教員は、出校できる曜日が決まっ ているので、出校できない曜日の係数を 999 とする min5 5 …常勤教員 p は、出校日として「火・水・木」を希望 -5- min999 999 999 999 …非常勤教員 q は、木曜日のみ出校可能 3.3.2:非常勤教員の出校日をなるべく少なくする(出校日に多く科目を配置) ∑ min ∈ 3.3.3:各常勤教員の担当科目が、1 つの曜日に偏らないようにする ∑ min 3.3.4:なるべく同一曜日同一時限に配置したい min s 3.3.5:同一学科同一学年(同セメスター)の必修科目以外はなるべく別曜日別時限に配置 ∑ min 3.3.6:5 時限目にはなるべく配置しない min z すべてをまとめて書くと、 min ∑ ∑ , 4 ∑ ∈ ∑ 時間割編成の最適解を求める概要 時間割編成の最適解は、Java を用いて LP ファイルを出力し、cplex を用いて解き、最 適解を求めた。cplex で出力される結果は 0-1 変数の最適解であり、このままでは見た目上 では判断できないため、出力された結果を Excel で処理し、JavaScript を用いてブラウザ 上に表示する。 4.1 データ 科目数 2015 年度春学期 235 科目(内訳は以下) 教養科目数:57 必修科目数:52 選択必修科目数:80 選択科目数:46 2015 年度秋学期 247 科目(内訳は以下) 教養科目数:51 必修科目数:60 選択必修科目数:77 選択科目数:59 教室数 93 (内訳は以下) 一般教室数:50 PC 教室数:8 PC-MM 教室数:2 -6- PC-CALL 教室数:4 特殊教室:29 特殊教室とは、他に代替がなくこの教室でのみ実施できる科目 4.2 計算結果 2015 年度情報学部の春・秋学期の科目・教員データを使用し、最適解を求めた。使用し た計算機は、 ノートパソコンの富士通 LIFEBOOK P770/B、CPU は intel Core i3(1.3GHz)、 メモリは 4GB。cplex のバージョンは 12.5.0.0 である。 春学期の問題に含まれる変数の数は 6,310、制約式の数は 7,200、計算時間は 3.32 秒で あった。また、秋学期の問題に含まれる変数の数は 6,690、制約式の数は 7,800、計算時間 5.65 秒であった。 春学期、秋学期の目的関数値は、それぞれ以下の表 1,2 に示した通りである。ただし、 表内では変数y ∑ c y である。3.3.1 で述べたとおり、常勤教員 p の変数 y ~y に 対する係数c ~c は 1 か 5 なので、例えば表 1 内の y の値が 1 であることから、教員 3 は 希望する出校日 1 日のみ出校となる解が最適解として得られていることを意味する。 -7- 表 1 2015 年度春学期:目的関数に含まれる各変数の値 変数 値 変数 値 変数 値 変数 値 変数 値 変数 値 変数 値 変数 値 y3 1 y95 3 y306 1 w201 1 w312 1 u3 1 u76 1 t1 6 y11 1 y96 3 y307 1 w202 1 w313 1 u11 1 u84 1 t3 5 y14 2 y97 2 y308 1 w203 1 w314 1 u14 1 u88 2 t5 2 y20 2 y98 3 y309 1 w204 1 w315 1 u20 2 u89 2 s 1 y21 2 y99 3 y310 1 w205 1 w316 1 u21 3 u90 3 z 7 y26 1 y100 3 y311 1 w206 1 w317 1 u26 1 u91 2 y32 2 y101 3 y312 1 w207 1 w318 1 u32 3 u92 2 y35 2 y102 3 y313 1 w208 1 w319 1 u35 3 u93 2 y39 1 y103 2 y314 1 w209 1 w320 1 u39 1 u94 2 y40 2 y104 3 y315 1 w210 1 w501 2 u40 1 u95 3 y46 1 y201 1 y316 1 w211 1 w502 1 u46 1 u96 2 y50 2 y202 1 y317 1 w212 1 w503 1 u50 1 u97 3 y53 1 y203 1 y318 1 w213 1 w504 2 u53 1 u98 2 y58 2 y204 5 y319 1 w214 1 w505 2 u58 3 u99 2 y61 1 y205 5 y320 5 w215 1 w506 2 u61 1 u100 2 y65 1 y206 1 y501 2 w302 1 w507 1 u65 1 u101 2 y68 1 y207 1 y502 1 w303 1 w508 1 u68 1 u102 2 y69 1 y208 1 y503 5 w304 1 w511 2 u69 1 u103 2 y71 1 y209 1 y504 2 w305 1 w512 1 u71 2 u104 2 y75 1 y210 1 y505 2 w306 1 w513 1 u75 1 y76 1 y211 1 y506 2 w307 1 w514 1 y84 1 y212 1 y507 1 w308 1 w515 1 y88 2 y213 1 y508 1 w309 1 w516 1 y89 3 y214 5 y511 2 w310 1 w517 1 y90 3 y215 1 y512 1 w311 1 y91 2 y302 1 y513 1 y92 3 y303 1 y514 1 y93 2 y304 1 y515 1 y94 3 y305 1 y516 1 y517 1 -8- 表 2 2015 年度秋学期:目的関数に含まれる各変数の値 変数 値 変数 値 変数 値 変数 値 変数 値 変数 値 変数 値 変数 値 y6 1 y96 3 y504 2 w201 1 w506 2 u6 1 u71 2 t2 5 y7 1 y97 2 y505 2 w206 1 w507 1 u7 1 u75 1 t4 4 y11 1 y98 3 y506 2 w207 1 w508 1 u11 1 u76 1 t6 2 y13 1 y99 2 y507 1 w208 1 w509 1 u13 1 u77 2 s 1 y14 1 y100 3 y508 1 w210 1 w510 1 u14 1 u78 3 z 9 y20 7 y101 3 y509 1 w211 1 w511 2 u20 2 u79 2 y21 2 y102 3 y510 1 w212 1 w512 1 u21 2 u80 3 y26 1 y103 3 y511 2 w214 1 w513 1 u26 1 u81 3 y32 2 y104 3 y512 1 w302 1 w514 1 u32 3 u82 4 y35 1 y201 1 y513 1 w303 1 w515 1 u35 5 u83 3 y39 1 y206 1 y514 1 w304 1 w516 1 u39 1 u84 2 y40 1 y207 1 y515 1 w305 1 w517 1 u40 1 u85 3 y42 1 y208 1 y516 1 w307 1 w601 1 u42 1 u86 3 y47 1 y210 1 y517 1 w308 1 w602 1 u47 1 u87 2 y50 2 y211 1 y601 1 w309 1 w603 1 u50 1 u88 3 y53 1 y212 1 y602 1 w310 1 w605 1 u53 1 u89 2 y58 1 y214 5 y603 1 w313 1 w606 1 u58 3 u90 3 y61 2 y302 1 y605 1 w315 1 w607 1 u61 1 u91 3 y64 2 y303 1 y606 1 w316 1 w608 1 u64 1 u92 2 y65 1 y304 1 y607 1 w317 1 w609 1 u65 1 u93 2 y68 1 y305 1 y608 1 w318 1 w610 1 u68 1 y71 2 y307 1 y609 1 w502 1 w611 1 y75 1 y308 1 y610 5 w503 1 w612 1 y84 1 y309 1 y611 5 w504 2 w620 1 y88 3 y310 1 y612 5 w505 2 w622 1 y89 3 y313 1 y620 1 w623 1 y90 2 y315 1 y622 5 y91 3 y316 1 y623 5 y92 3 y317 1 y93 3 y318 1 y94 3 y502 1 y95 3 y503 5 -9- 春・秋学期ともに、必ず満たさなければいけない制約は全て満たしている。必修科目は 重ならないように配置されていた。目的関数によって制御していた、なるべく満たしたい 制約は、満たせていないものもあった。細かく目的関数値を見ていくと、まず春・秋学期 の t1-t6 の値がそこまで高くない。最大で 6、つまり同セメスターの科目で同一曜日時限に 配置されている科目が 6 つあるということである。しかしこれは仕方のない数値である。 なぜなら、t1 に含まれる科目数は 75、そして必修科目数は 6、必修科目とその他の科目は 重ならないようになっているため、また、5 時限目にはなるべく科目を配置しないようにし ているため、t1 に含まれる科目が実質配置できる場所は、5×4-6=14(曜日×時限-必修科 目数)となるためである。 春・秋学期ともに s の値は 1 となっている。これは、なるべく同一曜日時限に配置した い科目の中で、同一曜日時限に配置できていない科目があるということである。他にも、 春・秋学期ともに 、 の値も最大で 5 なので、教員の出校希望が叶っていることがわか る。 時間割編成の結果の一例として、図 1.1~図 5.3 に時間割表の結果の一部を示す。科目名 の色が赤色は必修科目、青色が選択必修科目、緑色が選択科目である。 図 1.1 2015 年度 1 年次 春学期 - 10 - 図 1.2 2015 年度 2 年次 春学期 図 1.3 2015 年度 3 年次 春学期 - 11 - 図 1.4 2015 年度 4 年次 春学期 図 2.1 常勤教員の例 図 2.2 非常勤教員の例 - 12 - 図 3 2015 年度 経営学部 春学期 図 4 2015 年度 必修科目 秋学期 - 13 - 図 5.1 2015 年度 特殊教室(PC)秋学期 図 5.2 2015 年度 特殊教室(5201)秋学期 図 5.3 2015 年度 特殊教室(CALL)春学期 図 1.1~1.4 は、2015 年度 春学期の時間割を学年毎に表示したものである。例えば、な るべく 5 時限目に配置しない、というソフト制約が上手くきいていて、5 時限目への科目配 置が相対的に少なくできていることがみてとれる。 図 2.1,2.2 は、それぞれ常勤教員、非常勤教員の担当科目の配置の一例である。常勤教員 では、出校希望曜日の 3 日間が背景白色、希望していない曜日を灰色としており、非常勤 - 14 - 教員では、出校不可能曜日を背景赤色で表示している。図の例では、それぞれ希望が満た されている解であることがわかる。 図 3 は、学部毎表示にしてあり、例として経営学部の全科目配置を示している。必修科 目の配置がそれぞれ重ならずに上手くおこなわれており、選択必修、選択科目についても、 上手くばらけて配置されていることがわかる。 図 4 は、区分毎の表示例で、必修科目のみをあらわしている。同一曜日同一時限に配置 すべき必修科目や、なるべく同一曜日同一時限に配置したい科目が上手く配置できている。 図 5.1~5.3 は、特殊教室の配置が上手くいっているかどうかを表示したものである。PC 教室(最大同時使用教室 6)、5201 室、CALL 教室(最大同時使用教室 4)が、それぞれき ちんと制約を守れていることがわかる。 なお、この解については実際に時間割作成業務に携わっている教育支援課事務職員にご 確認いただき、以下のようなコメントをいただいた。春学期については、1 年次科目には特 に目立った問題はなく、2 年次科目の一部で選択科目がやや重なっているところが気になる。 秋学期については、1 年次選択必修科目が 3 つ重なっている点、2 年次選択科目が 3 つ重な っている点を改善した方が好ましいということであった。しかし、いずれも優先度が高い わけではなく、他学部他学科の科目配置との調整などによるようである。 5 まとめ 2015 年度文教大学情報学部の春学期・秋学期の科目・教員のデータを用いて、本研究で 作成したプログラムで時間割の最適解を求めた結果、実用的な計算時間で最適解を導くこ とができた。制約をすべて満たすことができ、目的関数値も教員の出校日を考慮しつつ、 高くない数値で解を求められた。 ただし、まだ教室の問題を全て取り入れて考えられていないので、実用化のためにはこ のままでは最適解とは言えない。よって、プログラムを組みなおす必要がある。また、今 後は汎用化に向けて、本プログラム作成者だけではなく、時間割編成担当者が簡単に利用 できるシステム作り、インタフェースを開発することが重要である。 データについては、すべての情報を得られているわけではなく、前年度からの憶測など が含まれている。最新のデータを得るためには、事務や教務に携わっている教員と密接に やりとりをする必要がある。また、教員・科目などの変更が、最終決定をするギリギリま であるので、このプログラムを使用するには、きちんとしたデータが必要である。 最終的な目的は、文教大学湘南校舎全学部の時間割の最適解を求めることであったが、 データを得られなかったため、解くことはできなかった。しかし、今後は解けるようにし たい。 - 15 - 謝辞 本研究の実施にあたり、現行時間割・科目データ等、データ提供において文教大学教育 支援課の方に多大なるご支援をいただきました。とくに、教育支援課の小柳氏には、こち らの解を何度か見てもらい、その都度、有益なコメントをいただきました。ありがとうご ざいます。また、文教大学施設課より、教室データの提供等でお世話になりました。ここ に謝意を表します。そして、終始熱心なご指導を頂いた文教大学情報学部堀田敬介准教授 に感謝の意を表します。本当にありがとうございました。 参考文献 [1]久保田敬:大学の時間割自動編成システムの研究,中央大学大学院理工学研究科情報工 学専攻 2003 年度修士論文,2004. [2]太田正和、鈴木敦夫:時間割自動編成システムの研究,日本 OR 学会 2006 年秋季研究発 表会アブストラクト集,2006,pp.58-59 [3]太田正和:時間割自動編成システムの研究,南山大学大学院数理情報研究科修士論文, 2007. - 16 - 付録 本研究で使用した、LP ファイルを出力す るプログラムである。 er2015haru.csv"); BufferedReader br = new BufferedReader(fr); //ファイルの読み込み(非常勤出校ファイル) FileReader fr1 = new import java.io.File; FileReader("/Users/ogawatomoya/Desktop/kamo import java.io.FileOutputStream; ku2015haru.csv"); import java.io.OutputStreamWriter; BufferedReader br1 = new BufferedReader(fr1); import java.io.PrintWriter; //ファイルの読み込み(目的関数ファイル) import java.io.FileReader; FileReader import java.io.BufferedReader; FileReader("/Users/ogawatomoya/Desktop/mok import java.util.StringTokenizer; uteki2015haru.csv"); import java.io.IOException; BufferedReader br3 = new BufferedReader(fr3); fr3 = new import java.util.Arrays; //ファイルの読み込み public class haru2015{ (一緒にする科目ファイル) FileReader public static void main ( String[]args){ fr4 = new FileReader("/Users/ogawatomoya/Desktop/same final int num = 235; //num=科目数 2015haru.csv"); final int pnum = 39; //pnum=常勤教員数 BufferedReader br4 = new BufferedReader(fr4); final int qnum = 49; //qnum=非常勤教員数 //ファイルの読み込み(なるべく一緒にする科目フ final int knum = 5; //knum=曜日数 ァイル) final int hnum = 5; //hnum=時限数 FileReader fr7 = new int cnt, i, k, h, p, p_i, l; FileReader("/Users/ogawatomoya/Desktop/hsam int[][] tanto = new int[pnum + qnum][num]; e2015haru.csv"); //二次元配列 BufferedReader br7 = new BufferedReader(fr7); //ファイルの読み込み(一緒にしない科目ファイル) int[][] a = new int[num+1][30]; int[][] aa = new int[num+1][28]; FileReader //出校日について FileReader("/Users/ogawatomoya/Desktop/diff20 int[][] aaa = new int[num][2]; 15haru.csv"); int[][] kamoku = new int[4][num]; BufferedReader br5 = new BufferedReader(fr5); int[][] mokuteki = new int[pnum + qnum][8]; //ファイルの読み込み(なるべく一緒にしない科目 //目的関数 ファイル) int[][] same = new int[4][2]; //一緒にす る科目 same[ペアの数][] fr5 FileReader = fr6 = new new FileReader("/Users/ogawatomoya/Desktop/hdiff int[][] hsame = new int[4][2]; //なるべく 一緒にする科目 hsame[ペアの数][] 2015haru.csv"); BufferedReader br6 = new BufferedReader(fr6); int[][] diff = new int[500][34]; //一緒にし ない科目について diff[][同一にしない科目の最大数] int[][] hdiff = new int[5][80]; ///読み込んだファイルの処理(科目ファイル) String line; //なるべく 一緒にしたくない科目 hdiff[][] StringTokenizer token; p = 0; while((line = br.readLine()) != null){ token try{ //ファイルの読み込み(科目ファイル) FileReader fr = = StringTokenizer(line,","); new FileReader("/Users/ogawatomoya/Desktop/teach - 17 - cnt = 0; while (token.hasMoreTokens()){ new tanto[p][cnt] = Integer.parseInt(token.nextToken()); //読み込んだファイルの処理(一緒にする科目ファ イル) if(cnt == 10){ String line4; System.out.print(tanto[p][cnt]); StringTokenizer token4; }else{ p = 0; while((line4 = br4.readLine()) != null){ System.out.print(tanto[p][cnt]+","); token4 } = new StringTokenizer(line4,","); cnt++; cnt = 0; } while (token4.hasMoreTokens()){ System.out.println(); same[p][cnt] p++; = Integer.parseInt(token4.nextToken()); } cnt++; //読み込んだファイルの処理(出校ファイル) } String line1; p++; StringTokenizer token1; } p = 0; //読み込んだファイルの処理(なるべく一緒にする while((line1 = br1.readLine()) != null){ 科目ファイル) token1 = new StringTokenizer(line1,","); String line7; StringTokenizer token7; cnt = 0; p = 0; while (token1.hasMoreTokens()){ aa[p][cnt] while((line7 = br7.readLine()) != null){ = Integer.parseInt(token1.nextToken()); token7 = new StringTokenizer(line7,","); System.out.print(aa[p][cnt]+","); cnt = 0; cnt++; while (token7.hasMoreTokens()){ } hsame[p][cnt] System.out.println(); = Integer.parseInt(token7.nextToken()); p++; cnt++; } } //読み込んだファイルの処理(目的関数ファイル) p++; String line3; } StringTokenizer token3; //読み込んだファイルの処理(一緒にしない科目フ p = 0; ァイル) while((line3 = br3.readLine()) != null){ String line5; token3 = new StringTokenizer(line3,","); StringTokenizer token5; p = 0; cnt = 0; while((line5 = br5.readLine()) != null){ while (token3.hasMoreTokens()){ mokuteki[p][cnt] token5 = Integer.parseInt(token3.nextToken()); = new StringTokenizer(line5,","); cnt = 0; cnt++; while (token5.hasMoreTokens()){ } diff[p][cnt] p++; Integer.parseInt(token5.nextToken()); } cnt++; - 18 - = } FileOutputStream p++; FileOutputStream(outputFile); fos } OutputStreamWriter //読み込んだファイルの処理(なるべく一緒にしな OutputStreamWriter(fos); osw = new = new PrintWriter pw = new PrintWriter(osw); い科目ファイル) String line6; ////////////////////////////////////////////////////// StringTokenizer token6; ここより下が出力内容 p = 0; ////////////////////////////////////////////////////// while((line6 = br6.readLine()) != null){ token6 pw.println("Minimize¥n"); = new StringTokenizer(line6,","); pw.print("t1 + t3 + t5 + 6 s1 + u + "); pw.println(); cnt = 0; for(i = 0; i < pnum; i++){ while (token6.hasMoreTokens()){ hdiff[p][cnt] pw.print("u" + tanto[i][0] + " + "); = Integer.parseInt(token6.nextToken()); } pw.println(); cnt++; for(i = 0; i < qnum; i++){ } pw.print("w" + tanto[i+pnum][0] +" + "); p++; } } pw.println(); }catch(IOException ex){ for(i = 0; i< pnum +qnum; i++){ ex.printStackTrace(); if(i == pnum+qnum-1){ } pw.println("y"+tanto[i][0]); // 書き込むファイルの名前 String }else{ outputFileName = pw.print("y"+tanto[i][0]+" + "); "/Users/ogawatomoya/Desktop/haru2015kyotu.lp"; }} // ファイルオブジェクトの生成 pw.println(); File outputFile = new File(outputFileName); pw.println("Subject To¥n"); //担当教員と科目を配列に格納 //全ての科目について、全日程のどこかに入る(常勤 //担当科目別に配列に格納 と非常勤一緒に書く) int c = 0; //ただし週2科目、連続科目はどこかに2回入る for(i = 0; i < pnum + qnum; i++ ){ for ( i = 0 ; i < num ; i++ ){ //科目 i p = 0; for(int j = 1; j <= tanto[i][1]; j++){ for ( k = 1 ; k <= knum ; k++ ){ //曜日k(1= kamoku[0][c] = tanto[i][j+2]; kamoku[1][c] = tanto[i][1]; 月曜) for ( h = 1 ; h <= hnum ; h++ ){ c++; //時限 h }} //配列 a に出校可能日を入れる(可能 1 不可能 0) pw.print("x("+aa[i][0]+","+k+","+h+")"); if ( k < knum || h < for(i=0; i <= num; i++){ hnum ){ for(int j = 0; j < 25; j++){ a[i][j] = aa[i][j+1]; pw.print(" + "); //aa[i][1]には、科目 No.が入っているので、それを } else { 削除する為 if(aa[i][26] == 1) pw.println(" = 1"); }} else{ try{ // 出力ストリームの生成 pw.println(" = 2"); - 19 - }} pw.println(" - 5 y(" + tanto[p][0] + "," + k + p++; ") <= 0"); }} }else{ p = 0; pw.print(" + "); for( k = 1 ; k <= knum ; k++ ){ } for ( h = 1 ; h <= hnum ; h++ ){ cnt++; if( a[i][p] == 0 ){ }}}} pw.println(""); //文字コード変換 ツールで検索(textcode コンバ pw.println("x("+aa[i][0]+","+k+","+h +") = 0"); ーター) } //常勤教員pの出校日は3日まで (水曜は出校確定) p++; for(p = 0; p < pnum; p++){ }}} pw.println("y(" +tanto[p][0]+ ",3) <= 1"); pw.println(); pw.println("y(" //教員 p について,「月 1〜金 5 の 25 箇所それぞれ y("+tanto[p][0]+",2) + y(" +tanto[p][0]+ ",4) + +tanto[p][0]+ ",1) + y("+tanto[p][0]+",5)<= 2"); 最大1科目しか持てない」の式をつくる for ( p = 0 ; p < pnum + qnum ; p++){ //教員 p(春 学期 11 人の教員) } pw.println(); for ( k = 1 ; k <= knum ; k++ ) // k=1= //非常勤教員の出校日制約(なるべく出校日を減ら す(=1つの曜日にたくさん科目を配置する) ) 月,...,k=5=金 for ( h = 1 ; h <= hnum ; h++ ){ // h=1=1 限,...,h=5=5 限 for( p = 0; p < qnum; p++){ pw.println("y(" for (p_i = 1 ; p_i <= +tanto[p+pnum][0]+ pw.print("x("+tanto[p][p_i+1]+","+k + + y("+tanto[p+pnum][0]+",3) tanto[p][1] ; p_i++ ){ ",1) y("+tanto[p+pnum][0]+",2) + +tanto[p+pnum][0]+ ",4) y("+tanto[p+pnum][0]+",5) - y(" + w" + tanto[p+pnum][0] + " <= 0"); +","+h+")"); } if( p_i == pw.println(); tanto[p][1]){ //週 2 科目(月-木,月-金,火-木,火-金)の制約 for(p = 0; p <num; p++){ pw.println(" <= 1"); if(aa[p][26] == 2){ }else{ for(l = 1; l <= hnum; l++){ pw.println("x("+aa[p][0 pw.print(" + "); ]+",1,"+l+") }}}} + pw.println(); x("+aa[p][0]+",2,"+l+") - //常勤教員(&非常勤)の出校日の制約(科目数分) x("+aa[p][0]+",4,"+l+") - for(p = 0; p < pnum + qnum; p++){ x("+aa[p][0]+",5,"+l+") = 0"); for( k = 1 ; k <= }}} knum ; k++){ for(p = 0; p <num; p++){ cnt = 1; if(aa[p][26] == 2){ for(p_i = 1 ; p_i <= tanto[p][1] ; p_i++ ){ pw.println("x("+aa[p][0]+",1,1) for (h = 1 ; h <= hnum ; h++ ){ + pw.print("x("+tanto[p][p_i+1]+","+k+","+h+")"); x("+aa[p][0]+",2,1) + x("+aa[p][0]+",1,2) + if( cnt == (tanto[p][1] * hnum)){ x("+aa[p][0]+",2,2) + x("+aa[p][0]+",1,3) + - 20 - x("+aa[p][0]+",2,3) + x("+aa[p][0]+",1,4) + x("+aa[p][0]+",2,4) + x("+aa[p][0]+",1,5) + for(h=1;h<=5;h++){ pw.println("x("+hsame[i][0]+" x("+aa[p][0]+",2,5) = 1"); ,"+k+","+h+") pw.println("x("+aa[p][0]+",4,1) - + x("+hsame[i][1]+","+k+","+h+ x("+aa[p][0]+",5,1) + x("+aa[p][0]+",4,2) + ") + s1 >= 0"); x("+aa[p][0]+",5,2) + x("+aa[p][0]+",4,3) + pw.println("x("+hsame[i][0]+" x("+aa[p][0]+",5,3) + x("+aa[p][0]+",4,4) + ,"+k+","+h+") x("+aa[p][0]+",5,4) + x("+aa[p][0]+",4,5) + x("+hsame[i][1]+","+k+","+h+ x("+aa[p][0]+",5,5) = 1"); - ") - s1 <= 0"); }} }}} pw.println(); pw.println(); //連続科目の制約 //同一曜日、時限に絶対に配置しない科目 for(p=0; p<num;p++){ for(i=1;i<=diff[0][0];i++){ if(aa[p][26] == 3 ){ for(k=1;k<=5;k++){ pw.println("x("+aa[p][0]+",1,1) + x("+aa[p][0]+",2,1) + x("+aa[p][0]+",3,1) + x("+aa[p][0]+",4,1) + x("+aa[p][0]+",5,1) + x("+aa[p][0]+",1,3) + x("+aa[p][0]+",2,3) + x("+aa[p][0]+",3,3) + x("+aa[p][0]+",4,3) + x("+aa[p][0]+",5,3) = 1"); for(h=1;h<=5;h++){ for(int j=1;j<=diff[i][0];j++){ if(j==diff[i][0]){ pw.println("x("+diff[i][j+1]+","+k+","+h+") <= "+diff[i][1]); pw.println("x("+aa[p][0]+",1,2) + }else{ x("+aa[p][0]+",2,2) + x("+aa[p][0]+",3,2) + x("+aa[p][0]+",4,2) + x("+aa[p][0]+",5,2) + }}}}} x("+aa[p][0]+",1,4) + x("+aa[p][0]+",2,4) + pw.println(); x("+aa[p][0]+",3,4) + x("+aa[p][0]+",4,4) + //なるべく一緒に配置したくない科目 x("+aa[p][0]+",5,4) = 1"); pw.print("x("+diff[i][j+1]+","+k+","+h+") + "); for(i=1;i<=hdiff[0][0];i++){ for(k = 1; k <= knum; k++){ for(k=1;k<=5;k++){ pw.println("x("+aa[p][0]+","+k+",1) - for(h=1;h<=5;h++){ x("+aa[p][0]+","+k+",2) = 0"); for(int pw.println("x("+aa[p][0]+","+k+",3) - j=2;j<=hdiff[i][0]+1;j++){ x("+aa[p][0]+","+k+",4) = 0"); if(j==hdiff[i][0]+1){ }}} pw.println("x("+hdiff[i][j]+","+k+","+ pw.println(); h+") - t"+hdiff[i][1]+" <= 0"); //同一曜日、時限に配置する科目 }else{ for(i=1;i<=same[0][0];i++){ pw.print("x("+hdiff[i][j]+","+k+","+h for(k=1;k<=5;k++){ +") + "); for(h=1;h<=5;h++){ }}}}} pw.println("x("+same[i][0]+"," +k+","+h+") - x("+same[i][1]+","+k+","+h+") = 0"); pw.println(); //なるべく5時限目に科目を配置しない for(i = 0; i < num;i++){ }}} for(k = 1; k <= 5; k++){ pw.println(); if(i==num-1 && k==5){ //なるべく同一曜日、時限に配置する科目 for(i=1;i<=hsame[0][0];i++){ pw.print("x("+aa[i][0]+","+ k +",5) - u for(k=1;k<=5;k++){ <= 0"); - 21 - }else{ } pw.print("x("+aa[i][0]+","+ k +",5) + "); pw.println(); } } for(p = 0; p < pnum + qnum; p++){ }} for(k = 1; k<= knum; k++){ pw.println(); pw.print("y("+tanto[p][0]+","+k+") "); //各曜日にまんべんなく科目を配置する制約(目的 } pw.println(); 関数値で制御) for ( p = 0; p < pnum; p++){ } for ( k = 1 ; k <= knum ; k++ ) pw.print("End"); for (p_i = 1 ; p_i <= tanto[p][1] ; p_i++ ){ // 後始末 pw.close(); for ( h = 1 ; h <= hnum ; h++ ){ }catch(Exception e) { pw.print("x("+tanto[p][p_i+1]+","+k+","+h+")"); if( h == 5 && p_i == tanto[p][1]){ }}} pw.println(" - u" + tanto[p][0] + " <= 0"); }else{ pw.print(" + "); } }}} pw.println(); //教員 p の目的関数 for(i = 0; i < pnum+qnum; i++){ for(p = 0; p< 5; p++){ if(p == 4){ pw.println(mokuteki[i][p+1]+ " y("+tanto[i][0]+","+(p+1)+") - y"+ tanto[i][0]+" <= 0"); }else{ pw.print(mokuteki[i][p+1]+" y("+tanto[i][0]+","+(p+1)+") + "); }} pw.println(); } pw.println(); pw.print("¥nBounds ¥nBinaries¥n"); //Binary の後に、今までに使った変数を 0-1 変数だ と定義する為に、記述する for(i = 0; i < num; i++ ){ for(k = 1; k <= knum; k++){ for(h h++){ = 1; h e.printStackTrace(); <= hnum; pw.print("x("+aa[i][0]+","+k+","+h+") "); } - 22 -
© Copyright 2024 Paperzz