首先,让我们通过指定输入、输出和每个可能解决方案的度量来形式化您的优化问题(我希望这符合您的利益):
给定一个由正整数组成的数组A和一个正整数P,将数组A分成P个不重叠的子数组,使得每个子数组的和与子数组的理论和(sum(A)/P)之间的差异最小。
输入:一个由正整数组成的数组A和一个正整数P。
输出:一个由P个非负整数组成的数组SA,表示A的每个子数组的长度,使得这些子数组的长度之和等于A的长度。
度量:对于每个sa ∈ {sa | sa = (A_i, …, A_i+SA_j) for i = (Σ SA_j), j从0到P-1},abs(sum(sa)-sum(A)/P)最小。
输入和输出定义了有效解集。度量定义了比较多个有效解的度量方式。由于我们正在寻找与完美解之间差异最小的解(最小化问题),因此度量也应该是最小的。
有了这些信息,实现测量函数就非常容易了(这里使用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])
现在,找到一个最优解有点困难。
我们可以使用
回溯算法来寻找有效的解,并使用
measure函数对其进行评分。我们基本上尝试所有可能的非负整数数字组合,这些数字之和等于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
这只是拒绝那些度量已经超过我们最优解的候选者。而我们接受任何有效的解决方案。
由于c现在可以包含None值,因此measure函数也略有改变。
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
函数:
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)
(14,14,14,14,19)
的不良度为20,而(15,14,16,14,16)
的不良度为4。当然,你可以尝试调整指数。 - user824425