小区门诊所最佳选址的
python
求解
下图给出了7个
居民小区,它们的邻接关系
及每个小区的居民人数
(括号中数字)。限于各方面条件限制,目前暂时只能在其中2个
小区开设医疗门诊所
,且每个门诊所只服务相邻的两个小区
(如在A开设门诊所,可服务于A和B或A和C)。问应在哪两个小区
开设门诊所及分别为哪些小区服务,使其覆盖
的居民人数
为最多
。
A(3400) | ||
---|---|---|
C(4200) | B(2900) |
|
D(2100) | ||
G(7100) | F(1800) | E(5000) |
5.2的视力看穿答案,在D
(或G
)开设门诊,并服务于D
和G
;在B
(或E
)开设门诊,并服务于B
和E
。总服务人数达到最大
,为17100
人。
可以设置
0~1
变量,利用Lingo
求解。抑或手工计算2^7
中情况。
线性规划法
,7个居民小区应选择4处地点,使得门诊所服务的人数最多,并且每个门诊所只能服务于自身及相邻的小区且只能服务于一个相邻小区,使其覆盖的人数最多。我们可以设置0-1
变量,共设置10
组0-1变量表示服务于哪两个小区,求解方程组使其目标函数值最大
。遍历法
,遍历所有可能的方案,并且计算每一种可能的总覆盖人数,设置标志,排除重复覆盖的情况,得出的所有情况中的最大值即为最佳选址方案。邻接矩阵法
,构造邻接矩阵并求解。0-A
、1-B
、2-C
、3-D
、4-E
、5-F
、6-G
代替小区编号。对于决策变量的选取个数及约束条件选择,有两种
方法:
(1)枚举法
:从A
开始遍历直到G
结束,列出与之相邻的所有小区组合,即AB、AC、BA 、BC、BD、BE、CA 、CB 、CD、DB 、DC 、DF、DG、EB 、EF、FD 、FE 、FG、GD 和GF ,划去重复的字母组合,即决策变量的个数有:AB
、AC
、BC
、BD
、BE
、CD
、DF
、DG
、EF
和FG
共10
组决策变量
(见下图1)。
(2)python
:首先将与各小区相邻小区的信息写入“字典”
存储,接着将各小区的编号存储于“列表”
,使用for
循环,若满足条件,则写入“字典”,最后输入结果如下(图2)所示。
'''存储与之相邻元素的信息'''
a=[{
"no":1,"num":2900},{
"no":2,"num":4200}]#存储与A相邻的信息,共两个(B和C)
b=[{
"no":0,"num":3400},{
"no":2,"num":4200},{
"no":3,"num":2100},{
"no":4,"num":5000}]
c=[{
"no":0,"num":3400},{
"no":1,"num":2900},{
"no":3,"num":2100}]
d=[{
"no":1,"num":2900},{
"no":2,"num":4200},{
"no":5,"num":1800},{
"no":6,"num":7100}]
e=[{
"no":1,"num":2900},{
"no":5,"num":1800}]
f=[{
"no":3,"num":2100},{
"no":4,"num":5000},{
"no":6,"num":7100}]
g=[{
"no":3,"num":2100},{
"no":5,"num":1800}]
'''信息写入列表'''
c_list=[];c_list.append(a);lstD=[] #存放所有相邻元素的信息的列表
c_list.append(b);c_list.append(c);c_list.append(d)
c_list.append(e);c_list.append(f);c_list.append(g)
stack=[i for i in range(7)]
sum1=0;lst1=[];lst2=[]
for i in range(7):
for j in range(len(c_list[i])):
if stack[i]<c_list[i][j]["no"]:
sum1+=1
a_dict={
}
a_dict["x"]=i
a_dict["y"]=c_list[i][j]["no"]
lst1.append(a_dict)
print("变量个数: ",end='');print(sum1)
print("变量索引:(0-A,1-B,2-C,3-D,4-E,5-F,6-G)")
for k in range(len(lst1)):
print(lst1[k]["x"],end='')
print(lst1[k]["y"],end=' ')
print()
for k in range(len(lst1)):
print(chr(65+lst1[k]["x"]),end='')
print(chr(65+lst1[k]["y"]),end=' ')
print();print("约束条件:")
for i in range(7):
print("对于",end='');print(chr(65+i),end='')
print("小区的约束: ",end='')
for j in range(len(c_list[i])):
print(chr(65+i),end='')
print(chr(65+c_list[i][j]["no"]),end=' ')
if j!=len(c_list[i])-1:
print("+",end='')
print("<= 1")
决策变量有:
AB | AC | BC | BD | BE | CD | DF | DG | EF | FG | |
---|---|---|---|---|---|---|---|---|---|---|
变量 | X1 | X2 | X3 | X4 | X5 | X6 | X7 | X8 | X9 | X10 |
其中 X 1 ∼ X 10 X_{1}\sim X_{10} X1∼X10都是 0 − 1 变 量 0-1变量 0−1变量,所以该规划的
目标函数
为
M a x Z = 6300 X 1 + 7600 X 2 + 7100 X 3 + 5000 X 4 + 7900 X 5 + 6300 X 6 + 3900 X 7 + 9200 X 8 + 6800 X 9 + 8900 X 10 MaxZ=6300X_{1}+7600X_{2}+7100X_{3}+5000X_{4}+7900X_{5}+6300X_{6}+3900X_{7}+9200X_{8}+6800X_{9}+8900X_{10} MaxZ=6300X1+7600X2+7100X3+5000X4+7900X5+6300X6+3900X7+9200X8+6800X9+8900X10
因为在两地建门诊所,所以有
∑ i = 1 10 X i = 2 \sum_{i=1}^{10}X_{i}=2 i=1∑10Xi=2
各小区不能重复覆盖
两次,即
s . t . { X 1 + X 2 ≤ 1 ; X 1 + X 3 + X 4 + X 5 ≤ 1 X 2 + X 3 + X 6 ≤ 1 X 4 + X 6 + X 7 + X 8 ≤ 1 X 5 + X 9 ≤ 1 X 7 + X 9 + X 10 ≤ 1 X 8 + X 10 ≤ 1 \begin{aligned} s.t.\begin{cases} X_{1}+X_{2}\leq1;\\ X_{1}+X_{3}+X_{4}+X_{5}\leq1\\ X_{2}+X_{3}+X_{6}\leq1\\ X_{4}+X_{6}+X_{7}+X_{8}\leq1\\ X_{5}+X_{9}\leq1\\ X_{7}+X_{9}+X_{10}\leq1\\ X_{8}+X_{10}\leq1 \end{cases} \end{aligned} s.t.⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧X1+X2≤1;X1+X3+X4+X5≤1X2+X3+X6≤1X4+X6+X7+X8≤1X5+X9≤1X7+X9+X10≤1X8+X10≤1
又因为 X 1 ∼ X 10 X_{1}\sim X_{10} X1∼X10都是 0 − 1 0-1 0−1变量,所以有
X j = 0 或 1 ( j = 1 , 2 , ⋯ , 10 ) X_{j}=0或1(j=1,2,\cdots,10) Xj=0或1(j=1,2,⋯,10)
!求解0-1整数规划的小区门诊所最佳选址问题;
max=6300*x1+7600*x2+7100*x3+5000*x4+7900*x5+6300*x6+3900*x7+9200*x8+6800*x9+8900*x10;
x1+x2+x3+x4+x5+x6+x7+x8+x9+x10=2;!两地建门诊所;
x1+x2<=1;!A小区约束条件;
x1+x3+x4+x5<=1;!B小区约束条件;
x2+x3+x6<=1;!C小区约束条件;
x4+x6+x7+x8<=1;!D小区约束条件;
x5+x9<=1;!E小区约束条件;
x7+x9+x10<=1;!F小区约束条件;
x8+x10<=1;!G小区约束条件;
!各决策变量为0-1变量,取值0或1;
@bin(x1);@bin(x2);@bin(x3);@bin(x4);@bin(x5);
@bin(x6);@bin(x7);@bin(x8);@bin(x9);@bin(x10);
得到Objective value=17100
,其中 X 5 X_{5} X5和 X 8 X_{8} X8取值为1
,其余决策变量的取值为0
。所以应该在D
(或G
)开设门诊,并服务于D
和G
;在B
(或E
)开设门诊(图3),并服务于B
和E
。即只要使门诊所服务于B
、E
和D
、G
即可满足服务人数的最大化
,最大服务人数为17100
。
# -*- coding: utf-8 -*-
from queue import Queue #导入队列的第三方库
q = Queue(maxsize=0) #初始化空队列
'''存储与之相邻元素的信息'''
a=[{
"no":1,"num":2900},{
"no":2,"num":4200}]#存储与A相邻的信息,共两个(B和C)
b=[{
"no":0,"num":3400},{
"no":2,"num":4200},{
"no":3,"num":2100},{
"no":4,"num":5000}]
c=[{
"no":0,"num":3400},{
"no":1,"num":2900},{
"no":3,"num":2100}]
d=[{
"no":1,"num":2900},{
"no":2,"num":4200},{
"no":5,"num":1800},{
"no":6,"num":7100}]
e=[{
"no":1,"num":2900},{
"no":5,"num":1800}]
f=[{
"no":3,"num":2100},{
"no":4,"num":5000},{
"no":6,"num":7100}]
g=[{
"no":3,"num":2100},{
"no":5,"num":1800}]
'''信息写入列表'''
c_list=[];c_list.append(a);lstD=[] #存放所有相邻元素的信息的列表
c_list.append(b);c_list.append(c);c_list.append(d)
c_list.append(e);c_list.append(f);c_list.append(g)
b_list=[] #存放所有情况的列表
d_list=[3400,2900,4200,2100,5000,1800,7100] #小区的居民数
number = 0 #覆盖四处地点,计数标志
stack1 = [];stack2 = []
lst = [] #存放一种情况的值
'''数据写入列表'''
for i in range(7):
for j in range(7):
if i >= j: #卫生所建在不同的两地
continue #且只考虑组合,不考虑排列顺序
else:
a_list = [] #存储一种建址的情况
a_dict = {
} #存储一种建址的信息
a_dict["x"] = i #一地
a_dict["y"] = j #二地
a_list.append(a_dict)
b_list.append(a_list) #两地选址的情况,不考虑选址顺序
'''判断最大覆盖区域'''
for k in range(len(b_list)):
# b_list[k][0]["x"]为第k组数据的第一个地址索引
number = 0 #统计
stack1 = [];stack2 = []
flag = [0,0,0,0,0,0,0] #标记,判断是否已覆盖,避免重复,1代表已覆盖
flag[b_list[k][0]["x"]] = 1 #选中一处地点,标记为1
flag[b_list[k][0]["y"]] = 1 #选中另一处地点,标记为1
# 排除相邻的情形
for m in range(len(c_list[b_list[k][0]["x"]])): #判断这时两者是否相邻
# c_list[b_list[k][0]["x"]]表示建在某地的索引值
stack1.append(c_list[b_list[k][0]["x"]][m]) #另一地点相邻的信息
if c_list[b_list[k][0]["x"]][m]["no"] == b_list[k][0]["y"] and \
c_list[b_list[k][0]["x"]][m]["num"] == d_list[b_list[k][0]["y"]]:
stack1.pop() #弹出,不能覆盖
for n in range(len(c_list[b_list[k][0]["y"]])): #判断这时两者是否相邻
stack2.append(c_list[b_list[k][0]["y"]][n]) #另一地点相邻的信息
if c_list[b_list[k][0]["y"]][n]["no"] == b_list[k][0]["x"] and \
c_list[b_list[k][0]["y"]][n]["num"] == d_list[b_list[k][0]["x"]]:
stack2.pop() #弹出,不能覆盖
# 每个地点只能覆盖一次
for t in range(len(stack1)):
flag[stack1[t]["no"]]=1
for r in range(len(stack2)):
if stack2[r]["no"]==stack1[t]["no"] and \
stack2[r]["num"]==stack1[t]["num"]:
continue
flag[stack2[r]["no"]]=1
for s in range(len(flag)):
number = number + flag[s]
if number==4:
break
z=d_list[b_list[k][0]["x"]]+d_list[b_list[k][0]["y"]]+\
d_list[stack1[t]["no"]]+d_list[stack2[r]["no"]]
lst.append(z)
if z==17100:
b_dict={
}
b_dict["x"]=b_list[k][0]["x"]
b_dict["y"]=b_list[k][0]["y"]
b_dict["m"]=stack1[t]["no"]
b_dict["n"]=stack2[r]["no"]
lstD.append(b_dict)
print("max=",end='');print(max(lst));
print("索引值:(0-A,1-B,2-C,3-D,4-E,5-F,6-G)")
for k in range(len(lstD)):
print(lstD[k]["x"],end=' ');print(lstD[k]["y"],end=' ')
print(lstD[k]["m"],end=' ');print(lstD[k]["n"])
clear;clc;
k=[];w=[];
C=[3400,2900,4200,2100,5000,1800,7100];
A=[0 1 1 0 0 0 0;
1 0 1 1 1 0 0;
1 1 0 1 0 0 0;
0 1 1 0 0 1 1;
0 1 0 0 0 1 0;
0 0 0 1 1 0 1;
0 0 0 1 0 1 0;];
%%
for i1=1:7
for j1=1:7
if A(i1,j1)==1
k1=C(i1)+C(j1);
for i2=1:7
for j2=1:7
if A(i2,j2)==1
if (i1~=i2)&&(i1~=j2)&&(j1~=i2)&&(j1~=j2)
k2=C(i2)+C(j2);
w=[w;i1,j1,i2,j2];
k=[k;k1+k2];
end
end
end
end
end
end
end
%%
[m,t]=max(k(:));
disp("max=");disp(m);
disp("小区编号");w(t,:)