まず、考えられる解ごとに入力、出力、および測定値を指定して、最適化問題を形式化しましょう (これがあなたの興味に合うことを願っています)。
正の整数の配列Aと正の整数Pが与えられた場合、各部分配列の合計と部分配列の完全な合計 (sum( A )/ P ) の差が最小になるように、配列AをP個の重複しない部分配列に分割します。 .
入力:正の整数の配列A。Pは正の整数です。
出力: A の各部分配列の長さを表すP個の非負整数の配列SA 。これらの部分配列の長さの合計はAの長さと等しくなります。尺度: abs(sum( sa )-sum( A )/ P ) は、各sa ∈ { sa | sa = ( A i , …, A i +<em>SA j ) for i = (Σ SA
j )、0 からP -1 までのj }。
入力と出力は、有効なソリューションのセットを定義します。メジャーは、複数の有効なソリューションを比較するためのメジャーを定義します。また、完全解との差が最小となる解 (最小化問題) を探しているため、測度も最小にする必要があります。
この情報があれば、measure
関数を実装するのは非常に簡単です (ここでは Python で):
def measure(a, sa):
sigma = sum(a)/len(sa)
diff = 0
i = 0
for j in xrange(0, len(sa)):
diff += abs(sum(a[i:i+sa[j]])-sigma)
i += sa[j]
return diff
print measure([2,4,6,7,6,3,3,3,4,3,4,4,4,3,3,1], [3,4,4,5]) # prints 8
現在、最適解を見つけるのは少し難しくなっています。
バックトラッキング アルゴリズムを使用して有効なソリューションを見つけ、メジャー関数を使用してそれらを評価できます。基本的に、すべての可能な有効な解を表すために、合計で length( A ) になるP個の非負の整数の可能な組み合わせをすべて試します。これにより、有効なソリューションを見逃すことはありませんが、基本的には力ずくのアプローチであり、最善のソリューションよりも優れているとは言えないいくつかのブランチを省略できるという利点があります。たとえば、上記の例では、すでに≤ 38のソリューションがある場合、[9,…] (メジャー> 38)でソリューションをテストする必要はありません。
ウィキペディアの擬似コード パターンに従うと、bt
関数は次のようになります。
def bt(c):
global P, optimum, optimum_diff
if reject(P,c):
return
if accept(P,c):
print "%r with %d" % (c, measure(P,c))
if measure(P,c) < optimum_diff:
optimum = c
optimum_diff = measure(P,c)
return
s = first(P,c)
while s is not None:
bt(list(s))
s = next(P,s)
グローバル変数P
、optimum
、およびは、 A、P、およびsigmaの値と、最適解とその尺度をoptimum_diff
保持する問題インスタンスを表します。
class MinimalSumOfSubArraySumsProblem:
def __init__(self, a, p):
self.a = a
self.p = p
self.sigma = sum(a)/p
次に、非常に単純なreject
andaccept
関数を指定します。
def reject(P,c):
return optimum_diff < measure(P,c)
def accept(P,c):
return None not in c
これは、測定値がすでに最適なソリューションを超えている候補を単純に拒否します。そして、私たちは有効な解決策を受け入れています。
値を含めることができるようになったため、measure
関数もわずかに変更されています。c
None
def measure(P, c):
diff = 0
i = 0
for j in xrange(0, P.p):
if c[j] is None:
break;
diff += abs(sum(P.a[i:i+c[j]])-P.sigma)
i += c[j]
return diff
残りの 2 つの機能はfirst
、next
もう少し複雑です。
def first(P,c):
t = 0
is_complete = True
for i in xrange(0, len(c)):
if c[i] is None:
if i+1 < len(c):
c[i] = 0
else:
c[i] = len(P.a) - t
is_complete = False
break;
else:
t += c[i]
if is_complete:
return None
return c
def next(P,s):
t = 0
for i in xrange(0, len(s)):
t += s[i]
if i+1 >= len(s) or s[i+1] is None:
if t+1 > len(P.a):
return None
else:
s[i] += 1
return s
基本的に、リストの次の値がリストの最後の値でない場合はリストの次の値をfirst
置き換えるか、リストの最後の値の場合は有効なソリューションを表す残りの値に置き換えます (ここでは少し最適化します)。リストに値がありません。単純に右端の整数を 1 だけインクリメントするか、インクリメントが合計制限に違反する場合は戻ります。None
0
None
None
next
None
bt
問題のインスタンスを作成し、グローバル変数を初期化し、ルートで呼び出すだけです。
P = MinimalSumOfSubArraySumsProblem([2,4,6,7,6,3,3,3,4,3,4,4,4,3,3,1], 4)
optimum = None
optimum_diff = float("inf")
bt([None]*P.p)