线性规划:我可以制定一个目标来一次最大化多个变量吗?

时间:2018-10-25 09:55:33

标签: algorithm mathematical-optimization linear-programming or-tools

假设我有以下系统说明的一些变量和约束: enter image description here 灰色线可以拉伸和收缩由其顶部范围给出的量。蓝线只是端点,显示了灰线如何相互作用。

我的目标:我想使用线性编程来均匀地最大化灰线的大小,如图所示。您可以想象带有弹簧的灰色线条,它们都同样向外推。一个糟糕的解决方案是将所有蓝线尽可能地移到一侧。请注意,此描述中有一些余地,并且可能有多种解决方案-我需要的是使它们合理地均匀,并且没有一个最大值可以压榨其他所有参数。

我尝试过的目标函数只是使行的总和最大化:

maximize: (B - A) + (C - B) + (C - A) + (D - C) + (E - B) + (E - D) + (F - E) + (F - D) + (F - A) 

对我来说,这显然不是一个好的解决方案,因为取消了这些条件,并且增加一条线只会使另一条线减少相同的量,因此目标永远不会加权到最大化分配上。变量。

我还尝试最小化每条线与中间可能范围的距离。对于第B - A行,在(1,3)范围内的中间值为2。这是第一个术语的目标:

minimize: |(B - A) - 2| + ...

要实现绝对值,我用U代替了该术语,并增加了其他约束:

minimize: U + ...
with: U <= (B - A - 2)
      U <= -(B - A - 2)

这与另一个目标存在相同的问题:差异始终与另一行差异的变化成比例。我想如果能求平方差就行了,但是我不能在线性求解器中输入。

是否有一些目标函数可以实现我所寻求的目标,或者线性求解器不是解决此问题的正确工具?

如果有帮助,我正在使用Google或工具。

以下是约束条件:

 1 <= B - A <= 3
 0 <= C - B <= 1
 1 <= C - A <= 4
 9 <= E - B <= 11
 7 <= D - C <= 11
 0 <= E - D <= 1
 3 <= F - E <= 5
 3 <= F - D <= 6
15 <= F - A < = 15

1 个答案:

答案 0 :(得分:1)

请记住,您最大的问题是,您确切地不知道想要什么。所以我不得不猜测。有时看到一些猜测可以帮助您完善所需的内容,虽然这对您来说并不算太糟糕,但确实使您对本网站格式的疑问更加困难。

首先,我假设可以将弹簧建模为有向无环图。也就是说,我可以将所有弹簧替换为指向右侧的箭头。永远不会有从右到左的箭头(否则您的弹簧会弯曲成一个圆)。

完成此操作后,您可以使用set逻辑找出最左边的蓝色条的标识。 (我假设只有一个-剩下的练习是弄清楚如何进行概括。)然后,您可以将该栏固定在合适的位置。所有其他钢筋将相对于其定位。此约束看起来像:

S[leftmost] = 0

现在,我们需要一些约束条件。

每个边i都有一个源和端点(因为边是有方向的)。调用源点S的位置和端点E的位置。此外,边缘具有最小长度l和最大长度L。由于我们固定了最左边的蓝条的位置,因此连接到它的弹簧定义了它们的端点落入的间隔。这些端点是其他弹簧&c的源点。因此,每个边缘在其端点的位置上定义了两个约束。

S[i]+l[i] <= E[i]
E[i]      <= S+L[i]

顺便说一句,请注意,我们现在可以制定一个简单的线性程序:

min 1
s.t.  S[leftmost]  = 0
      S[i]+l[i]   <= E[i]
      E[i]        <= S+L[i]

如果该程序可以解决,那么您可以找到一个可行的解决方案。也就是说,条形的长度并不会相互矛盾地描述蓝色条的位置。

现在,无论什么意思,我们都希望“使灰线的大小均匀地最大化”。

最小化与平均长度的偏差

这是一个主意。程序为每个小节选择的长度由E[i]-S[i]给出。让我们指定该长度应“接近”钢筋(L[i]+l[i])/2的平均长度。因此,我们要为每个条形图最小化的目标数量是:

(E[i]-S[i])-(L[i]+l[i])/2

问题在于,根据(E[i]-S[i])>(L[i]+l[i])/2是否为正或负。这不是很好,因为我们希望将与(L[i]+l[i])/2的偏差减至最小,该值应始终为正。

为解决这个问题,让我们对值求平方,然后取平方根,得到:

sqrt(((E[i]-S[i])-(L[i]+l[i])/2)^2)

这似乎无法解决,但请和我在一起。

请注意,上述内容与采用一元向量的L2范数相同,因此我们可以将其重写为:

|(E[i]-S[i])-(L[i]+l[i])/2|_2

我们现在可以对每个条形的偏差求和:

|(E[0]-S[0])-(L[0]+l[0])/2|_2 + |(E[1]-S[1])-(L[1]+l[1])/2|_2 + ...

这给我们带来了以下优化问题:

min |(E[0]-S[0])-(L[0]+l[0])/2|_2 + |(E[1]-S[1])-(L[1]+l[1])/2|_2 + ...
s.t.  S[leftmost]  = 0
      S[i]+l[i]   <= E[i]
      E[i]        <= S+L[i]

这个问题很难以上述形式解决,但是我们可以通过引入变量t

来执行简单的操作
min   t[0] + t[1] + ...
s.t.  S[leftmost]  = 0
      S[i]+l[i]   <= E[i]
      E[i]        <= S+L[i]
      |(E[i]-S[i])-(L[i]+l[i])/2|_2<=t[i]

此问题与先前的问题完全相同。那我们获得了什么?

优化是将问题转换为标准形式的游戏。以标准形式出现问题后,我们就可以站在巨人的肩膀上,并使用强大的工具来解决我们的问题。

上述操作已使问题变成second-order cone problem (SOCP)。一旦采用这种形式,就可以直接解决。

执行此操作的代码如下:

#!/usr/bin/env python3

import cvxpy as cp
import networkx as nx
import matplotlib.pyplot as plt

def FindTerminalPoints(springs):
  starts = set([x[0] for x in springs.edges()])
  ends   = set([x[1] for x in springs.edges()])
  return list(starts-ends), list(ends-starts)

springs = nx.DiGraph()
springs.add_edge('a', 'b', minlen= 1, maxlen= 3)
springs.add_edge('a', 'c', minlen= 1, maxlen= 4)
springs.add_edge('a', 'f', minlen=15, maxlen=15)
springs.add_edge('b', 'c', minlen= 0, maxlen= 1)
springs.add_edge('b', 'e', minlen= 9, maxlen=11)
springs.add_edge('c', 'd', minlen= 7, maxlen=11)
springs.add_edge('d', 'e', minlen= 0, maxlen= 1)
springs.add_edge('d', 'f', minlen= 3, maxlen= 6)
springs.add_edge('e', 'f', minlen= 3, maxlen= 5)

if not nx.is_directed_acyclic_graph(springs):
  raise Exception("Springs must be a directed acyclic graph!")

starts, ends = FindTerminalPoints(springs)
if len(starts)!=1:
  raise Exception("One unique start is needed!")

if len(ends)!=1:
  raise Exception("One unique end is needed!")  

start = starts[0]
end   = ends[0]

#At this point we have what is essentially a directed acyclic graph beginning at
#`start` and ending at `end`

#Generate a variable for the position of each blue bar
bluevars = {n: cp.Variable(name=n) for n in springs.nodes()}
dvars    = {e: cp.Variable()       for e in springs.edges()}
#Anchor the leftmost blue bar to prevent pathological solutions
cons   = [bluevars[start]==0]
for s,e in springs.edges():
  print("Loading edge {0}-{1}".format(s,e))
  sv   = bluevars[s]
  ev   = bluevars[e]
  edge = springs[s][e]
  cons += [sv+edge['minlen']<=ev]
  cons += [ev<=sv+edge['maxlen']]
  cons += [cp.norm((ev-sv)-(edge['maxlen']-edge['minlen'])/2,2)<=dvars[(s,e)]]

obj  = cp.Minimize(cp.sum(list(dvars.values())))
prob = cp.Problem(obj,cons)

val = prob.solve()

fig, ax = plt.subplots()
for var, val in bluevars.items():
  print("{:10} = {:10}".format(var,val.value))
  plt.plot([val.value,val.value],[0,3])

plt.show()

结果如下:

Location of terminii in spring optimization problem

如果要“手动调整”蓝色条,可以通过添加权重w[i]来修改我们已经建立的优化问题。

min   w[0]*t[0] + w[1]*t[1] + ...
s.t.  S[leftmost]  = 0
      S[i]+l[i]   <= E[i]
      E[i]        <= S+L[i]
      |(E[i]-S[i])-(L[i]+l[i])/2|_2<=t[i]

w[i]越大,所讨论的弹簧接近其平均长度就越重要。

使受约束的蓝色条形之间的平方距离最小化

使用与上述相同的策略,我们可以最小化假设某种已知顺序的蓝色条之间的平方距离。这导致:

min   t[0] + t[1] + ...
s.t.  S[leftmost]  = 0
      S[i]+l[i]   <= E[i]
      E[i]        <= S+L[i]
      |(S[i]-S[i+1])/2|_2<=t[i]

在下面的代码中,我首先找到蓝色条的可行位置,然后假定它们映射到所需的顺序。用更准确的信息代替这种启发式方法是一个好主意。

#!/usr/bin/env python3

import cvxpy as cp
import networkx as nx
import matplotlib.pyplot as plt

def FindTerminalPoints(springs):
  starts = set([x[0] for x in springs.edges()])
  ends   = set([x[1] for x in springs.edges()])
  return list(starts-ends), list(ends-starts)

springs = nx.DiGraph()
springs.add_edge('a', 'b', minlen= 1, maxlen= 3)
springs.add_edge('a', 'c', minlen= 1, maxlen= 4)
springs.add_edge('a', 'f', minlen=15, maxlen=15)
springs.add_edge('b', 'c', minlen= 0, maxlen= 1)
springs.add_edge('b', 'e', minlen= 9, maxlen=11)
springs.add_edge('c', 'd', minlen= 7, maxlen=11)
springs.add_edge('d', 'e', minlen= 0, maxlen= 1)
springs.add_edge('d', 'f', minlen= 3, maxlen= 6)
springs.add_edge('e', 'f', minlen= 3, maxlen= 5)

if not nx.is_directed_acyclic_graph(springs):
  raise Exception("Springs must be a directed acyclic graph!")

starts, ends = FindTerminalPoints(springs)
if len(starts)!=1:
  raise Exception("One unique start is needed!")

if len(ends)!=1:
  raise Exception("One unique end is needed!")  

start = starts[0]
end   = ends[0]

#At this point we have what is essentially a directed acyclic graph beginning at
#`start` and ending at `end`

#Generate a variable for the position of each blue bar
bluevars = {n: cp.Variable(name=n) for n in springs.nodes()}

#Anchor the leftmost blue bar to prevent pathological solutions
cons   = [bluevars[start]==0]

#Constraint each blue bar to its range
for s,e in springs.edges():
  print("Loading edge {0}-{1}".format(s,e))
  sv   = bluevars[s]
  ev   = bluevars[e]
  edge = springs[s][e]
  cons += [sv+edge['minlen']<=ev]
  cons += [ev<=sv+edge['maxlen']]

#Find feasible locations for the blue bars. This is a heuristic for getting a
#sorted order for the bars
obj  = cp.Minimize(1)
prob = cp.Problem(obj,cons)

prob.solve()

#Now that we have a sorted order, we modify the objective to minimize the
#squared distance between the ordered bars
bar_locs = list(bluevars.values())
bar_locs.sort(key=lambda x: x.value)

dvars = [cp.Variable() for n in range(len(springs.nodes())-1)]
for i in range(len(bar_locs)-1):
  cons += [cp.norm(bar_locs[i]-bar_locs[i+1],2)<=dvars[i]]

obj  = cp.Minimize(cp.sum(dvars))
prob = cp.Problem(obj,cons)

val = prob.solve()

fig, ax = plt.subplots()
for var, val in bluevars.items():
  print("{:10} = {:10}".format(var,val.value))
  plt.plot([val.value,val.value],[0,3])

plt.show()

看起来像这样:

Ordered lines in optimization problem

使所有蓝色条之间的平方距离最小化,受约束

我们也可以尝试最小化蓝色条之间的所有成对平方距离。在我看来,这似乎是最好的结果。

min   t[i,j] + ...                 for all i,j
s.t.  S[leftmost]        = 0
      S[i]+l[i]         <= E[i]    for all i
      E[i]              <= S+L[i]  for all i
      |(S[i]-S[j])/2|_2 <= t[i,j]  for all i,j

它看起来像这样:

#!/usr/bin/env python3

import cvxpy as cp
import networkx as nx
import matplotlib.pyplot as plt
import itertools

def FindTerminalPoints(springs):
  starts = set([x[0] for x in springs.edges()])
  ends   = set([x[1] for x in springs.edges()])
  return list(starts-ends), list(ends-starts)

springs = nx.DiGraph()
springs.add_edge('a', 'b', minlen= 1, maxlen= 3)
springs.add_edge('a', 'c', minlen= 1, maxlen= 4)
springs.add_edge('a', 'f', minlen=15, maxlen=15)
springs.add_edge('b', 'c', minlen= 0, maxlen= 1)
springs.add_edge('b', 'e', minlen= 9, maxlen=11)
springs.add_edge('c', 'd', minlen= 7, maxlen=11)
springs.add_edge('d', 'e', minlen= 0, maxlen= 1)
springs.add_edge('d', 'f', minlen= 3, maxlen= 6)
springs.add_edge('e', 'f', minlen= 3, maxlen= 5)

if not nx.is_directed_acyclic_graph(springs):
  raise Exception("Springs must be a directed acyclic graph!")

starts, ends = FindTerminalPoints(springs)
if len(starts)!=1:
  raise Exception("One unique start is needed!")

if len(ends)!=1:
  raise Exception("One unique end is needed!")  

start = starts[0]
end   = ends[0]

#At this point we have what is essentially a directed acyclic graph beginning at
#`start` and ending at `end`

#Generate a variable for the position of each blue bar
bluevars = {n: cp.Variable(name=n) for n in springs.nodes()}

#Anchor the leftmost blue bar to prevent pathological solutions
cons   = [bluevars[start]==0]

#Constraint each blue bar to its range
for s,e in springs.edges():
  print("Loading edge {0}-{1}".format(s,e))
  sv   = bluevars[s]
  ev   = bluevars[e]
  edge = springs[s][e]
  cons += [sv+edge['minlen']<=ev]
  cons += [ev<=sv+edge['maxlen']]

dist_combos = list(itertools.combinations(springs.nodes(), 2))
dvars       = {(na,nb):cp.Variable() for na,nb in dist_combos}
distcons    = []
for na,nb in dist_combos:
  distcons += [cp.norm(bluevars[na]-bluevars[nb],2)<=dvars[(na,nb)]]

cons += distcons

#Find feasible locations for the blue bars. This is a heuristic for getting a
#sorted order for the bars
obj  = cp.Minimize(cp.sum(list(dvars.values())))
prob = cp.Problem(obj,cons)

val = prob.solve()

fig, ax = plt.subplots()
for var, val in bluevars.items():
  print("{:10} = {:10}".format(var,val.value))
  plt.plot([val.value,val.value],[0,3])

plt.show()

看起来像这样:

Unordered lines in optimization problem