华电北风吹
最后修改日期:2015/8/6
如果只有一些线性约束,想把所有的可行解都输出出来怎么办?
例如下面的小例子:
约束条件为
|a1x1+a2x2+a3x3+...+anxn−b|<e
m1=<x1<=M1
m2=<x2<=M2
…
mn=<xn<=Mn
最简单的方法肯定是把所有的点根据超矩形约束条件遍历一遍,然后把与到超平面距离满足约数的点输出。
我写了个小例子,如下,循环把所有的可行解个数输出。
__author__ = 'zhengyi'
def mainfunc(m0,m,x):
if len(m)>0:
k1=m0[0]
k2=m[0]
for i in range(k1,k2+1):
t=x.copy()
t.append(i)
mainfunc(m0[1::],m[1::],t)
else:
if distance(x)<=e:
print(x)
def distance(x):
result=map(lambda x,y:x*y,a,x)
return abs(sum(result)-b)
a=[20,30]
b=90
m=[5,5]
m0=[2,0]
e=20
x=[]
mainfunc(m0,m,x)
代码运行结果为:
[2, 1]
[2, 2]
[3, 1]
[4, 0]
[4, 1]
[5, 0]
但是当特征个数增大的时候,这个计算时间会特别大。因为,计算时间复杂度是theta( mn )。其中m为每个特征的取值范围,n为特征维数。
考虑到即使你知道那个解是可行解,把所有的解输出也需要循环n次。因此如果想要减少计算量,貌似只能减少在无用解上的计算次数。这时候我们可以考虑先在可行解内部获取一个初始点,然后以这个初始点为中心,在可行解空间中向外围搜索,直到到达可行解边界。
这样做需要考虑搜索方向问题。比较好的一点是,由线性约束的可行解构成的空间肯定是一个凸空间。因此可以想到以下两种搜索策略:
1、根据特征递归循环(这里有缺陷,就是如果不让下层特征对上层特征进行搜索的话,需要保证当前特征搜索路径是最宽的,这个就很麻烦了)
2、根据空间的外方向,逐层外扩(这也是下面代码采用的思想)
__author__ = 'zhengyi'
def mainfunc(x,d):
flag=True
for i in range(0,len(d)):
x[i]+=d[i]
if x[i]<m0[i] or x[i]>m[i]:
return
if d[i]!=0 and flag:
flag=False
if flag:
return
if distance(x)>e:
return
print(x)
mainfunc(x.copy(),d.copy())
for i in range(0,len(d)):
if d[i]!=0:
td=d.copy()
td[i]=0
mainfunc(x.copy(),td)
def distance(x):
result=map(lambda x,y:x*y,a,x)
return abs(sum(result)-b)
a=[20,30]
b=90
m=[5,5]
m0=[2,0]
e=20
x=[2,2]
print(x)
d=[-1 for i in range(0,len(a))]
k=pow(3,len(a))
for i in range(1,k):
td=d.copy()
j=0
while i>0:
td[j]+=i%3
i//=3
j+=1
mainfunc(x.copy(),td)
代码输出结果:
[2, 2]
[2, 1]
[3, 1]
[4, 0]
[5, 0]
[4, 1]
但是这个算法的复杂度仍旧是theta(3^n),因为每一维都有3中选择,共同构成的空间外方向数目就是 3n−1 种。这样的搜索策略能够保证搜索一直在可行解内部搜索,代码计算效率比较高,这是我目前想到的最好的方法,欢迎对这个问题感兴趣的有想法的朋友一起讨论。
补充:
第二种方法需要在可行解内找一个初始点,由于这个问题不是主要问题,可以由随便指定一个优化函数,然后利用单纯形法得到一个初始解。在这里我为了方便,使用Lingo得到一个初始解。
Lingo代码如下:
model:
sets:
s/1..20/:a,x,m0,m;
endsets
data:
a=20,30,15,17,20,20,20,20,30,15,17,20,20,20,20,30,15,17,20,20;
m=5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5;
m0=2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;
enddata
@abs(@sum(s(i):a(i)*x(i))-1000)<100;
@for(s(i):m0(i)<x(i));
@for(s(i):x(i)<m(i));
@for(s(i):@gin(x(i)));
end
代码1的非递归形式:
__author__ = 'zhengyi'
def distance(x):
result=map(lambda x,y:x*y,a,x)
return abs(sum(result)-b)
a=[20,30]
b=90
m=[5,5]
m0=[2,0]
e=20
k=len(a)
totalNum=1
original=m0.copy()
rangeList=[0 for i in range(0,k)]
for i in range(0,k):
rangeList[i]=m[i]-m0[i]+1
totalNum*=rangeList[i]
for i in range(0,totalNum):
s=original.copy()
p=0
while i>0:
s[p]=original[p]+i%(rangeList[p])
i=i//(rangeList[p])
p+=1
if distance(s)<=e:
print(s)
#include <iostream>
#include <algorithm>
using namespace std;
int main(int argc, char* argv[])
{
int sum2, sum3, sum5, sum6;
int count = 0;
for (int x2 = -100; x2 <= 100; x2++)
{
if (x2 == 0)
continue;
cout << x2 << endl;
sum2 = 28 * x2;
for (int x5 = -100; x5 <= 100; x5++)
{
if (x5 == 0)
continue;
sum5 = sum2 + 13 * x5;
for (int x6 = -100; x6 <= 100; x6++)
{
if (x6 == 0)
continue;
sum6 = sum5 + 39 * x6;
if ((sum6 > 6500) || (sum6 < -6500) || ((sum6 % 5) != 0))
continue;
else
{
sum6 = sum6 / 5;
for (int x3 = -100; x3 <= 100; x3++)
{
if (x3 == 0)
continue;
sum3 = sum6 + 9 * x3;
if ((sum3>400) || ((sum3 < -400)))
continue;
else
{
int left = max(-100, (sum3 - 100) / 3);
int right = min(100, (sum3 + 100) / 3);
count += right - left + 1;
if (sum3 == 0)
{
count--;
}
else
{
if ((sum3 <= 100) && (sum3 >= -100))
count--;
if ((sum3 % 3 == 0) && ((sum3 / 3) <= 100) && ((sum3 / 3) >= -100))
count--;
}
}
}
}
}
}
}
cout << count << endl;
getchar();
return 0;
}