把有界凸空间中所有点输出的算法讨论

华电北风吹
最后修改日期:2015/8/6

如果只有一些线性约束,想把所有的可行解都输出出来怎么办?
例如下面的小例子:
约束条件为

|a1x1+a2x2+a3x3+...+anxnb|<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中选择,共同构成的空间外方向数目就是 3n1 种。这样的搜索策略能够保证搜索一直在可行解内部搜索,代码计算效率比较高,这是我目前想到的最好的方法,欢迎对这个问题感兴趣的有想法的朋友一起讨论。

补充:
第二种方法需要在可行解内找一个初始点,由于这个问题不是主要问题,可以由随便指定一个优化函数,然后利用单纯形法得到一个初始解。在这里我为了方便,使用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;
}

你可能感兴趣的:(超空间,线性约束,输出所有点)