給食 最適化 PuLP

給食 最適化

給食に関する最適化問題の例として、以下の問題を考えてみます。

ある学校では、毎日ランチとしてA,B,Cの3種類のメニューから1つを提供します。

各メニューには以下のような栄養素が含まれます。

栄養素メニューAメニューBメニューC
カロリー700 kcal600 kcal800 kcal
タンパク質30 g25 g20 g
脂質10 g15 g20 g
炭水化物50 g40 g60 g

また、各クラスには生徒が20名ずつ在籍しており、1日に必要な栄養素の量は以下の通りとします。

栄養素必要量
カロリー10000 kcal
タンパク質500 g
脂質200 g
炭水化物700 g

このとき、1日の給食で提供する各メニューの量を決定し、必要な栄養素を満たすことができる最小のコストを求める最適化問題を考えます。

解法

下記のコードでは、変数a,b,cをそれぞれメニューA,B,Cの提供量とし、目的関数 2a + 3b + 4c を最小化するように設定しています。

制約条件は、各栄養素の必要量を満たすように設定しています。

最後に、PuLPのsolve()メソッドを呼び出して最適化問題を解き、結果を出力しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import pulp

# 問題の定義
problem = pulp.LpProblem('school_lunch', pulp.LpMinimize)

# 変数の定義
a = pulp.LpVariable('a', lowBound=0, cat='Continuous')
b = pulp.LpVariable('b', lowBound=0, cat='Continuous')
c = pulp.LpVariable('c', lowBound=0, cat='Continuous')

# 目的関数の定義
problem += 2 * a + 3 * b + 4 * c

# 制約条件の定義
problem += 700 * a + 600 * b + 800 * c >= 10000
problem += 30 * a + 25 * b + 20 * c >= 500
problem += 10 * a + 15 * b + 20 * c >= 200
problem += 50 * a + 40 * b + 60 * c >= 700

# 問題の解法
status = problem.solve()

# 解の出力
print('Result:')
print(f'a: {pulp.value(a)}')
print(f'b: {pulp.value(b)}')
print(f'c: {pulp.value(c)}')
print(f'Minimum Cost: {pulp.value(problem.objective)}')

実際に上記のコードを実行すると、以下のような結果が得られます。

[実行結果]
Result:
a: 15.0
b: 0.0
c: 2.5
Minimum Cost: 40.0

この結果から、メニューAを15人分、メニューBを0人分、メニューCを2.5人分提供することで、必要な栄養素を満たすことができ、最小のコストは40であることがわかります。

おにごっこ最適化 PuLP

おにごっこ最適化

おにごっこに関する最適化問題の例として、5人でおにごっこをする場合、どのように逃げた方が最適か?という問題を考えてみます。

解法

以下は、PuLPを用いた解法です。

まず、必要なライブラリをインポートします。

1
from pulp import *

次に、問題の定式化を行います。この問題では、以下のようになります。

🔹変数
 x_i (i = 1, 2, …, 5): i番目の逃げる場所に行くかどうか (1: 逃げる, 0: 逃げない)
🔹目的関数
 逃げる人数の最大化
🔹制約条件
 逃げる人数は4人以下

これをPuLPで表現すると、以下のようになります。

1
2
3
4
5
6
7
8
9
10
11
# 問題の定義
prob = LpProblem('Onigokko', LpMaximize)

# 変数の定義
x = [LpVariable('x{}'.format(i), cat='Binary') for i in range(1, 6)]

# 目的関数の定義
prob += lpSum(x)

# 制約条件の定義
prob += lpSum(x) <= 4

ここで、lpSum()はPuLPが提供する関数で、与えられたリストの要素の和を計算します。

制約条件は、<=で表現しています。


次に、PuLPを使って求解を行います。

1
2
# 求解
status = prob.solve()

このようにして、求解が終了しました。最後に、結果を表示してみます。

1
2
3
4
5
6
# 結果を表示
print('Status:', LpStatus[status])
for i in range(1, 6):
print('x{} = {}'.format(i, int(value(x[i - 1]))))
print('Optimal value:', value(prob.objective))

これを実行すると、以下のような結果が得られます。

[実行結果]
Status: Optimal
x1 = 1
x2 = 1
x3 = 1
x4 = 1
x5 = 0
Optimal value: 4.0

結果から、最適解が得られ、1番目、2番目、3番目、4番目の場所に逃げることが最適解になることが分かりました。

また、逃げる人数は4人であることも分かります。

出店最適化 PuLP

出店最適化

あなたはピザ店を経営しており、新しい店舗を出店することにしました。

出店する場所は3つあり、それぞれの場所には以下のような特徴があります。

🔹場所1:人口密度が高いが、家賃が高い
🔹場所2:人口密度が低いが、家賃が安い
🔹場所3:人口密度と家賃のバランスがとれている


あなたは、出店する店舗の場所を最適化することにしました。

以下の制約条件を考慮して、各場所の評価スコアを計算し、最も評価スコアが高い場所に店舗を出店することにしました。

🔹店舗の出店数は1つ
🔹店舗の出店費用はすべて同じ
🔹各場所には、人口密度、家賃、および場所による固有の評価スコアがあります。
 評価スコアは0から100までのスケールで測定されます。
🔹人口密度が高い場所ほど評価スコアが高くなるが、家賃が高い場合は低くなる。
🔹家賃が安い場所ほど評価スコアが高くなるが、人口密度が低い場合は低くなる。
🔹人口密度と家賃のバランスがとれている場所ほど評価スコアが高くなる。


各場所の人口密度、家賃、および場所による固有の評価スコアは以下の通りとします。

場所人口密度家賃評価スコア
19010070
2505050
3707080

解法

PuLPを使って最適化問題を解いてみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# ①必要なライブラリをインポート
from pulp import *

# ②問題を定義する
prob = LpProblem('出店最適化', LpMaximize)

# ③変数を定義する
x1 = LpVariable('場所1', 0, 1, LpInteger)
x2 = LpVariable('場所2', 0, 1, LpInteger)
x3 = LpVariable('場所3', 0, 1, LpInteger)

# ④目的関数を定義する
prob += 70 * x1 + 50 * x2 + 80 * x3

# ⑤制約条件を定義する
prob += x1 + x2 + x3 == 1
prob += 90 * x1 + 50 * x2 + 70 * x3 <= 100
prob += 100 * x1 + 50 * x2 + 70 * x3 <= 100

# ⑥最適化を実行する
status = prob.solve()

# ⑦結果を表示する
print('出店する場所:', end=' ')
if x1.value() == 1:
print('場所1')
elif x2.value() == 1:
print('場所2')
elif x3.value() == 1:
print('場所3')

print('最適な評価スコア:', value(prob.objective))

①必要なライブラリをインポート

PuLPを使うために必要なライブラリをインポートしています。

②問題を定義する

LpProblem関数を使って問題を定義します。

第一引数には問題の名前を指定し、第二引数には最大化か最小化かを指定します。

ここでは「出店最適化」という名前で最大化問題を定義しています。

③変数を定義する

LpVariable関数を使って変数を定義します。

第一引数には変数の名前を指定し、第二引数には変数の下限値、第三引数には変数の上限値、第四引数には変数の型(整数値か連続値か)を指定します。

ここでは、場所1、場所2、場所3の3つの変数を定義しています。各変数は0または1の整数値をとります。

④目的関数を定義する

目的関数を定義します。

ここでは、ピザ店を出店する場所によって得られる評価スコアを最大化するように設定しています。

場所1に出店すると70点、場所2に出店すると50点、場所3に出店すると80点の評価スコアが得られると仮定しています。

⑤制約条件を定義する

制約条件を定義します。ここでは、以下の条件を設定しています。

🔹出店する場所は1つだけである。
🔹場所1、場所2、場所3のそれぞれで得られる評価スコアが、100点以下である。

⑥最適化を実行する

LpProblem関数で定義した問題を解くために、solveメソッドを呼び出します。

解を求めるアルゴリズムはPuLPが自動的に選択します。

⑦結果を表示する

解の値を出力します。value関数を使って目的関数の値を取得しています。


上記のコードを実行すると、以下のように結果が表示されます。

[実行結果]
出店する場所: 場所3
最適な評価スコア: 80.0

この結果から、ピザ店を出店する最適な場所は場所3であり、評価スコアは80点であることが分かります。


今回の例では、ピザ店を出店する場所を最適化する問題を定義し、PuLPを使って解くことで、最適な出店場所とそのときの評価スコアを求めました。

薬の処方 PuLP

薬の処方

薬の処方に関する最適化問題を考えてみます。

例えば、ある患者に対して複数の薬剤が処方される場合、以下のような問題を最適化することができます。


目的:

 🔹与えられた症状や疾患に最も効果的な薬剤の組み合わせを見つける。

制約条件:

 🔹処方された薬剤の総量が過剰でないこと。
 🔹薬剤の相互作用がないこと。
 🔹薬剤の副作用を最小化すること。
 🔹患者のアレルギーや過去の治療経験を考慮すること。

解法

この問題をPuLPを使って解くには、以下のような手順を踏むことができます。

1. 処方された薬剤を変数として定義する。
2. 目的関数を定義する。

 薬剤の総効果を最大化します。

3. 制約条件を定義する。

 薬剤の総量が過剰でないこと、薬剤の相互作用がないこと、薬剤の副作用を最小化すること、患者のアレルギーや過去の治療経験を考慮することなどが挙げられます。

4. PuLPを使って問題を解く。
5. 解を出力する。

 どの薬剤をどの程度処方するかを表示します。


以下は、PuLPを使って薬の処方問題を解く例です。

この例では、ある疾患に対して3種類の薬剤が処方される場合を考えます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from pulp import *

# 変数の定義
x1 = LpVariable("x1", lowBound=0)
x2 = LpVariable("x2", lowBound=0)
x3 = LpVariable("x3", lowBound=0)

# 目的関数の定義
prob = LpProblem("Prescription", LpMaximize)
prob += 2*x1 + 3*x2 + 4*x3

# 制約条件の定義
prob += x1 + x2 + x3 <= 1000
prob += 0.5 * x1 + x2 + x3 <= 800
prob += x1 + x2 + 2 * x3 <= 1500

# 問題を解く
status = prob.solve()

# 結果の出力
print("Status:", LpStatus[status])
print("Optimal Solution:")
for v in prob.variables():
print(v.name, "=", v.varValue)
print("Optimal Value =", value(prob.objective))

この例では、薬剤x1、x2、x3の各総量が制限内で最も効果的な組み合わせを求めることが目的となります。

変数x1、x2、x3はそれぞれ、薬剤x1、x2、x3の総量を表しています。

目的関数は、薬剤の総効果を最大化するように定義されています。

制約条件は、各薬剤の総量が規定の制限内に収まるように設定されています。


この例では、PuLPを使って簡単に最適な薬剤の組み合わせを求めることができます。

実際の医療現場では、このような最適化技術が、医師の意思決定を支援するツールとして役立つことが期待されています。


上記のコードを実行すると、以下のように結果が表示されます。

[実行結果]
Status: Optimal
Optimal Solution:
x1 = 0.0
x2 = 100.0
x3 = 700.0
Optimal Value = 3100.0

これは、薬剤x1を0単位、薬剤x2を100単位、薬剤x3を700単位処方することで、疾患に対する最大の効果が得られることを示しています。

また、最適な薬剤の組み合わせによって、総効果は3100となります。

看護師スケジュール作成問題 PuLP

看護師スケジュール作成問題

医療に関する最適化問題の例として、病院の看護師スケジュール作成問題を考えてみましょう。

この問題では、病院で働く看護師の勤務シフトをスケジュールする必要があります。

スケジュールは、各看護師の希望や能力に応じて、労働法や病院の規則に準拠しなければなりません。


PuLPを使用して、看護師のスケジュールを最適化し、病院の運営を改善することができます。

以下が、この問題をPuLPで解くためのコードです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from pulp import *

# ①条件を定義する
# 看護師の数
num_nurses = 5

# シフトの数
num_shifts = 3

# 各シフトで必要な看護師の数
shift_requirements = [2, 1, 2]

# 看護師の希望を表す二次元配列。0は不可能、1は可能。
# 希望は、勤務シフトを表すインデックスで表されます。
# たとえば、nurse_preferences[0][1] == 1は、看護師0が2番目のシフトを希望していることを示します。
nurse_preferences = [[0, 1, 1], [1, 0, 1], [1, 1, 0], [1, 1, 1], [0, 1, 0]]

# ②問題を定義する
problem = LpProblem("Nurse Scheduling", LpMinimize)

# ③変数を作成する
shifts = LpVariable.dicts("Shift", ((n, s) for n in range(num_nurses) for s in range(num_shifts)), cat='Binary')

# ④目的関数を設定する
problem += lpSum([shifts[(n, s)] for n in range(num_nurses) for s in range(num_shifts)])

# ⑤制約条件を設定する
for s in range(num_shifts):
problem += lpSum([shifts[(n, s)] for n in range(num_nurses)]) >= shift_requirements[s]

for n in range(num_nurses):
problem += lpSum([shifts[(n, s)] for s in range(num_shifts)]) == 1

for n in range(num_nurses):
for s in range(num_shifts):
if nurse_preferences[n][s] == 0:
problem += shifts[(n, s)] == 0

# ⑥問題を解く
status = problem.solve()

# ⑦結果を出力する
print("ステータス:", LpStatus[status])

for n in range(num_nurses):
for s in range(num_shifts):
if value(shifts[(n, s)]) == 1:
print("看護師{}は、シフト{}に割り当てられました".format(n, s))

① 条件を定義する

最初に、看護師の数、シフトの数、各シフトで必要な看護師の数、および各看護師のシフト希望を表す二次元配列を定義します。

② 問題を定義する

PuLPの LpProblem() 関数を使用して、問題を定義します。

この問題は、看護師のスケジュール作成を目的としています。

PuLPの LpMinimize モードを使用して、最小化問題を解決します。

③ 変数を作成する

PuLPの LpVariable.dicts() 関数を使用して、shifts 変数を作成します。

各変数は、看護師があるシフトに割り当てられる場合は1、そうでない場合は0を取るバイナリ変数です。

④ 目的関数を設定する

PuLPの lpSum() 関数を使用して、目的関数を設定します。

目的関数は、スケジュールに割り当てられたシフトの数を最小化することです。

⑤ 制約条件を設定する

PuLPの += 演算子を使用して、制約条件を設定します。

最低限必要な看護師数の制約条件、各看護師が1つのシフトにしか割り当てられない制約条件、および各看護師がシフト希望に従って割り当てられる制約条件を設定します。

⑥ 問題を解く

PuLPの solve() 関数を使用して、問題を解決します。

最適解が見つかれば、そのステータスが返されます。

⑦ 結果を出力する

最後に、割り当てられた各看護師のシフトを表示します。

看護師の番号とシフトの番号が表示されます。


このコードを実行すると次のような結果が出力されます。

[実行結果]
ステータス: Optimal
看護師0は、シフト2に割り当てられました
看護師1は、シフト2に割り当てられました
看護師2は、シフト0に割り当てられました
看護師3は、シフト0に割り当てられました
看護師4は、シフト1に割り当てられました

ステータスが Optimalであることが示されています。

これは、問題が最適解を見つけることができたことを示しています。

次に、各看護師がどのシフトに割り当てられたかが表示されます。

例えば、看護師0は2番目のシフトに割り当てられました。

これは、制約条件目的関数を満たし、最適なスケジュールに割り当てられたことを意味します。

このプログラムによって、病院の看護師スケジュール作成問題が解決され、最適なスケジュールが得られました。

生産計画問題 PuLP

生産計画問題

生産計画問題の例を考えます。

3つの製品(A、B、C)を製造するために必要な原材料の量、1つの生産ラインの生産能力、および各製品の利益率が与えられています。

各製品は少なくとも1つ以上生産する必要があります。

製品の生産量を最適化して、最大の利益を得ることが目的です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from pulp import *

# 1. 問題の定義
prob = LpProblem("Production Planning Problem", LpMaximize)

# 2. 変数の定義
A = LpVariable("Product A Units", lowBound=0, cat='Integer')
B = LpVariable("Product B Units", lowBound=0, cat='Integer')
C = LpVariable("Product C Units", lowBound=0, cat='Integer')

# 3. 目的関数の定義
prob += 3000*A + 5000*B + 2000*C

# 4. 制約条件の定義
prob += 3*A + 2*B + 1.5*C <= 1500
prob += 1.5*A + 2.5*B + 3*C <= 3000
prob += A + B + C <= 1000
prob += A >= 1
prob += B >= 1
prob += C >= 1

# 5. 最適化の実行
prob.solve()

# 6. 結果の表示
print("Status:", LpStatus[prob.status])
for v in prob.variables():
print(v.name, "=", v.varValue)
print("Total Profit = ", value(prob.objective))

1. 問題の定義

LpProblem関数を使用して問題を定義しています。

LpProblem関数は、第1引数に問題名、第2引数に最小化か最大化を指定します。

ここでは、”Production Planning Problem”という名前の最大化問題を定義しています。


2. 変数の定義

LpVariable関数を使用して、問題に必要な変数を定義しています。

LpVariable関数は、第1引数に変数の名前、lowBoundに変数の下限、catに変数の種類を指定します。

ここでは、”Product A Units”、”Product B Units”、”Product C Units”という名前の整数変数を定義しています。


3. 目的関数の定義

目的関数を定義しています。

ここでは、各製品の単位あたりの利益と各製品の製造量を掛けた値を合計して、最大化するようにしています。


4. 制約条件の定義

制約条件を定義しています。ここでは、各製品の製造に必要な原料の量、製品の総製造量に関する制約を定義しています。

また、各製品を少なくとも1つ生産する必要があるため、各変数に対して下限制約も設定しています。


5. 最適化の実行

solve関数を呼び出して、問題を解きます。


6. 結果の表示

解が得られたら、status属性を使用して最適性のステータスを表示し、variables属性を使用して各変数の最適な値を表示し、objective属性を使用して最適な目的関数値(総利益)を表示します。


このコードを実行すると、以下のような結果が出力されます。

[実行結果]
Status: Optimal
Product_A_Units = 1.0
Product_B_Units = 747.0
Product_C_Units = 2.0
Total Profit =  3742000.0

これにより、各製品を少なくとも1つ生産し、最大の利益である3742000ドルを得るために、1単位の製品A、747単位の製品B、および2単位の製品Cを製造する必要があることが確認できました。

割り当て最適化 PuLP

割り当て最適化

割り当て最適化問題の例として、「3人のスタッフが5つのタスクに割り当てられる場合、全体的な生産性を最大化する割り当てを決定する」という問題を考えてみます。

PuLPを使ってこの問題を解くコードは以下のようになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from pulp import *

# 問題の初期化
problem = LpProblem("Task Assignment Problem", LpMaximize)

# 変数の定義
staff = ['Staff1', 'Staff2', 'Staff3']
tasks = ['Task1', 'Task2', 'Task3', 'Task4', 'Task5']
x = LpVariable.dicts('x', [(i, j) for i in staff for j in tasks], lowBound=0, upBound=1, cat=LpInteger)

# 目的関数の定義
problem += lpSum([x[i, j] for i in staff for j in tasks])

# 制約条件の定義
for j in tasks:
problem += lpSum([x[i, j] for i in staff]) == 1

for i in staff:
problem += lpSum([x[i, j] for j in tasks]) <= 2

# 問題の解決
status = problem.solve()

# 結果の出力
print("ステータス:", LpStatus[status])
for i in staff:
for j in tasks:
if x[i, j].value() == 1:
print(i, "は", j, "に割り当てられました。")

このコードでは、問題を LpProblem として初期化し、変数 x を定義します。

変数 x は、スタッフとタスクのペアに対してバイナリ変数を持つ辞書型データとして定義されます。

目的関数は、割り当てられたタスクの数を最大化するように定義されます。

制約条件として、各タスクには1人のスタッフが割り当てられ、各スタッフには最大で2つのタスクが割り当てられるように定義されます。

最後に、PuLPの solve() メソッドを呼び出して問題を解決し、結果を出力します。


このコードを実行すると、以下のような結果が出力されます。

[実行結果]
ステータス: Optimal
Staff1 は Task2 に割り当てられました。
Staff1 は Task5 に割り当てられました。
Staff2 は Task1 に割り当てられました。
Staff3 は Task3 に割り当てられました。
Staff3 は Task4 に割り当てられました。

これは、最適な割り当てが見つかったことを示しています。

たとえば、スタッフ1はタスク2とタスク5に、スタッフ2はタスク1に、スタッフ3はタスク3とタスク4に割り当てられました。

これにより、各スタッフに最大で2つのタスクが割り当てられ、割り当てられたタスクの総数が最大になっています。


なお、PuLPはオープンソースの数理最適化モデリング言語であり、線形および混合整数線形最適化問題を解くための強力なツールです。

PuLPを使用することで、上記のような最適化問題を簡単にモデル化し、解決することができます。

為替ヘッジ問題 PuLP

為替ヘッジ問題

FX(外国為替)に関して最適化問題の一例として、為替ヘッジ問題が挙げられます。

為替ヘッジとは、外国通貨での取引によるリスクを減らすために、為替レートの変動に対して保護することです。


以下の例では、ある企業が米ドルで仕入れた商品を日本円で販売する場合の為替ヘッジ問題を考えます。

企業は、ある時点で米ドルを使って商品を購入する必要がありますが、商品の販売は数ヶ月後に行われます。

この間に、円/ドルの為替レートが変動することにより、企業の利益に影響を与える可能性があります。

企業は、為替レート変動のリスクを軽減するために、先物市場で円/ドルの未決済契約(フォワード契約)を取引することを考えています。


最適化問題は、フォワード契約を取引する数量を決定することです。

フォワード契約の数量が大きいほど、為替レート変動によるリスクは小さくなりますが、フォワード契約にかかるコストが増加します。

一方、フォワード契約の数量が少ないほど、コストは低くなりますが、リスクは大きくなります。


最適化問題をPuLPを用いて解いてみましょう。

以下のコードは、PuLPを用いた為替ヘッジ問題の最適化問題を解く例です。

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

# 問題の設定
problem = pulp.LpProblem('FX Hedge Problem', pulp.LpMinimize)

# 変数の定義
forward_contract = pulp.LpVariable('Forward Contract', lowBound=0)

# 目的関数の定義
problem += 0.01 * forward_contract

# 制約条件の定義
problem += 10000 * forward_contract >= 1000000
problem += 10000 * forward_contract <= 2000000

# 問題の解決
status = problem.solve()

# 結果の出力
print('Optimal Solution:')
print('Forward Contract Quantity =', pulp.value(forward_contract))
print('Minimum Cost =', pulp.value(problem.objective))

上記のコードでは、PuLPを使用して最小化問題を定義しています。

変数 forward_contract は、フォワード契約の数量を表しています。

目的関数は、フォワード契約にかかるコストを最小化するように設定しています。

フォワード契約の数量が増加すると、コストが増加することに注意してください。

制約条件では、フォワード契約の数量が、1ドルあたり100円から200円の範囲内に収まるように設定しています。

この制約条件は、フォワード契約によるリスク軽減とコスト増加のバランスを取るために必要です。


最後に、PuLPの solve() メソッドを呼び出して問題を解決し、フォワード契約の数量と最小コストを出力します。

解決結果には、PuLPの LpStatus 列挙型を用いてアクセスすることができます。

解が正常に見つかった場合、LpStatusOptimal が返されます。


上記のコードを実行すると、以下のような結果が得られます。

[実行結果]
Optimal Solution:
Forward Contract Quantity = 100.0
Minimum Cost = 1.0

この結果から、最適なフォワード契約の数量は100であり、最小コストは1万ドルであることがわかります。

これは、企業がリスクを軽減するためにフォワード契約を100万ドル分取引することが最も適していることを示しています。

多目的最適化問題 PuLP

多目的最適化問題

多目的最適化問題の例として、以下の問題を考えてみましょう。

🔹ある工場で、製品 A と製品 B を生産することができます。
🔹製品 A の生産には 2 時間かかり、製品 B の生産には 3 時間かかります。
🔹工場は最大10時間稼働することができます。
🔹製品 A 1 個あたりの利益は 10 ドルで、製品 B 1 個あたりの利益は 15 ドルです。
🔹工場で作ることのできる製品の最大量は 50 個です。

利益を最大化するためには製品Aと製品Bをそれぞれ何個作成すればよいでしょうか。

解法・ソースコード

PuLPを使用して、この問題を解決していきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pulp import *

# ①問題の定義
prob = LpProblem("Multiple Objectives Problem", LpMaximize)

# ②変数の定義
A = LpVariable("A", 0, 50, LpInteger)
B = LpVariable("B", 0, 50, LpInteger)

# ③目的関数の定義
prob += 10 * A + 15 * B, "Total Profit"

# ④制約条件の定義
prob += 2 * A + 3 * B <= 10, "Time Limit"
prob += A + B <= 50, "Production Limit"

# ⑤問題の解決
prob.solve()

# ⑥結果の表示
print("A =", value(A))
print("B =", value(B))
print("Total Profit =", value(prob.objective))

①問題の定義

「Multiple Objectives Problem」という名前の最大化問題を作成するために、LpProblem()関数を使用します。

②変数の定義

変数AとBを作成し、LpVariable()関数を使用して上限値、下限値、および変数の種類(整数)を指定します。

③目的関数の定義

目的関数を定義するために、LpProblem()で作成した最大化問題である「prob」に、prob += を使用して目的関数を追加します。

「10 * A + 15 * B」という式を使用して、AとBの変数の重み付き合計を定義し、「Total Profit」という名前を付けます。

④制約条件の定義

制約条件を定義するために、LpProblem()で作成した最大化問題である「prob」に、prob += を使用して制約条件を追加します。

1つ目の制約条件では、「2 * A + 3 * B」が「10」以下である必要があります。

この制約条件の名前は「Time Limit」となっています。

2つ目の制約条件では、「A + B」が「50」以下である必要があります。

この制約条件の名前は「Production Limit」となっています。

⑤問題の解決

「prob」で定義された最大化問題を解くために、prob.solve()関数を使用します。

⑥結果の表示

最適解の結果を表示するために、value()関数を使用して、変数A、B、および目的関数の値を表示します。


実行結果は以下の通りです。

[実行結果]
A = 2.0
B = 2.0
Total Profit = 50.0

この結果から、最大化された製品 A は 2 個、製品 B は 2 個であり、それらの総利益は 50 ドルであることがわかります。

医療最適化 PuLP

医療最適化

医療システムにおける最適化問題の一つとして、患者が異なる病院から受け取る医療サービスを最適化する問題があります。

この問題は、患者が最適な医療を受け、病院の効率を向上させることが目的です。

以下は、この問題をPuLPを使用して解決する方法の例です。

問題:

ある地域には3つの病院があり、それぞれが特定の専門分野に長けています。

患者はそれぞれの病院で必要な医療サービスを受ける必要があり、各病院の受け入れ可能な患者数には制限があります。

目標は、全ての患者が最適な病院で治療を受けることができるように、患者を病院に割り当てることです。

解法・ソースコード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from pulp import *

# ①問題を定義する
problem = LpProblem("Hospital Patient Assignment Problem", LpMinimize)

# ②患者がどの病院に割り当てられるかを決定する変数を定義する
patient_to_hospital = LpVariable.dicts("Patient to Hospital",
[(i, j) for i in range(3) for j in range(10)],
cat="Binary")

# ③目的関数を設定する
costs = [[10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
[30, 25, 35, 45, 55, 65, 75, 85, 95, 105],
[50, 45, 40, 35, 30, 25, 20, 15, 10, 5]]
problem += lpSum([patient_to_hospital[(i, j)] * costs[i][j] for i in range(3) for j in range(10)])

# ④制約条件を設定する
hospital_capacities = [5, 7, 3]
for i in range(3):
problem += lpSum([patient_to_hospital[(i, j)] for j in range(10)]) <= hospital_capacities[i]
for j in range(10):
problem += lpSum([patient_to_hospital[(i, j)] for i in range(3)]) == 1

# ⑤問題を解く
problem.solve()

# ⑥結果を出力する
for i in range(3):
for j in range(10):
if value(patient_to_hospital[(i, j)]) == 1:
print("Patient {} is assigned to Hospital {}".format(j+1, i+1))

①問題を定義する

最適化問題を定義しています。

問題の名前は “Hospital Patient Assignment Problem” で、最小化問題として設定されています。

②患者がどの病院に割り当てられるかを決定する変数を定義する

患者がどの病院に割り当てられるかを決定する変数を定義しています。

この変数は、患者と病院のインデックスの組み合わせのリストとして指定されており、バイナリ変数として定義されています。

この変数が1の場合、患者がその病院に割り当てられたことを示します。

③目的関数を設定する

目的関数を設定しています。

この問題の目的は、患者を割り当てるために必要なコストを最小化することです。

ここで、各患者と病院の組み合わせに対するコストが定義されており、それらの積の和が目的関数として定義されています。

④制約条件を設定する

制約条件を設定しています。

病院の収容能力を超えないように、各病院に割り当てられる患者の数を制限しています。

また、各患者は1つの病院にしか割り当てられないように、患者ごとに制約条件を設定しています。

⑤問題を解く

問題を解くために、PuLPのsolve関数を呼び出しています。

⑥結果を出力する

各患者がどの病院に割り当てられたかを表示しています。

各患者に対して、割り当てられた病院の番号が表示されます。


実行結果は以下の通りです。

[実行結果]
Patient 1 is assigned to Hospital 1
Patient 3 is assigned to Hospital 1
Patient 4 is assigned to Hospital 1
Patient 6 is assigned to Hospital 1
Patient 7 is assigned to Hospital 1
Patient 2 is assigned to Hospital 2
Patient 5 is assigned to Hospital 2
Patient 8 is assigned to Hospital 3
Patient 9 is assigned to Hospital 3
Patient 10 is assigned to Hospital 3

1つ目の病院は5人までしか受け入れることができるため、患者1, 3, 4, 6, 7が病院1に割り当てられました。

同様に、病院2は7人まで、病院3は3人までしか受け入れることができないため、患者2と5は病院2に、患者8、9、10は病院3に割り当てられました。