与我的其他问题here部分相关。
在我的案例中,“原始”目标是从N = 292中选择n = 50个对象,以使所选对象之间的所有成对距离的总和最大化(maxsum或p色散总和)。
由于提供了建议的用户,我做了一些进一步的阅读,现在我了解到,问题的确以最简单的形式二次出现,像CPLEX这样的求解器也许可以解决它。
但是,库比的this article指出maxsum结果不能保证不会存在彼此非常接近的对象;确实,通过对模拟的较小案例进行的蛮力测试,我发现maxsum很高的解决方案有时包含非常接近的对象。
所以现在我在考虑p分散(maxmin)方法可能更适合我想要实现的目标。这最初也是一个二次问题。
由于我还没有CPLEX,因此无法尝试二次公式,因此我研究了线性化方法。这2篇文章对我来说似乎很有趣:
Franco, Uchoa
Sayah, 2015
后者指向另一篇文章,我也觉得很有趣:
Pisinger, 2006
下一步是尝试以下操作:
我没有尝试收紧下限或增加更多的不平等,因为文章中建议的方法超出了我的数学水平。
让我感到困惑的是,方法4应该是“紧凑的”,实际上具有大量的二进制变量和随之而来的约束,在我进行的测试中,它的性能比方法1和2差得多。另一方面,上限产生了很大的影响,实际上,目前的方法2是唯一似乎能够在合理的时间内解决大型问题的方法。
但是,我确实没有完全实现Sayah论文中的方法,所以也许我的观察是无效的。
问题:您如何看待这些文章中介绍的各种线性化方法?你能建议更好的吗?您认为像Kuby的公式那样将最大最小距离保持为连续变量比像Sayah的公式那样将其“量化”更好吗?
实际上,与此同时,进一步的复杂性和发展出现了。是否存在“强迫”对象以及需要为每个对象使用分数,但我想首先解决上述问题。
我将其粘贴到用于测试此代码的R代码下面。
谢谢!
#Test of linearized methods for the solution of p-dispersion (maxmin) problems
#-----------------------------------------------------------------------------
#Definitions
#Given N objects, whose distance matrix 'distmat' is available:
#p-dispersion (maxmin): select n (n >= 2, n < N) objects such that the minimal distance between any two objects is maximised
#p-dispersion sum (maxsum): select n (n >= 2, n < N) objects such that the sum of all the pairwise distances between them is maximised
#Literature
#Kuby, 1987: https://onlinelibrary.wiley.com/doi/abs/10.1111/j.1538-4632.1987.tb00133.x
#Pisinger, 1999: https://pdfs.semanticscholar.org/1eb3/810077c0af9d46ed5ff2b0819d954c97dcae.pdf
#Pisinger, 2006: http://yalma.fime.uanl.mx/~roger/work/teaching/clase_tso/docs_project/problems/PDP/cor-2006-Pisinger.pdf
#Franco, Uchoa: https://pdfs.semanticscholar.org/4092/d2c98cdb46d5d625a580bac08fcddc4c1e60.pdf
#Sayah, 2015: https://download.uni-mainz.de/RePEc/pdf/Discussion_Paper_1517.pdf
#Initialization
require(Matrix)
if (length(find.package(package="Rsymphony",quiet=TRUE))==0) install.packages("Rsymphony")
require(Rsymphony)
par(mfrow = c(2,2))
#0. Choose N, n and which methods to run
N = 20
n = ceiling(0.17*N)
run_PD_Erkut = TRUE
run_PD_brute_force = TRUE
run_PD_Erkut_UB_Sayah = TRUE
run_PD_Sayah = TRUE
#1. Make random distance matrix for testing
set.seed(1)
coords <- cbind(runif(N,-5,5),runif(N,-5,5))
distmat <- t(as.matrix(dist(coords,diag=T)))
distmat[lower.tri(distmat)] <- 0
distmat <- Matrix(distmat,sparse=T)
N.i <- NROW(distmat)
colnames(distmat) <- paste("j",1:N.i,sep="_")
rownames(distmat) <- paste("i",1:N.i,sep="_")
#2. Make a 2D representation of the points using classic multidimensional scaling
cmds <- cmdscale(as.dist(t(distmat)))
#3. Link the pairwise distances to the rows and columns of the distmat
distmat_summary <- summary(distmat)
N.ij <- NROW(distmat_summary)
distmat_summary["ID"] <- 1:(N.ij)
i.mat <- xtabs(~ID+i,distmat_summary,sparse=T)
j.mat <- xtabs(~ID+j,distmat_summary,sparse=T)
ij.mat <- cbind(i.mat,0)+cbind(0,j.mat)
colnames(ij.mat)[[N.i]] <- as.character(N.i)
zij.mat <- .sparseDiagonal(n=N.ij,x=1)
#4. MaxMin task by Kuby/Erkut (N binary variables + 1 continuous variable for max Dmin)
if (run_PD_Erkut == TRUE) {
#4a. Building the constraint matrix (mat), direction (dir), right-hand-side (rhs) and objective (obj) for the LP task
dij <- distmat_summary$x
M <- max(dij)
m <- min(dij)
#Erkut's condition: for each i,j i<j, D (min distance to maximise) + M*xi + M*xj <= 2*M + dij
constr.dij <- cbind("D"=1,ij.mat*M)
dir.dij <- rep("<=",N.ij)
rhs.dij <- 2*M+dij
constr.D <- c(1,rep(0,N.i))
dir.DM <- "<="
rhs.DM <- M
dir.Dm <- ">="
rhs.Dm <- m
#constraining the total number of objects to be n
constr.n <- c(0,rep(1,N.i))
dir.n <- "=="
rhs.n <- n
#assembling the constraints
mat <- rbind(constr.n,constr.dij,constr.D,constr.D)
dir <- c(dir.n,dir.dij,dir.DM,dir.Dm)
rhs <- c(rhs.n,rhs.dij,rhs.DM,rhs.Dm)
#objective
obj <- setNames(c(1,rep(0,N.i)), c("D",colnames(ij.mat)))
#4.b. Solution
st <- system.time(LP.sol <- Rsymphony_solve_LP(obj,mat,dir,rhs,types=c("C",rep("B",N.i)),max=TRUE,verbosity = -2, time_limit = 5*60))
ij.sol <- names(obj[-1])[as.logical(LP.sol$solution[-1])]
items.sol <- rownames(distmat)[as.numeric(ij.sol)]
Dmin <- LP.sol$solution[1]
#4.c. Plotting the results
plot(cmds,main=paste(c("p-dispersion (Erkut), N =",N,", n =",n,"\nUB =",round(M,2),", time =",round(st[3],2),"s, Dmin =",round(Dmin,2)),collapse=" ") )
points(cmds[as.numeric(ij.sol),],pch=16,col="red")
text(cmds[as.numeric(ij.sol),],ij.sol,cex=0.9,col="red",adj=c(0,1))
}
#5. MaxMin task by brute force
if (run_PD_brute_force == TRUE) {
if (choose(N,n) <= 200000) {
st <- system.time({combs <- as.data.frame(t(combn(N,n)))
combs["maxmin"] <- apply(combs, 1, function(x) {min(distmat_summary[(distmat_summary$j %in% x) & (distmat_summary$i %in% x),"x"])})
combs["maxsum"] <- apply(combs, 1, function(x) {sum(distmat_summary[(distmat_summary$j %in% x) & (distmat_summary$i %in% x),"x"])})
combs_maxmin_max <- combs[combs$maxmin == max(combs$maxmin),][1,]})
ij.sol <- as.character(combs_maxmin_max[,1:n])
items.sol <- rownames(distmat)[as.numeric(ij.sol)]
Dmin <- combs_maxmin_max[1,"maxmin"]
plot(cmds,main=paste(c("p-dispersion (brute force), N =",N,", n =",n,"\ntime =",round(st[3],2),"s, Dmin =",round(Dmin,2)),collapse=" ") )
points(cmds[as.numeric(ij.sol),],pch=16,col="red")
text(cmds[as.numeric(ij.sol),],ij.sol,cex=0.9,col="red",adj=c(0,1))
}
}
#6. MaxMin task by Erkut with Sayah's upper bound
if (run_PD_Erkut_UB_Sayah == TRUE) {
#6a. Building the constraint matrix (mat), direction (dir), right-hand-side (rhs) and objective (obj) for the LP task
m <- min(distmat_summary$x)
M <- sort(sapply(1:(N.i), function(it) {min((sort(distmat_summary[(distmat_summary$i == it) | (distmat_summary$j == it),"x"],decreasing = TRUE)[1:(n-1)]))}),decreasing=TRUE)[n]
#Erkut's condition: for each i,j i<j, D (min distance to maximise) + M*xi + M*xj <= 2*M + dij
constr.dij <- cbind("D"=1,ij.mat*M)
dir.dij <- rep("<=",N.ij)
rhs.dij <- 2*M+dij
constr.D <- c(1,rep(0,N.i))
dir.DM <- "<="
rhs.DM <- M
dir.Dm <- ">="
rhs.Dm <- m
#constraining the total number of objects to be n
constr.n <- c(0,rep(1,N.i))
dir.n <- "=="
rhs.n <- n
#assembling the constraints
mat <- rbind(constr.n,constr.dij,constr.D,constr.D)
dir <- c(dir.n,dir.dij,dir.DM,dir.Dm)
rhs <- c(rhs.n,rhs.dij,rhs.DM,rhs.Dm)
#objective
obj <- setNames(c(1,rep(0,N.i)), c("D",colnames(ij.mat)))
#6.b. Solution
st <- system.time(LP.sol <- Rsymphony_solve_LP(obj,mat,dir,rhs,types=c("C",rep("B",N.i)),max=TRUE,verbosity = -2, time_limit = 5*60))
ij.sol <- names(obj[-1])[as.logical(LP.sol$solution[-1])]
items.sol <- rownames(distmat)[as.numeric(ij.sol)]
Dmin <- LP.sol$solution[1]
#6.c. Plotting the results
plot(cmds,main=paste(c("p-dispersion (Erkut, UB by Sayah), N =",N,", n =",n,"\nUB =",round(M,2),", time =",round(st[3],2),"s, Dmin =",round(Dmin,2)),collapse=" ") )
points(cmds[as.numeric(ij.sol),],pch=16,col="red")
text(cmds[as.numeric(ij.sol),],ij.sol,cex=0.9,col="red",adj=c(0,1))
}
#7. MaxMin task by Sayah (N binary variables + binary variables from unique values of dij)
if (run_PD_Sayah == TRUE) {
#7a. Building the constraint matrix (mat), direction (dir), right-hand-side (rhs) and objective (obj) for the LP task
#7a.1. Finding the upper (M) and lower (m) bound for the minimal distance
m <- min(distmat_summary$x)
M <- sort(sapply(1:(N.i), function(it) {min((sort(distmat_summary[(distmat_summary$i == it) | (distmat_summary$j == it),"x"],decreasing = TRUE)[1:(n-1)]))}),decreasing=TRUE)[n]
dijs <- unique(sort(distmat_summary$x))
dijs <- dijs[dijs <= M]
N.dijs <- length(dijs)
z.mat <- .sparseDiagonal(N.dijs,1)
#Sayah's formulation:
#applying z[k] <= z[k-1]
constr.z <- cbind(rep(0,N.i*(N.dijs-1)),cbind(0,z.mat[-1,-1])-z.mat[-NROW(z.mat),])
dir.z <- rep("<=",N.dijs-1)
rhs.z <- rep(0,N.dijs-1)
#applying x[i]+x[j]+z[k] <= 2
constr.ijk <- NULL
for (k in 2:N.dijs) {
IDs <- distmat_summary[distmat_summary$x < dijs[k],"ID"]
constr.ijk <- rbind(constr.ijk,cbind(ij.mat[IDs,,drop=F],z.mat[rep(k,length(IDs)),,drop=F]))
}
dir.ijk <- rep("<=",NROW(constr.ijk))
rhs.ijk <- rep(2,NROW(constr.ijk))
#constraining the total number of objects to be n
constr.n <- c(rep(1,N.i),rep(0,N.dijs))
dir.n <- "=="
rhs.n <- n
#assembling the constraints
mat <- rbind(constr.n,constr.z,constr.ijk)
dir <- c(dir.n,dir.z,dir.ijk)
rhs <- c(rhs.n,rhs.z,rhs.ijk)
#objective
obj <- setNames(c(rep(0,N.i),1,diff(dijs)), c(colnames(ij.mat),paste("z",1:N.dijs,sep="_")))
#7.b. Solution
st <- system.time(LP.sol <- Rsymphony_solve_LP(obj,mat,dir,rhs,types="B",max=TRUE,verbosity = -2, time_limit = 5*60))
ij.sol <- names(obj[1:N.i])[as.logical(LP.sol$solution[1:N.i])]
items.sol <- rownames(distmat)[as.numeric(ij.sol)]
Dmin <- sum(LP.sol$solution[(1+N.i):(N.dijs+N.i)]*obj[(1+N.i):(N.dijs+N.i)])
#7.c. Plotting the results
plot(cmds,main=paste(c("p-dispersion (Sayah), N =",N,", n =",n,"\nUB =",round(M,2),", time =",round(st[3],2),"s, Dmin =",round(Dmin,2)),collapse=" ") )
points(cmds[as.numeric(ij.sol),],pch=16,col="red")
text(cmds[as.numeric(ij.sol),],ij.sol,cex=0.9,col="red",adj=c(0,1))
}
答案 0 :(得分:0)
您没有提到是否可以容忍非最佳解决方案。但是您应该能够,因为您不能期望能够通常找到针对此问题的最佳解决方案。在这种情况下,存在2因子近似值。
Let V be the set of nodes/objects
Let i and j be two nodes at maximum distance
Let p be the number of objects to choose
p = set([i,j])
while size(P)<p:
Find a node v in V-P such that min_{v' in P} dist(v,v') is maximum
\That is: find the node with the greatest minimum distance to the set P
P = P.union(v)
Output P
保证该近似算法可以找到不超过最优值两倍的值,除非P = NP,否则多项式时间启发式算法都不能提供更好的性能保证。
在White (1991)和Ravi et al. (1994)中证明了最优边界。后者证明了启发式方法是最好的。
作为参考,我为p = 50,n = 400运行了完整的MIP。 6000s之后,最佳差距仍然是568%。逼近算法需要0.47s的时间才能获得100%(或更少)的最优间隙。
逼近算法的Python(对不起,我没有在R中建模)表示如下:
#!/usr/bin/env python3
import numpy as np
p = 50
N = 400
print("Building distance matrix...")
d = np.random.rand(N,N) #Random matrix
d = (d + d.T)/2 #Make the matrix symmetric
print("Finding initial edge...")
maxdist = 0
bestpair = ()
for i in range(N):
for j in range(i+1,N):
if d[i,j]>maxdist:
maxdist = d[i,j]
bestpair = (i,j)
P = set()
P.add(bestpair[0])
P.add(bestpair[1])
print("Finding optimal set...")
while len(P)<p:
print("P size = {0}".format(len(P)))
maxdist = 0
vbest = None
for v in range(N):
if v in P:
continue
for vprime in P:
if d[v,vprime]>maxdist:
maxdist = d[v,vprime]
vbest = v
P.add(vbest)
print(P)
Gurobi Python表示可能如下所示:
#!/usr/bin/env python
import numpy as np
import gurobipy as grb
p = 50
N = 400
print("Building distance matrix...")
d = np.random.rand(N,N) #Random matrix
d = (d + d.T)/2 #Make the matrix symmetric
m = grb.Model(name="MIP Model")
used = [m.addVar(vtype=grb.GRB.BINARY) for i in range(N)]
objective = grb.quicksum( d[i,j]*used[i]*used[j] for i in range(0,N) for j in range(i+1,N) )
m.addConstr(
lhs=grb.quicksum(used),
sense=grb.GRB.EQUAL,
rhs=p
)
# for maximization
m.ModelSense = grb.GRB.MAXIMIZE
m.setObjective(objective)
# m.Params.TimeLimit = 3*60
# solving with Glpk
ret = m.optimize()