首先,让我们通过为每个可能的解决方案指定输入、输出和度量来形式化您的优化问题(我希望这符合您的兴趣):
给定一个正整数数组A和一个正整数P,将数组A分成P个不重叠的子数组,使得每个子数组的和与子数组的完美和 (sum( A )/ P ) 之间的差异最小.
输入:正整数数组A;P是一个正整数。
输出:由P个非负整数组成的数组SA表示A的每个子数组的长度,其中这些子数组长度的总和等于A的长度。度量: abs(sum( sa )-sum( A )/ P ) 对于每个sa ∈ { sa |是最小的 sa = ( A i , ..., A i +<em>SA j ) 对于i = (Σ SA
j ), j从 0 到P -1}。
输入和输出定义了一组有效的解决方案。度量定义了比较多个有效解决方案的度量。而且由于我们正在寻找与完美解决方案(最小化问题)差异最小的解决方案,因此度量也应该是最小的。
有了这些信息,就很容易实现这个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
现在找到一个最佳解决方案有点困难。
我们可以使用回溯算法来寻找有效的解决方案,并使用度量函数对它们进行评分。我们基本上尝试所有可能的P个非负整数的组合,总和为 length( A ),以表示所有可能的有效解决方案。尽管这确保不会错过有效的解决方案,但它基本上是一种蛮力方法,其好处是我们可以省略一些不能比我们最好的解决方案更好的分支。例如,在上面的例子中,如果我们已经有了一个measure ≤ 38的解,我们就不需要用 [9,…] ( measure > 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
和optimum_diff
表示保存A、P和sigma值的问题实例,以及最优解及其度量:
class MinimalSumOfSubArraySumsProblem:
def __init__(self, a, p):
self.a = a
self.p = p
self.sigma = sum(a)/p
接下来我们指定非常简单的reject
和函数:accept
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
剩下的两个函数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
替换为表示有效解决方案的余数(这里很少优化),或者如果存在则返回列表中没有值。只需将最右边的整数加一,如果增量超出总限制,则返回。None
0
None
None
next
None
现在您只需要创建一个问题实例,初始化全局变量并bt
使用 root 调用:
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)