5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go と Python で解き比べ

Last updated at Posted at 2024-12-10

TL;DR

Python ではじめる数理最適化(第2版) の 第2章 P 17 の問題を Go と Python で解き比べました。

問題設定

ある工場では製品 $p$ と $q$ を製造しています。製品 $p$ と $q$ を製造するには原料 $m$ と $n$ が必要で、次のことがわかっています。

  • 製品 $p$ を $1$ kg 製造するには、原料 $m$ が $1$ kg、原料 $n$ が $2$ kg 必要である。
  • 製品 $q$ を $1$ kg 製造するには、原料 $m$ が $3$ kg、原料 $n$ が $1$ kg 必要である。
  • 原料 $m$ の在庫は $30$ kg、原料 $n$ の在庫は $40$ kg である。
  • 単位量あたりの利得は、製品 $p$ は $1$ 万円、製品 $q$ は $2$ 万円である。

この条件で利得を最大にするためには、製品 $p$ と $q$ をそれぞれ何 kg 製造すればよいでしょうか。

PuLP で解いてみるよ

こちらの解説は、ぜひ書籍をお買い求めいただいてお確かめくださいー

import pulp

problem = pulp.LpProblem("LP", pulp.LpMaximize)

x = pulp.LpVariable('x', cat='Continuous')
y = pulp.LpVariable('y', cat='Continuous')

problem += 1 * x + 3 * y <= 30
problem += 2 * x + 1 * y <= 40
problem += x >= 0
problem += y >= 0
problem += x + 2 * y

status = problem.solve()

print('Status:', pulp.LpStatus[status])
print('x= ', x.value(), 'y= ', y.value(), 'obj= ', problem.objective.value())
Status: Optimal
x=  18.0 y=  4.0 obj=  26.0

Go で解いてみるよ

gonum (https://gonum.org) といういろいろ数値計算ができるライブラリがあります。
今回はその中から、lp パッケージを使用して Simplex Algorithm により、解いてみます。

package main

import (
	"fmt"
	"log"

	"gonum.org/v1/gonum/mat"
	"gonum.org/v1/gonum/optimize/convex/lp"
)

func main() {
	c := []float64{1, 2, 0, 0}
	A := mat.NewDense(2, 4, []float64{1, 3, 1, 0, 2, 1, 0, 1})
	b := []float64{30, 40}

	// 目的関数の最大化のため、c に -1 をかける
	for i, v := range c {
		c[i] = -1 * v
	}
	z, s, err := lp.Simplex(c, A, b, 0, nil)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("x= %v y= %v obj= %v\n", s[0], s[1], z*-1)
}
x= 18 y= 4 obj= 26

PuLP は Maximize で解いているのに対して、Go は Minimize で解いているので、目的関数の値が異なりますが、
等しい解を得ることができました!

Simplex Algorithm について

https://pkg.go.devドキュメントを読むと。。。

Strang, Gilbert. "Linear Algebra and Applications." Academic, New York (1976). の第8章に説明があって、lectures 11-13 of UC Math 352 の動画へのリンクがあります。
ふむふむ

問題文から立式するよ

$$
\displaylines{
maximize \quad 1 \cdot x + 2 \cdot y \newline
s.t. \quad x \geq 0 \newline
\quad y \geq 0 \newline
\quad 1 \cdot x + 3 \cdot y \leq 30 \newline
\quad 2 \cdot x + 1 \cdot y \leq 40 \newline
}
$$

  • maximize : 目的関数
  • s.t. : 制約条件

グラフで描画

Screenshot from 2024-12-11 04-09-48.png

標準形にする

ここで、非負の値を取るスラック変数 (slack vairables) $s_1$ と $s_2$ を導入します。

$$
\displaylines{
z = 1 \cdot x + 2 \cdot y \newline
1 \cdot x + 3 \cdot y + s_1 = 30 \newline
2 \cdot x + 1 \cdot y + s_2 = 40 \newline
x, y, s_1, s_2 \geq 0
}
$$

目的関数の式変形

目的関数 $z$ を最大化するために変形します。

$$
\displaylines{
z - 1 \cdot x - 2 \cdot y = 0 \newline
1 \cdot x + 3 \cdot y + s_1 = 30 \newline
2 \cdot x + 1 \cdot y + s_2 = 40 \newline
x, y, s_1, s_2 \geq 0
}
$$

行列で表すと

$$
\displaylines{
\boldsymbol{A} = \begin{pmatrix}
1 & 3 & 1 & 0 \newline
2 & 1 & 0 & 1
\end{pmatrix} \newline
\boldsymbol{s} = \begin{pmatrix}
x \newline
y \newline
s_1 \newline
s_2 \newline
\end{pmatrix}
\newline
\boldsymbol{b} = \begin{pmatrix}
30 \newline
40
\end{pmatrix} \newline
\boldsymbol{c} = \begin{pmatrix}
1 \newline
2 \newline
0 \newline
0 \newline
\end{pmatrix}
}
$$

として

最大化:

$$
\boldsymbol{c}^T \cdot \boldsymbol{s} =
\begin{pmatrix}
1 & 2 & 0 & 0
\end{pmatrix}
\cdot
\begin{pmatrix}
x \newline
y \newline
s_1 \newline
s_2 \newline
\end{pmatrix}
$$

条件:

$$
\boldsymbol{A} \cdot \boldsymbol{s} = \boldsymbol{b}, \quad \boldsymbol{s} \geq 0
$$

$$
\begin{pmatrix}
1 & 3 & 1 & 0 \newline
2 & 1 & 0 & 1
\end{pmatrix}
\cdot
\begin{pmatrix}
x \newline
y \newline
s_1 \newline
s_2 \newline
\end{pmatrix} =
\begin{pmatrix}
30 \newline
40
\end{pmatrix}
$$

のように表すことができます。

制約条件に変数導入

制約条件にいくつかの変数を導入します。

  • $\boldsymbol{s_N}$ : 非基底変数 (non-basic variables) を表す行列
  • $\boldsymbol{s_B}$ : 基底変数 (basic variables) を表す行列
  • $\boldsymbol{N}$ : 制約条件の係数行列 $\boldsymbol{A}$ のうち非基底変数の係数の部分行列
  • $\boldsymbol{B}$ : 制約条件の係数行列 $\boldsymbol{A}$ のうち基底変数の係数の部分行列

変数の内容は以下です

$$
\displaylines{
\boldsymbol{s_N} =
\begin{pmatrix}
x \newline
y \newline
\end{pmatrix}, \quad
\boldsymbol{s_B} =
\begin{pmatrix}
s_1 \newline
s_2 \newline
\end{pmatrix} \newline
\boldsymbol{N} =
\begin{pmatrix}
1 & 3 \newline
2 & 1
\end{pmatrix}, \quad
\boldsymbol{B} =
\begin{pmatrix}
1 & 0 \newline
0 & 1
\end{pmatrix}
}
$$

そうすると、$\boldsymbol{A} \cdot \boldsymbol{s}$ は次のようにあらわせられます。

$$
\displaylines{
\boldsymbol{A} \cdot \boldsymbol{s} =
\begin{pmatrix}
1 & 3 & 1 & 0 \newline
2 & 1 & 0 & 1
\end{pmatrix}
\cdot
\begin{pmatrix}
x \newline
y \newline
s_1 \newline
s_2 \newline
\end{pmatrix} \newline
= \begin{pmatrix}
1 & 3 \newline
2 & 1
\end{pmatrix}
\cdot
\begin{pmatrix}
x \newline
y \newline
\end{pmatrix}
+
\begin{pmatrix}
1 & 0 \newline
0 & 1
\end{pmatrix}
\cdot
\begin{pmatrix}
s_1 \newline
s_2 \newline
\end{pmatrix} \newline
= \boldsymbol{N}\cdot\boldsymbol{s_N} + \boldsymbol{B}\cdot\boldsymbol{s_B}
}
$$

よって

$$
\boldsymbol{N}\cdot\boldsymbol{s_N} + \boldsymbol{B}\cdot\boldsymbol{s_B} = \boldsymbol{b}
$$

目的関数にに変数導入

  • $\boldsymbol{c_N}$ : 非基底変数の係数行列
  • $\boldsymbol{c_B}$ : 基底変数の係数行列

$\boldsymbol{c}$ は次のように分解できます。

$$
\displaylines{
\boldsymbol{c_N} =
\begin{pmatrix}
1 \newline
2 \newline
\end{pmatrix}, \quad
\boldsymbol{c_B} =
\begin{pmatrix}
0 \newline
0 \newline
\end{pmatrix}
}
$$

すると、$\boldsymbol{c}^T \cdot \boldsymbol{s}$ は次のようにあらわせられます。

$$
\displaylines{
\boldsymbol{c}^T \cdot \boldsymbol{s} =
\begin{pmatrix}
1 & 2 & 0 & 0
\end{pmatrix}
\cdot
\begin{pmatrix}
x \newline
y \newline
s_1 \newline
s_2 \newline
\end{pmatrix} \newline
= \begin{pmatrix}
1 & 2
\end{pmatrix}
\cdot
\begin{pmatrix}
x \newline
y
\end{pmatrix}
+
\begin{pmatrix}
0 & 0
\end{pmatrix}
\cdot
\begin{pmatrix}
s_1 \newline
s_2
\end{pmatrix} \newline
= \boldsymbol{c_N}^T \cdot \boldsymbol{s_N} + \boldsymbol{c_B}^T \cdot \boldsymbol{s_B}
}
$$

よって

$$
z = \boldsymbol{c_N}^T \cdot \boldsymbol{s_N} + \boldsymbol{c_B}^T \cdot \boldsymbol{s_B}
$$

ところで
以下の式の両辺に左から $\boldsymbol{B}^{-1}$ をかけると

$$
\displaylines{
\boldsymbol{N}\cdot\boldsymbol{s_N} + \boldsymbol{B} \cdot \boldsymbol{s_B} = \boldsymbol{b} \newline
\boldsymbol{B}^{-1} \cdot \boldsymbol{N}\cdot\boldsymbol{s_N} + \boldsymbol{B}^{-1} \cdot \boldsymbol{B} \cdot \boldsymbol{s_B} = \boldsymbol{B}^{-1} \cdot \boldsymbol{b} \newline
\boldsymbol{B}^{-1} \cdot \boldsymbol{N}\cdot\boldsymbol{s_N} + \boldsymbol{s_B} = \boldsymbol{B}^{-1} \cdot \boldsymbol{b} \newline
\boldsymbol{s_B} = \boldsymbol{B}^{-1} \cdot \boldsymbol{b} - \boldsymbol{B}^{-1} \cdot \boldsymbol{N}\cdot\boldsymbol{s_N} \newline
}
$$

これを先程の式に代入すると、

$$
\displaylines{
z = \boldsymbol{c_N}^T \cdot \boldsymbol{s_N} + \boldsymbol{c_B}^T \cdot \boldsymbol{s_B} \newline
= \boldsymbol{c_N}^T \cdot \boldsymbol{s_N} + \boldsymbol{c_B}^T \cdot \left( \boldsymbol{B}^{-1} \cdot \boldsymbol{b} - \boldsymbol{B}^{-1} \cdot \boldsymbol{N}\cdot\boldsymbol{s_N} \right) \newline
= \boldsymbol{c_N}^T \cdot \boldsymbol{s_N} + \boldsymbol{c_B}^T \cdot \boldsymbol{B}^{-1} \cdot \boldsymbol{b} - \boldsymbol{c_B}^T \cdot\boldsymbol{B}^{-1} \cdot \boldsymbol{N} \cdot \boldsymbol{s_N} \newline
= \boldsymbol{c_B}^T \cdot \boldsymbol{B}^{-1} \cdot \boldsymbol{b} \left( \boldsymbol{c_N}^T - \boldsymbol{c_B}^T \cdot\boldsymbol{B}^{-1} \cdot \boldsymbol{N} \right) \boldsymbol{s_N} \newline
}
$$

ここで

$$
\boldsymbol{s_N} = \begin{pmatrix}
x \newline
y
\end{pmatrix}
= \begin{pmatrix}
0 \newline
0
\end{pmatrix}
$$

と固定すると、

$$
\displaylines{
\boldsymbol{s_B} = \begin{pmatrix}
s_1 \newline
s_2
\end{pmatrix}
= \boldsymbol{B}^{-1} \cdot \boldsymbol{b} \newline
= \begin{pmatrix}
1 & 0 \newline
0 & 1
\end{pmatrix}
\cdot
\begin{pmatrix}
30 \newline
40
\end{pmatrix} \newline
= \begin{pmatrix}
30 \newline
40
\end{pmatrix}
}
$$

となり、$\boldsymbol{s_B}$ が求まります。
また、

  • $\boldsymbol{b}^\prime = \boldsymbol{B}^{-1} \cdot \boldsymbol{b}$
  • $\boldsymbol{c_N}^\prime = \boldsymbol{c_N}^T - \boldsymbol{c_B}^T \cdot\boldsymbol{B}^{-1} \cdot \boldsymbol{N}$
  • $\boldsymbol{N}^\prime = \boldsymbol{B}^{-1} \cdot \boldsymbol{N}$

とおくと、

  • $\boldsymbol{z} = \boldsymbol{c_B}^T \cdot \boldsymbol{b}^\prime + \boldsymbol{c_N}^\prime \cdot \boldsymbol{s_N}$
  • $\boldsymbol{s_B} = \boldsymbol{b}^\prime - \boldsymbol{N}^\prime \cdot \boldsymbol{s_N}$

となります。

$\boldsymbol{c_N}^\prime$ は非基底変数の係数ベクトルであり、被約費用とよばれます。
$\boldsymbol{c_N}^\prime$ の各要素は、対応する非基底変数の値を $1$ 増加させたときに目的変数の改善料を表します。

ここで、$\boldsymbol{c_N}^\prime$ の各要素を $c_1^\prime, \quad c_2^\prime$ とし、対応する非基底変数を $x, y$ と置きます。
このとき、目的関数の係数が正となる非基底変数が存在する場合、すなわち $c_k^\prime > 0$ となる $c_1, \quad c_2, \quad x, \quad y$ が存在する場合は、
$x, \quad y$ を増加させることで、目的関数の値が改善します。

非基底変数のいずれかを選んで、選ばなかった日基底変数を $0$ に固定したまま、選んだ非基底変数を増加させます。
ここで、$\boldsymbol{a_y}$ を非基底変数 $y$ に対応する $\boldsymbol{N}^\prime$ の列ベクトルとすると

$$
\displaylines{
\boldsymbol{a_y} = \begin{pmatrix}
\end{pmatrix}
3 \newline
1
}
$$

となります。

非基底変数 $y$ の値を $ y = \theta$ まで増加させた場合の目的関数と基底変数はそれぞれ、

$$
\displaylines{
\boldsymbol{z} = \boldsymbol{c_B}^T \cdot \boldsymbol{b}^\prime + c_2^\prime \cdot \theta \newline
= (0 0) \cdot
\begin{pmatrix}
1 & 0 \newline
0 & 1
\end{pmatrix}
\cdot
\begin{pmatrix}
30 \newline
40
\end{pmatrix}
+
2 \theta \newline
= 2 \theta
}
$$

$$
\displaylines{
\boldsymbol{s_B} = \boldsymbol{b}^\prime - \theta \cdot \boldsymbol{a_y}
}
$$

参考文献

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?