上一节大纲
Hololens Anchor 自动布局(一)—— 背景、问题以及可行性分析
讲到,求解ILP的一个比较好的求解器就是CPLEX。
CPLEX,也就是IBM ILOG CPLEX Optimization Studio
官方网站有正式版本,收费
但是!但是IBM有一个Global University Programs。
通过学校分配的邮箱可以在IBM注册会员并免费使用其软件
注册完后,就可以免费使用了(第一次体会到了学校邮箱的作用)
Click Download,然后选择对应的操作系统就行了
(总感觉IBM的按钮按起来很带感,也可能是free的原因)
在Windows下的CPLEX库是DLL文件,命名方式为:cplexXXX.dll ,
其中 XXX 代表的当前的版本号。在Windows下可以通过多种方式定位cplexXXX.dll
Name : PATH
Value : %PATH%;你的CPLEX路径\CPLEXXXX\bin\X86_win32
由于是WPF程序,我直接把该文件夹下的文件拷贝到了我的项目目录下。在项目中
CPLEX 的 官方API手册下载
针对.Net用户的 官方API下载
数据结构 DataStruct.cs
using ILOG.Concert;
using ILOG.CPLEX;
using System.Collections;
namespace mynamespace
{
#region patch190120 by bxy
class BaoSCP
{
public List<Anchor> anchors;
//该问题和集合覆盖问题等价,锚点相当于子集,模型相当于集合里的元素
//一个锚点可以覆盖多个模型,相当于,一个字集可以包含多个元素
public int x_length; //锚点的个数
public int v_length; //模型的个数
public int[] x_weight; //每个空间锚的权重 (设为1,即每多放置一个空间锚,总成本+1)
public int[] x_min; //下限为0 代表不使用该时间锚
public int[] x_max; //上限为1 代表 使用该时间锚
public int[] v_min; //每个模型的下界为1,即至少有一个空间锚覆盖它
public int[] v_max; //每个模型的上界无界限(官方示例程序设为9999),即所有空间锚都可以覆盖它
public int[][] a;//第i个锚点是否覆盖第j个模型,1为是,0为否
private bool byColumn = true;
NumVarType varType = NumVarType.Int; //整数规划
public bool Valid_Result;
public int[] result;
public BaoSCP()
{
anchors = new List<Anchor>();
}
public void Add(Anchor anchor)
{
this.anchors.Add(new Anchor(anchor.loc, anchor.models));
}
public void Standard()
{
UpdateLength();
x_weight = new int[x_length];
x_min = new int[x_length];
x_max = new int[x_length];
for(int i=0;i<x_length;++i)
{
x_weight[i] = 1;
x_min[i] = 0;
x_max[i] = 1;
}
v_min = new int[v_length];
v_max = new int[v_length];
for(int i=0;i<v_length;++i)
{
v_min[i] = 1;
v_max[i] = 9999;
}
a = new int[v_length][];
for(int i=0;i<v_length;++i)
{
a[i] = new int[x_length];
for (int j = 0; j < x_length; ++j)
a[i][j] = 0;
}
int len;
for(int i=0;i<x_length;++i)
{
len = anchors[i].models.Count();
for(int j=0;j<len;++j)
{
a[i][anchors[i].models[j]] = 1;
}
}
}
public void Start()
{
try
{
Valid_Result = false;
Cplex cplex = new Cplex();
INumVar[] Result = new INumVar[x_length];
if (byColumn)
BuildModelByColumn(cplex, Result, varType);
else
BuildModelByRow(cplex, Result, varType);
if (cplex.Solve())
{
int[] res = new int[x_length];
for (int i = 0; i < x_length; i++)
res[i] = int.Parse(cplex.GetValue(Result[i]).ToString());
Output(cplex.GetStatus(), cplex.ObjValue, res);
}
cplex.End();
}
catch (ILOG.Concert.Exception ex)
{
System.Console.WriteLine("Concert Error: " + ex);
}
catch (System.IO.IOException ex)
{
System.Console.WriteLine("IO Error: " + ex);
}
}
public void Output(Cplex.Status status, double cost, int[] Result)
{
this.result = new int[x_length];
System.Console.WriteLine();
System.Console.WriteLine("Solution status = " +status);
System.Console.WriteLine();
System.Console.WriteLine(" cost = " + cost);
for (int i = 0; i < x_length; i++)
{
System.Console.WriteLine(" Buy"
+ i + " = " + Result[i]);
this.result[i] = Result[i];
}
System.Console.WriteLine();
Valid_Result = true;
}
private void BuildModelByRow(IModeler model, INumVar[] Result, NumVarType type)
{
for (int j = 0; j < x_length; j++)
{
Result[j] = model.NumVar(x_min[j], x_max[j], type); //每个锚点要么放,要么不放
}
model.AddMinimize(model.ScalProd(x_weight, Result)); //目标是最小化锚点的价值(也就是数量)
for (int i = 0; i < v_length; i++)
{
model.AddRange(v_min[i], model.ScalProd(a[i], Result), v_max[i]); //添加约束
}
}
private void BuildModelByColumn(IMPModeler model, INumVar[] Result, NumVarType type)
{
IObjective cost = model.AddMinimize();
IRange[] constraint = new IRange[v_length];
for (int i = 0; i < v_length; i++)
{
constraint[i] = model.AddRange(v_min[i], v_max[i]);
}
for (int j = 0; j < x_length; j++)
{
Column col = model.Column(cost,x_weight[j]);
for (int i = 0; i < v_length; i++)
{
col = col.And(model.Column(constraint[i], a[i][j]));
}
Result[j] = model.NumVar(col, x_min[j], x_max[j], type);
}
}
private void UpdateLength()
{
v_length = 0;
x_length = anchors.Count();
for(int i=0;i<x_length;++i)
{
int len = anchors[i].models.Count();
for(int j=0;j<len;++j)
{
if(anchors[i].models[j]>v_length)
{
v_length = anchors[i].models[j];
}
}
}
v_length++;
}
}
#endregion
}
Main Program 主程序窗口 MainWindows.xaml.cs
#region patch190120 by bxy
List<BaoPath> paths; //这是和外界连接的数据结构,可忽略
BaoSCP scp = new BaoSCP();
void ILP()
{
int[] result = new int[paths.Count()];
ConvertModelsToSCP(paths);
if(SCPBasedOnCplex(ref result))
//some operations based on "result" array
;
}
void ConvertModelsToSCP(List<BaoPath> paths)
{
for (int i = 0; i < maxn; ++i)
for (int j = 0; j < maxn; ++j)
{
pixels[i, j].value = 0;
pixels[i, j].id = "";
}
int radius = 75;//这个值要 equals 现实中的3米
int length = WallAndModelList.Count();
#region loop
for(int i=0;i<length;++i)
{
BaoModel bm = WallAndModelList[i];
PointD center = new PointD(bm.x, bm.y);
List<BaoSide> PointVis = new List<BaoSide>();
GetsVisibleRegion(center.X, center.Y, false, ref PointVis);
for (int ii = BInt(center.Y - radius, true); ii <= BInt(center.Y, false); ++ii)
{
double j_tmp = Math.Sqrt(radius * radius - (center.Y - ii) * (center.Y - ii));
for (int jj = BInt(center.X - j_tmp, true); jj <= BInt(center.X + j_tmp, false); ++jj)
{
if (IsPointInPolygon(jj, ii, PointVis))
{
pixels[jj, ii].Add(WallAndModelList[i].value, WallAndModelList[i].id); //可能有问题
}
}
}
for (int ii = BInt(center.Y + 1, false); ii <= BInt(center.Y + radius, false); ++ii)
{
double j_tmp = Math.Sqrt(radius * radius - (ii - center.Y) * (ii - center.Y));
for (int jj = BInt(center.X - j_tmp, true); jj <= BInt(center.X + j_tmp, false); ++jj)
{
if (IsPointInPolygon(jj, ii, PointVis))
{
pixels[jj, ii].Add(WallAndModelList[i].value, WallAndModelList[i].id); //可能有问题
}
}
}
}
#endregion
int bias = 31;
double last_value = -1;
string last_string = "null";
// int fi = -1;
// int fj = -1;
Anchor anchor;
List<int> models = new List<int>();
for (int i = 0; i < maxn; ++i)
for (int j = 0; j < maxn; ++j)
{
if (pixels[i, j].value!=0&&(!pixels[i,j].id.Equals(last_string)||pixels[i,j].value!=last_value))
{
String[] ss = pixels[i, j].id.Split(',');
int len = ss.Count();
for (int k = 0; k < len; ++k)
{
if (ss[k].Count() != 0)
models.Add(int.Parse(ss[i].ToString())+bias);
}
anchor = new Anchor(new PointD(i, j), models);
scp.Add(anchor);
}
}
}
bool SCPBasedOnCplex(ref int[] result)
{
int length = scp.x_length;
scp.Standard();
scp.Start();
if(scp.Valid_Result)
{
for (int i = 0; i < length; ++i)
result[i] = scp.result[i];
return true;
}
return false;
}
#endregion
测试数据采自 J.E.Beasley 的 OR-Library
在下网络上搜到的关于CSP的Paper,都使用此数据进行测试
Instances For Set Covering (OR-Library)
实验结果如下:
由于是确实性算法,所以CPLEX求的总是最优解,衡量性能的为运行时间
Filename | OPT | CPLEX | Time(ms) |
scp41.txt | 429 | 429 | 164 |
scp410.txt | 514 | 514 | 84 |
scp42.txt | 512 | 512 | 81 |
scp43.txt | 516 | 516 | 76 |
scp44.txt | 494 | 494 | 99 |
scp45.txt | 512 | 512 | 75 |
scp46.txt | 560 | 560 | 98 |
scp47.txt | 430 | 430 | 78 |
scp48.txt | 492 | 492 | 107 |
scp49.txt | 641 | 641 | 114 |
scp51.txt | 253 | 253 | 268 |
scp510.txt | 265 | 265 | 154 |
scp52.txt | 302 | 302 | 227 |
scp53.txt | 226 | 226 | 147 |
scp54.txt | 242 | 242 | 181 |
scp55.txt | 211 | 211 | 166 |
scp56.txt | 213 | 213 | 166 |
scp57.txt | 293 | 293 | 184 |
scp58.txt | 288 | 288 | 172 |
scp59.txt | 279 | 279 | 146 |
scp61.txt | 138 | 138 | 436 |
scp62.txt | 146 | 146 | 673 |
scp63.txt | 145 | 145 | 135 |
scp64.txt | 131 | 131 | 112 |
scp65.txt | 161 | 161 | 337 |
scpa1.txt | 253 | 253 | 895 |
scpa2.txt | 252 | 252 | 725 |
scpa3.txt | 232 | 232 | 711 |
scpa4.txt | 234 | 234 | 428 |
scpa5.txt | 236 | 236 | 398 |
scpb1.txt | 69 | 69 | 901 |
scpb2.txt | 76 | 76 | 1429 |
scpb3.txt | 80 | 80 | 1066 |
scpb4.txt | 79 | 79 | 1676 |
scpb5.txt | 72 | 72 | 880 |
scpc1.txt | 227 | 227 | 847 |
scpc2.txt | 219 | 219 | 1451 |
scpc3.txt | 243 | 243 | 1706 |
scpc4.txt | 219 | 219 | 683 |
scpc5.txt | 215 | 215 | 878 |
scpd1.txt | 60 | 60 | 1602 |
scpd2.txt | 66 | 66 | 2220 |
scpd3.txt | 72 | 72 | 2148 |
scpd4.txt | 62 | 62 | 2067 |
scpd5.txt | 61 | 61 | 884 |