我搜索了此内容,但找不到任何看起来匹配的内容;如果有人能帮忙或指出正确的方向,那就太好了。
假设您有一个数值向量h = c(-1,-2,3,5,6,9)
(我正在使用R)。
您对该向量的3个元素进行2个不同的选择。取每个选择的总和,然后取两个总和之间的差。
例如。一个选择可以是c(-1,3,5)
->总和为7;另一个c(-2,3,9)
->总和为10;差异是10-7 = 3。
您想知道:
对于像这样的小向量,您可以蛮力破解,得到两个和之间的所有可能差值,然后查找所需的值。
h = c(-1,-2,3,5,6,9)
N <- length(h)
n <- 3
vs <- expand.grid(rep(list(0:1),2*N))
vs["np1"] <- rowSums(vs[,1:N])
vs["np2"] <- rowSums(vs[,(N+1):(2*N)])
vs <- vs[(vs$np1 == n) & (vs$np2 == n),]
vs["hSum1"] <- apply(vs,1,function(x) sum(h*x[1:N]))
vs["hSum2"] <- apply(vs,1,function(x) sum(h*x[(N+1):(2*N)]))
vs["hdiff"] <- vs$hSum2-vs$hSum1
max(vs$hdiff)
#[1] 20
min(vs[vs$hdiff > 0,"hdiff"])
#[1] 1
显然,对于较大的向量来说这是不可能的。
我发现可以通过简单地对h
进行排序(升序)并取其最后3个元素和前3个元素之间的差异来找到最大的差异:
sum(sort(h)[(N-n+1):(N)]-sort(h)[1:n])
#[1] 20
但是,我想不出最小的阳性差异的解决方案。
我尝试了线性编程,但是我找不到找到将严格的不等式强加于差值上的技巧,这是避免将0作为解决方案所必需的。
从这个意义上讲,this post在概念上似乎很接近,但是我无法理解它,因为我看不到方法的来源,并且在不理解它的情况下应用它时,没用。
有什么想法吗?
谢谢!
编辑-可能的LP解决方案,以找到最小的正差
通过迭代。
令Dmin
为所寻求的差异。首先,找到Dmin
的上限,作为h
唯一元素之间的最小成对差异:
h <- c(2.1,1,-0.5,0,1.7,2.3)
N <- length(h)
n <- 3
min.hdiff <- min(diff(sort(unique(h))))
UB <- min.hdiff
UB
#[1] 0.2
下限可以设置为0:
LB <- 0
在这种情况下,很明显UB
不是Dmin
;可以用蛮力检查Dmin
是否为0.1。
为了通过LP重现该问题,我首先定义一个查找 max (不是 min ) positive 差异的函数。在LB和UB之间:
obj <- c(h,-h)
constr.n1 <- c(rep(1,N),rep(0,N))
constr.n2 <- c(rep(0,N),rep(1,N))
dir.n <- "=="
rhs.n <- n
constr.D <- c(h,-h)
dir.D1 <- ">="
rhs.D1 <- LB
dir.D2 <- "<="
rhs.D2 <- UB
mat <- rbind(constr.n1,constr.n2,constr.D,constr.D)
dir <- c(dir.n,dir.n,dir.D1,dir.D2)
rhs <- c(rhs.n,rhs.n,rhs.D1,rhs.D2)
N.rhs <- length(rhs)
require(Rsymphony)
DS.feas <- function(LB,UB) {
rhs[c(N.rhs-1,N.rhs)] <- c(LB,UB)
LP.sol <- Rsymphony_solve_LP(obj,mat,dir,rhs,types="B",max=T)
if ((LP.sol$status == 0) & (LP.sol$objval > 0)) {return(list(1,LP.sol$solution,LP.sol$objval))} else {return(list(0,NULL,LP.sol$objval))}
}
然后我检查初始[LB,UB]是否可行:
LB.feas <- DS.feas(LB,UB)
LB.feas
#[[1]]
#[1] 1
#
#[[2]]
# [1] 0 1 0 1 0 1 1 1 0 1 0 0
#
#[[3]]
#[1] 0.2
由于当前的[LB,UB]
是可行的,因此在下一次迭代中,我将LB
和UB
(MB
)之间的中点作为新的假定UB
进行测试。 :
MB = (LB+UB)/2
MB
#[1] 0.1
MB.feas = DS.feas(LB,MB)
MB.feas
#[[1]]
#[1] 1
#
#[[2]]
# [1] 0 1 0 1 1 0 1 1 1 0 0 0
#
#[[3]]
#[1] 0.1
可行。因此,我将UB
设置为MB
并测试了新的较低(但仍为正)的中点:
UB = MB
MB = (LB+UB)/2
MB
[1] 0.05
MB.feas = DS.feas(LB,MB)
#MB.feas
#[[1]]
#[1] 0
#
#[[2]]
#NULL
#
#[[3]]
#[1] 0
不可行。因此Dmin
必须在当前MB
和UB
之间。我将LB
设置为MB
并运行下一个迭代。
依次类推,直到实现收敛为止。
我在各种载体上进行了测试。
似乎可行;仅N = 20
就已经变得非常慢。
如果有人可以建议如何做得更好...
答案 0 :(得分:1)
我不知道r,但是我敢肯定您可以提出与此Python解决方案相同的问题:
from pulp import *
vals = [-1,-2,3,5,6,9]
r = range(0, len(vals))
#group1[i] = 1 if we use vals[i] in first group
group1 = LpVariable.dicts('group1', r, cat='Binary')
group2 = LpVariable.dicts('group2', r, cat='Binary')
#repeats[i] = 1 if we allow to use vals[i] in both groups
repeats = LpVariable.dicts('repeats', r, cat='Binary')
prob = LpProblem('test', LpMinimize)
sum1 = lpSum([group1[i] * vals[i] for i in r])
sum2 = lpSum([group2[i] * vals[i] for i in r])
#objective
prob += sum1 - sum2
#make group1 be highest sum to prevent negative solutions
prob += sum1 >= sum2
#make each group have 3 items
prob += lpSum([group1[i] for i in r]) == 3
prob += lpSum([group2[i] for i in r]) == 3
#dont allow choosing same number for both groups if we are not allowing repeat
for i in r:
prob += group1[i] + group2[i] <= repeats[i] + 1
#only allow up to 2 repeats (thus preventing same solution repeating 3)
prob += lpSum([repeats[i] for i in r]) <= 2
prob.solve()
print([vals[x] for x in r if group1[x].value()], [vals[x] for x in r if group2[x].value()])
编辑:只需重新阅读您的问题,似乎您不想允许解决方案0。在这种情况下,它甚至更容易,可以删除与重复项相关的所有内容,只需将第一个限制更改为:< / p>
#avoid solution 0
prob += sum1 - sum2 >= 1
答案 1 :(得分:0)
最小的差异实际上是分区问题https://en.wikipedia.org/wiki/Partition_problem
最大的差异是一个子集中的所有负数,另一个子集中的正数