マーケティング最適化問題 PuLP

マーケティング最適化問題

簡単なマーケティング最適化問題を考えます。

この問題では、広告費用を最小限に抑えつつ、与えられた広告予算内で最大の利益を得るために、どの製品にどの程度の広告予算を割り当てるかを決定する必要があります。

例:

ある企業が3つの製品を販売しています。

各製品には、それぞれ異なる利益と広告費用があります。

企業は、合計で10,000ドルの広告予算を持っています。

どの製品にどの程度の広告予算を割り当てるべきかを決定して、利益を最大化する必要があります。

各製品の利益広告費用は以下のとおりです。

製品利益広告費用
A3002000
B2001500
C1501000

解き方・ソースコード

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

[Google Colaboratory]

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("マーケティング最適化問題", pulp.LpMaximize)

# ②変数を定義
a = pulp.LpVariable("Aの広告費用", lowBound=0)
b = pulp.LpVariable("Bの広告費用", lowBound=0)
c = pulp.LpVariable("Cの広告費用", lowBound=0)

# ③目的関数を定義
problem += 300*a + 200*b + 150*c, "利益の合計"

# ④制約条件を定義
problem += a + b + c <= 10000, "広告予算の合計"
problem += a <= 2000, "Aの広告費用の上限"
problem += b <= 1500, "Bの広告費用の上限"
problem += c <= 1000, "Cの広告費用の上限"

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

# ⑥結果を出力
print(f"ステータス: {pulp.LpStatus[status]}")
print(f"最大利益: {problem.objective.value()}")
print(f"Aの広告費用: {a.value()}")
print(f"Bの広告費用: {b.value()}")
print(f"Cの広告費用: {c.value()}")

①問題を初期化

pulp.LpProblemを使って問題の名前を指定し、最大化問題であることを指定します。

②変数を定義

pulp.LpVariableを使って各製品の広告費用を表す変数a、b、cを定義します。

また、lowBound引数を使って変数の下限を0に設定します。

③目的関数を定義

各製品の利益を表す定数と広告費用を表す変数をかけた値を足し合わせて、最大化したい利益の合計を定義します。

④制約条件を定義

各制約条件を表す不等式をproblem +=で連結していきます。

制約条件は、広告予算の合計が10000ドル以下、各製品の広告費用が上限に収まることです。

⑤問題解決

problem.solve()を使って問題を解きます。

⑥結果出力

解のステータスや各製品の広告費用最大利益を出力します。

pulp.LpStatus[status]を使って、解のステータスをテキストで表示します。

また、各変数の値はvalue()を使って取得できます。

最大利益はproblem.objective.value()で取得できます。

[実行結果]

ステータス: Optimal

最大利益: 1050000.0

Aの広告費用: 2000.0

Bの広告費用: 1500.0

Cの広告費用: 1000.0

この結果から、最大利益は1050000ドルであり、A製品には広告費用2000ドル、B製品には広告費用1500ドル、C製品には広告費用1000ドルを割り当てることが最適であることがわかります。

また、制約条件がすべて満たされていることも確認できます。

建設プロジェクトのスケジューリング問題の最適化 PuLP

建設プロジェクトのスケジューリング問題の最適化

建設プロジェクトのスケジューリング問題を考えてみます。

ある建設プロジェクトにおいて、以下の6つの作業が必要です。

🔹作業A: 2週間かかる
🔹作業B: 3週間かかる
🔹作業C: 4週間かかる
🔹作業D: 2週間かかる
🔹作業E: 3週間かかる
🔹作業F: 4週間かかる

作業が開始されたら中断することはできません。さらに、以下の制約があります。

🔹作業A, B, Cは同時に開始することができます。
🔹作業Bが完了した後、作業Dを開始することができます。
🔹作業Cが完了した後、作業Eを開始することができます。
🔹作業DとEは同時に開始するることができます。
🔹作業DとEが完了した後、作業Fを開始できます。

解き方・ソースコード

この問題をPuLPで解いてみます。

問題の設定や解法の詳細はコメントを確認して下さい。

[Google Colaboratory]

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 必要なライブラリをインポートする
import pulp

# 問題を定義する
problem = pulp.LpProblem('Construction Project', 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')
d = pulp.LpVariable('d', lowBound=0, cat='Continuous')
e = pulp.LpVariable('e', lowBound=0, cat='Continuous')
f = pulp.LpVariable('f', lowBound=0, cat='Continuous')

# 制約式を定義する

# 各作業の所要日数
problem += a >= 2
problem += b >= 3
problem += c >= 4
problem += d >= 2
problem += e >= 3
problem += f >= 4

# 作業A, B, Cは同時に開始することができる
# a + 2 <= dは、作業Dを開始する前に、作業Aが完了している必要があることを表している
# b + 3 <= cは、作業Cを開始する前に、作業Bが完了している必要があることを表している
problem += a + 2 <= d
problem += b + 3 <= c

# 作業Cが完了した後、作業Eを開始する
# c + 4 <= eは、作業Eを開始する前に、作業Cが完了している必要があることを表している
problem += c + 4 <= e

# 作業DとEは同時に開始することができる
# d + 2 <= fは、作業Fを開始する前に、作業Dが完了している必要があることを表している
# e + 3 <= fは、作業Fを開始する前に、作業Eが完了している必要があることを表している
problem += d + 2 <= f
problem += e + 3 <= f

# 目的関数を定義する
problem += f

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

# 結果を表示する
print(f"Status: {pulp.LpStatus[status]}")
print(f"Optimal Solution:")
print(f"\ta: {a.value():.2f}")
print(f"\tb: {b.value():.2f}")
print(f"\tc: {c.value():.2f}")
print(f"\td: {d.value():.2f}")
print(f"\te: {e.value():.2f}")
print(f"\tf: {f.value():.2f}")

# それぞれの作業の開始時期を計算する
start_a = a.value()
start_b = b.value()
start_c = c.value()
start_d = d.value()
start_e = e.value()
start_f = f.value()

# 結果を表示する
print("Task Schedule:")
print(f"\tTask A starts at day {start_a:.2f}")
print(f"\tTask B starts at day {start_b:.2f}")
print(f"\tTask C starts at day {start_c:.2f}")
print(f"\tTask D starts at day {start_d:.2f}")
print(f"\tTask E starts at day {start_e:.2f}")
print(f"\tTask F starts at day {start_f:.2f}")

[実行結果]

Status: Optimal

Optimal Solution:

    a: 2.00

    b: 3.00

    c: 6.00

    d: 11.00

    e: 10.00

    f: 13.00

Task Schedule:

    Task A starts at day 2.00

    Task B starts at day 3.00

    Task C starts at day 6.00

    Task D starts at day 11.00

    Task E starts at day 10.00

    Task F starts at day 13.00

最適解は、作業の完了までの日数を最小化するようにスケジュールが設定された結果であり、最適なスケジュールでの各タスクの開始日を示しています。

都市最適化問題 PuLP

都市最適化問題

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

ある都市には5つの工場があり、それぞれが製品を生産しています。

各工場は、それぞれの製品を販売するための市場に製品を供給する必要があります。

この問題は、工場と市場をどのように結びつけるかを決定する問題となります。

解き方・ソースコード

この問題を解くために、PuLPを使用して以下のようにモデル化します。

[Google Colaboratory]

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
# 必要なライブラリをインポート
import pulp

# 問題を初期化
problem = pulp.LpProblem("Factory Market Problem", pulp.LpMinimize)

# 工場と市場の数
factories = ["Factory 1", "Factory 2", "Factory 3", "Factory 4", "Factory 5"]
markets = ["Market 1", "Market 2", "Market 3", "Market 4", "Market 5"]

# 各工場の製品の生産量(変数)
production = pulp.LpVariable.dicts("Production", factories, lowBound=0)

# 各市場に必要な製品の量(定数)
demand = {"Market 1": 20, "Market 2": 25, "Market 3": 30, "Market 4": 20, "Market 5": 15}

# 各工場が各市場に供給する製品の量(変数)
shipments = pulp.LpVariable.dicts("Shipments", (factories, markets), lowBound=0)

# 目的関数を設定
problem += pulp.lpSum([500 * production[i] for i in factories]) + pulp.lpSum([300 * shipments[i][j] for i in factories for j in markets])

# 制約条件を追加
for j in markets:
problem += pulp.lpSum([shipments[i][j] for i in factories]) == demand[j]

for i in factories:
problem += pulp.lpSum([shipments[i][j] for j in markets]) <= production[i]

# 問題を解く
problem.solve()

# 結果を表示
for i in factories:
for j in markets:
if shipments[i][j].value() > 0:
print(f"{i} から {j}{shipments[i][j].value()} だけ製品を供給します。")

この例では、5つの工場5つの市場があり、各市場に必要な製品の量が設定されています。

それぞれの工場の生産量と、各工場が各市場に供給する製品の量が決まっており、目的関数には生産コスト出荷コストが含まれています。

制約条件では、各市場に必要な製品の量が供給されていることと、各工場が生産した製品の量を超えて製品を出荷していないことを設定しています。


このモデルを実行すると、各工場が各市場に供給する製品の量が計算されます。

例えば、”Factory 2”から”Market 3”には30個の製品が供給され、”Factory 3”から”Market 2”には25個の製品が供給されるなど、全ての製品の供給量が計算されます。

このように、PuLPを使用して都市最適化問題を解くことができます。

実際の都市最適化問題では、工場や市場の数がより大きく、制約条件が複雑であることがありますが、PuLPはそれらの問題にも対応できます。

[実行結果]

Factory 2 から Market 3 へ 30.0 だけ製品を供給します。

Factory 2 から Market 5 へ 15.0 だけ製品を供給します。

Factory 3 から Market 2 へ 25.0 だけ製品を供給します。

Factory 4 から Market 1 へ 20.0 だけ製品を供給します。

Factory 4 から Market 4 へ 20.0 だけ製品を供給します。

この結果から、各工場が各市場に供給する製品の量が計算され、各市場に必要な量が供給されていることがわかります。

経済最適化問題 PuLP

経済最適化問題

あなたは、小売店を経営しています。

あなたの店には、リンゴ、バナナ、オレンジといった果物があります。

あなたは、これらの果物を卸売業者から購入し、顧客に販売します。

各果物の単価、在庫、需要量は以下の通りです。

  • リンゴ:単価100円、在庫200個、需要量100個
  • バナナ:単価80円、在庫150個、需要量50個
  • オレンジ:単価120円、在庫100個、需要量75個

最大の利益を得るためには、どの果物を何個購入し販売すべきでしょうか。

解き方・ソースコード

3種類の果物(リンゴ、バナナ、オレンジ)の購入・販売数を最適化する方法を考えます。

目的は、最大利益を得ることであり、利益は各果物の単価と数量の積の総和となります。

[Google Colaboratory]

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
from pulp import *

# ①各果物の単価、在庫、需要量を設定
prices = {"apple": 100, "banana": 80, "orange": 120}
inventory = {"apple": 200, "banana": 150, "orange": 100}
demand = {"apple": 100, "banana": 50, "orange": 75}

# ②変数
apple_qty = LpVariable("apple_qty", lowBound=0, cat='Integer')
banana_qty = LpVariable("banana_qty", lowBound=0, cat='Integer')
orange_qty = LpVariable("orange_qty", lowBound=0, cat='Integer')

# ③問題の定義
prob = LpProblem("Fruit Buying Problem", LpMaximize)

# ④目的関数
prob += prices["apple"] * apple_qty + prices["banana"] * banana_qty + prices["orange"] * orange_qty

# ⑤制約条件
prob += apple_qty <= inventory["apple"]
prob += banana_qty <= inventory["banana"]
prob += orange_qty <= inventory["orange"]
prob += apple_qty + banana_qty + orange_qty >= demand["apple"] + demand["banana"] + demand["orange"]
prob += apple_qty >= demand["apple"]
prob += banana_qty >= demand["banana"]
prob += orange_qty >= demand["orange"]

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

# ⑦結果の表示
print("Apple quantity: ", apple_qty.value())
print("Banana quantity: ", banana_qty.value())
print("Orange quantity: ", orange_qty.value())
print("Maximum profit: ", value(prob.objective))

①まず、各果物の単価、在庫、需要量を辞書型データで設定します。

これらの情報は、問題を解決するための制約条件として使用されます。

例えば、リンゴの在庫量は200であり、需要量は100であることが設定されています。


②次に、PuLPを使用して変数を作成します。

LpVariableを使用して、リンゴ、バナナ、オレンジの数量を表す3つの変数を作成しています。

これらの変数は、それぞれapple_qty、banana_qty、orange_qtyという名前で定義され、lowBoundパラメータを使用して0以上の整数値であることが保証されています。


③PuLPを使用して問題を定義します。

LpProblemを使用して、問題のタイトル(Fruit Buying Problem)と目的(最大化)を指定します。

④目的関数は、各果物の数量と単価の積の総和であり、prob += prices[“apple”] * apple_qtyという形式で追加されます。

⑤リンゴ、バナナ、オレンジの数量に関する制約条件を追加します。

例えば、リンゴの在庫量は apple_qty <= inventory[“apple”] の制約条件で表されます。

その他の制約条件には、需要量の制約や、リンゴ、バナナ、オレンジの数量を合計した値の制約が含まれます。


⑥PuLPを使用して問題を解きます。

⑦各果物の数量と最大利益を表示しています。

[実行結果]

Apple quantity:  200.0

Banana quantity:  150.0

Orange quantity:  100.0

Maximum profit:  44000.0

この結果から、リンゴ200個、バナナ150個、オレンジ100個を購入し、最大利益は44,000円であることがわかります。

飛行機スケジュール最適化問題 PuLP

飛行機スケジュール最適化問題

飛行機スケジュール最適化問題は、与えられた制約条件の下で、最小限のコストまたは最大限の利益で運ぶように航空便をスケジュールする問題です。

この問題に対する一つのアプローチは、線形プログラミングを用いることです。

PuLPは、Pythonで線形プログラミング問題を解くためのオープンソースのライブラリです。


以下に、飛行機スケジュール最適化問題の例を示します。

例:

ある航空会社が、3つの都市(A、B、C)を結ぶ便を運航しています。

各都市の間には、以下のような航空路があります。

  • 都市Aから都市Bへの便
  • 都市Aから都市Cへの便
  • 都市Bから都市Cへの便

各都市への到着時刻出発時刻フライト時間最大搭乗可能人数が与えられています。

以下の制約条件を満たしながら、最大限の利益を得るために、どのようなスケジュールを立てるかを決定する必要があります。

  • 各便の発着は、出発地の時刻よりも遅れることはできない。
  • 各便の出発時刻は、前の便の到着時刻よりも遅れることはできない。
  • 各便の乗客数は、最大搭乗可能人数を超えてはなりません。

解き方・ソースコード

PuLPを使って飛行機スケジュール最適化問題を解決しています。

[Google Colaboratory]

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
# ①pulpライブラリをインポート
import pulp

# ②制約条件を定義する
problem = pulp.LpProblem('Flight Schedule', pulp.LpMaximize)

# ③便ごとに決定変数を作成する
flight_AB = pulp.LpVariable('Flight_AB', lowBound=0, cat='Integer')
flight_AC = pulp.LpVariable('Flight_AC', lowBound=0, cat='Integer')
flight_BC = pulp.LpVariable('Flight_BC', lowBound=0, cat='Integer')

# ④目的関数を設定する
problem += 1000 * flight_AB + 1500 * flight_AC + 1200 * flight_BC

# ⑤制約条件を追加する
problem += flight_AB <= 50
problem += flight_AC <= 70
problem += flight_BC <= 80

problem += flight_AB <= 8
problem += flight_AC <= 11
problem += flight_BC <= 12

problem += flight_AB + flight_AC <= 70
problem += flight_AB + flight_BC <= 60
problem += flight_AC + flight_BC <= 75

# ⑥最適化を実行する
problem.solve()

# ⑦結果を表示する
print('Flight AB:', pulp.value(flight_AB))
print('Flight AC:', pulp.value(flight_AC))
print('Flight BC:', pulp.value(flight_BC))
print('Total profit:', pulp.value(problem.objective))

①PuLPライブラリをインポートしています。

②pulp.LpProblemを使用して、最適化問題を定義しています。

‘Flight Schedule’は問題の名前であり、pulp.LpMaximizeは目的関数を最大化することを示しています。

③各便に対して決定変数を作成します。

ここでは、都市Aと都市Bの間のフライトをflight_AB、都市Aと都市Cの間のフライトをflight_AC、都市Bと都市Cの間のフライトをflight_BCとしています。

各決定変数の値は、整数であることを指定し、0以上の値を取るように制限されています。

目的関数は、各便の利益の合計を設定します。

ここで、flight_AB、flight_AC、およびflight_BCは各便の数を表します。

制約条件を設定します。

これらの条件は、便の数に関する制限を設定するものです。

⑥problem.solve()で最適化問題を解きます。

⑦結果を表示しています。

pulp.valueを使用して、各便の数総利益を取得しています。

[実行結果]

Flight AB: 8.0

Flight AC: 11.0

Flight BC: 12.0

Total profit: 38900.0

この結果は、都市Aと都市Bの間に8便、都市Aと都市Cの間に11便、都市Bと都市Cの間に12便の便をスケジュールすることが最適であり、その結果、最大の利益である38900ドルを得ることができることを示しています。

クラウドコンピューティング最適化問題 PuLP

クラウドコンピューティング最適化問題

クラウドコンピューティングにおけるサーバー配置問題を例にして、PuLPを用いて解いていきます。

サーバー配置問題とは、複数のクラウドサービスを提供するために必要なサーバーを、最小限のコストで配置する問題です。

ここでは、各サーバーのスペックと利用料金が既知であると仮定します。

[Google Colaboratory]

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
from pulp import *

# 問題設定
servers = ["Server1", "Server2", "Server3"]
spec = {"Server1": {"CPU": 4, "Memory": 16, "Storage": 500},
"Server2": {"CPU": 2, "Memory": 8, "Storage": 250},
"Server3": {"CPU": 8, "Memory": 32, "Storage": 1000}}
costs = {"Server1": 100, "Server2": 50, "Server3": 200}
demands = {"CPU": 10, "Memory": 32, "Storage": 800}

# モデル定義
prob = LpProblem("Server Allocation Problem", LpMinimize)

# 変数定義
servers_vars = LpVariable.dicts("Server", servers, lowBound=0, upBound=None, cat="Integer")

# 目的関数
prob += lpSum([costs[s] * servers_vars[s] for s in servers])

# 制約条件
for r in spec["Server1"].keys():
prob += lpSum([spec[s][r] * servers_vars[s] for s in servers]) >= demands[r]

# 解法
prob.solve()

# 結果出力
print("Total cost: {}".format(value(prob.objective)))
for s in servers:
print("{}: {}".format(s, value(servers_vars[s])))

このコードでは、各サーバーのスペックをspec、利用料金をcosts、要求スペックをdemandsとして定義し、PuLPを用いてモデルを定義しています。

変数として各サーバーの割り当てをservers_varsと定義し、目的関数と制約条件を設定した後にprob.solve()を実行することで最適解を求め、結果を出力しています。

[実行結果]

Total cost: 250.0

Server1: 0.0

Server2: 1.0

Server3: 1.0

この結果から、サーバー2サーバー3それぞれ1台使用することで、最小のコストで要求スペックを満たすことができることがわかります。

また、この最適解においてかかるコストは250となります。

医療最適化問題 PuLP

医療最適化問題

ある病院における輸血の最適化問題を考えてみましょう。

具患者に輸血を行う場合、その輸血に使用する血液の種類によって、患者に不適切な血液を投与してしまうリスクがあります。

このため、輸血に使用する血液の種類を適切に選択することが重要です。

この問題をPuLPを使って解くためには、以下のような手順を踏みます。

  1. 必要なパッケージをインポートします。

    1
    from pulp import *
  2. 問題を定義します。

    1
    2
    # 問題を定義
    prob = LpProblem('Blood_Transfusion', LpMinimize)
  3. 変数を定義します。

    1
    2
    3
    4
    # 変数を定義
    blood_type_A = LpVariable('Blood_Type_A', lowBound=0, cat='Integer')
    blood_type_B = LpVariable('Blood_Type_B', lowBound=0, cat='Integer')
    blood_type_O = LpVariable('Blood_Type_O', lowBound=0, cat='Integer')
  4. 目的関数を定義します。

    1
    2
    # 目的関数を定義
    prob += 10 * blood_type_A + 20 * blood_type_B + 30 * blood_type_O

各血液の種類について、そのコストを10, 20, 30としました。

血液の種類がAの場合には10のコスト、Bの場合には20のコスト、Oの場合には30のコストがかかるということになります。

  1. 制約条件を定義します。
    1
    2
    3
    4
    5
    # 制約条件を定義
    prob += blood_type_A + blood_type_B + blood_type_O >= 100
    prob += blood_type_A >= 20
    prob += blood_type_B >= 30
    prob += blood_type_O >= 40

病院で必要な総血液量が100であること、血液の種類Aが20以上必要であること、血液の種類Bが30以上必要であること、血液の種類Oが40以上必要であることを制約条件として定義しています。

  1. 最適化を実行します。

    1
    2
    # 最適化を実行
    prob.solve()
  2. 結果を表示します。

    1
    2
    3
    4
    5
    # 結果を表示
    print('Blood Type A: ', blood_type_A.value())
    print('Blood Type B: ', blood_type_B.value())
    print('Blood Type O: ', blood_type_O.value())
    print('Total Cost: ', value(prob.objective))

完全なソースコード

以上の手順をすべて組み合わせた完全なコードは次のようになります。

[Google Colaboratory]

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
from pulp import *

# 問題を定義
prob = LpProblem('Blood_Transfusion', LpMinimize)

# 変数を定義
blood_type_A = LpVariable('Blood_Type_A', lowBound=0, cat='Integer')
blood_type_B = LpVariable('Blood_Type_B', lowBound=0, cat='Integer')
blood_type_O = LpVariable('Blood_Type_O', lowBound=0, cat='Integer')

# 目的関数を定義
prob += 10 * blood_type_A + 20 * blood_type_B + 30 * blood_type_O

# 制約条件を定義
prob += blood_type_A + blood_type_B + blood_type_O >= 100
prob += blood_type_A >= 20
prob += blood_type_B >= 30
prob += blood_type_O >= 40

# 最適化を実行
prob.solve()

# 結果を表示
print('Blood Type A: ', blood_type_A.value())
print('Blood Type B: ', blood_type_B.value())
print('Blood Type O: ', blood_type_O.value())
print('Total Cost: ', value(prob.objective))

[実行結果]

Blood Type A:  30.0
Blood Type B:  30.0
Blood Type O:  40.0
Total Cost:  2100.0

この結果から、血液の種類Aが30、種類Bが30、種類Oが40必要であり、総コストが2100であることが確認できました。

音楽推薦最適化問題 PuLP

音楽推薦最適化問題

音楽推薦の最適化問題PuLPを用いて解く例題を以下に示します。

この問題では、複数の楽曲を聴取したユーザーが、各楽曲に対して評価を付けたデータを利用して、ユーザーに最適な楽曲の推薦リストを作成します。

推薦リストは、ユーザーが楽曲を聴取するためにかかる時間の合計を最小限に抑えるように決定します。

[Google Colaboratory]

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
# PuLPをインポートする
from pulp import *

# データを定義する
users = ['User 1', 'User 2', 'User 3', 'User 4']
songs = ['Song 1', 'Song 2', 'Song 3', 'Song 4']
ratings = {
('User 1', 'Song 1'): 3,
('User 1', 'Song 2'): 4,
('User 1', 'Song 3'): 5,
('User 1', 'Song 4'): 2,
('User 2', 'Song 1'): 5,
('User 2', 'Song 2'): 4,
('User 2', 'Song 3'): 2,
('User 2', 'Song 4'): 3,
('User 3', 'Song 1'): 4,
('User 3', 'Song 2'): 5,
('User 3', 'Song 3'): 3,
('User 3', 'Song 4'): 4,
('User 4', 'Song 1'): 2,
('User 4', 'Song 2'): 3,
('User 4', 'Song 3'): 4,
('User 4', 'Song 4'): 5
}
time_limits = {
'User 1': 60,
'User 2': 90,
'User 3': 120,
'User 4': 60
}

# 問題を定義する
prob = LpProblem("Music Recommendation", LpMinimize)

# 変数を定義する
x = LpVariable.dicts('x', [(u, s) for u in users for s in songs], cat='Binary')

# 目的関数を定義する
prob += lpSum([x[(u, s)] * ratings[(u, s)] for u in users for s in songs])

# 制約条件を追加する
for u in users:
prob += lpSum([x[(u, s)] for s in songs]) <= len(songs) // 2, f"User {u} - Song count"
prob += lpSum([x[(u, s)] * ratings[(u, s)] for s in songs]) >= 3, f"User {u} - Minimum rating"
prob += lpSum([x[(u, s)] * ratings[(u, s)] for s in songs]) <= 5, f"User {u} - Maximum rating"
prob += lpSum([x[(u, s)] * ratings[(u, s)] for s in songs]) * 5 <= time_limits[u], f"User

[実行結果]

Status: Optimal

Total rating: 12.0

User 1 recommendations: ['Song 1']

User 2 recommendations: ['Song 4']

User 3 recommendations: ['Song 3']

User 4 recommendations: ['Song 2']

この結果から、各ユーザーにおすすめする曲を決定することができます。

たとえば、User 1 には ‘Song 1’ をおすすめすることができます。

また、最適化問題の総合評価が 12 であることから、ユーザーが評価する曲の総合的な評価を最大化しつつ、制約条件を満たす最適な曲の選択を行っていることがわかります。

グラフ理論の例題 NetworkX

NetworkX

PythonのライブラリであるNetworkXは、グラフ理論を扱う際に便利なツールです。

このライブラリを使うことで、グラフ理論の様々な問題を解決することができます。

例えば、グラフの可視化グラフの検索グラフの解析などが挙げられます。

1. 最短経路を求める

以下のようなグラフがあるとします。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
import networkx as nx
import matplotlib.pyplot as plt

G = nx.DiGraph()
G.add_edge('A', 'B', weight=4)
G.add_edge('A', 'C', weight=2)
G.add_edge('B', 'D', weight=5)
G.add_edge('C', 'D', weight=1)

nx.draw(G, with_labels=True)
plt.show()

[実行結果]

このグラフでは、ノード同士を結ぶエッジには重みが設定されています。

このグラフで、ノードAからノードDへの最短経路を求めるには、以下のコードを実行します。

[Google Colaboratory]

1
2
shortest_path = nx.shortest_path(G, source='A', target='D', weight='weight')
print("最短経路は", shortest_path, "です。")

このコードでは、nx.shortest_path()を用いて最短経路を求めています。

weight=’weight’を指定することで、エッジの重みを考慮した最短経路が求められます。

[実行結果]

最短経路は ['A', 'C', 'D'] です。

2. 最小全域木を求める

以下のようなグラフがあるとします。

[Google Colaboratory]

1
2
3
4
5
6
7
8
import networkx as nx
import matplotlib.pyplot as plt

G = nx.Graph()
G.add_weighted_edges_from([('A', 'B', 4), ('A', 'H', 8), ('B', 'C', 8), ('B', 'H', 11), ('C', 'D', 7), ('C', 'F', 4), ('C', 'I', 2), ('D', 'E', 9), ('D', 'F', 14), ('E', 'F', 10), ('F', 'G', 2), ('G', 'H', 1), ('G', 'I', 6), ('H', 'I', 7)])

nx.draw(G, with_labels=True)
plt.show()

[実行結果]

このグラフは、9つの頂点と14本の辺を持つ重み付きグラフです。

このグラフの最小全域木を求めるには、以下のコードを実行します。

[Google Colaboratory]

1
2
T = nx.minimum_spanning_tree(G)
print("最小全域木の辺は", T.edges(data=True), "です。")

このコードでは、nx.minimum_spanning_tree()を用いて最小全域木を求めています。

求められた最小全域木の辺は、辞書形式で返されます。

[実行結果]

最小全域木の辺は [('A', 'B', {'weight': 4}), ('A', 'H', {'weight': 8}), ('H', 'G', {'weight': 1}), ('C', 'I', {'weight': 2}), ('C', 'F', {'weight': 4}), ('C', 'D', {'weight': 7}), ('D', 'E', {'weight': 9}), ('F', 'G', {'weight': 2})] です。

まとめ

今回は、NetworkXを用いてグラフ理論の問題を解決する例題を2つ挙げてみました。

NetworkXは非常に豊富な機能を持っているため、様々な問題に対して適用することができます。

グラフ理論 NetworkX

NetworkX

NetworkXは、グラフ理論を用いた問題を解決するためのPythonライブラリです。

以下に、NetworkXで解決できる問題の例です。

🔹グラフの構築や可視化
🔹グラフの特徴量の計算(次数中心性、媒介中心性、近接中心性など)
🔹グラフの操作(ノードやエッジの追加・削除、グラフのサブグラフの抽出など)
🔹最短経路や最短距離の計算
🔹ネットワーク分析や社会ネットワーク分析の実行
🔹グラフの可視化やレイアウトの設定

これらの機能を使って、例えば以下のような問題を解決することができます。

🔹ソーシャルメディアのネットワーク分析による、情報拡散や影響力の分析
🔹輸送網や交通網の最適ルートの検索や交通量の分析
🔹電力網や水道網の効率的な運営のためのネットワーク解析
🔹化学反応のシミュレーションや、生物学的ネットワークの解析
🔹ウェブページのリンク構造の解析や、検索エンジンのランキングアルゴリズムの開発

なお、これらはあくまでも一例であり、グラフ理論が応用される領域は多岐に渡ります。

最短経路問題

最短経路問題とは、あるグラフ上で2つの頂点間の最短経路を求める問題です。

NetworkXライブラリには、最短経路を求めるための関数が用意されています。

以下は、最短経路を求める例として、グラフ上の2つの頂点の最短経路を求めるコードです。

[Google Colaboratory]

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
import networkx as nx
import matplotlib.pyplot as plt

# グラフの定義
G = nx.Graph()
G.add_edge(0, 1, weight=4)
G.add_edge(0, 2, weight=3)
G.add_edge(1, 3, weight=2)
G.add_edge(2, 3, weight=5)
G.add_edge(2, 4, weight=6)
G.add_edge(3, 4, weight=1)

# 最短経路の計算
shortest_path = nx.shortest_path(G, source=0, target=4, weight='weight')

# 結果の表示
print(f"The shortest path from node 0 to node 4 is: {shortest_path}")

# グラフの可視化
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos, node_color='lightblue')
nx.draw_networkx_edges(G, pos)
nx.draw_networkx_labels(G, pos)
edge_labels = {(u, v): d['weight'] for u, v, d in G.edges(data=True)}
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.axis('off')
plt.show()

この例では、グラフGを定義して、nx.shortest_path()関数を使って、頂点0から頂点4までの最短経路を計算しています。

nx.shortest_path()関数は、グラフと、最短経路の始点と終点を指定する引数を取り、最短経路を表す頂点のリストを返します。


次に、計算結果を表示し、最後にグラフを可視化しています。

グラフの可視化には、matplotlibライブラリを使用しています。


nx.spring_layout()関数は、グラフのレイアウトを計算する関数であり、nx.draw_networkx_nodes()nx.draw_networkx_edges()nx.draw_networkx_labels()nx.draw_networkx_edge_labels()関数は、それぞれ、ノード、エッジ、ラベル、エッジラベルを描画するための関数です。

[実行結果]