赞 | 189 |
VIP | 627 |
好人卡 | 188 |
积分 | 95 |
经验 | 171230 |
最后登录 | 2024-7-3 |
在线时间 | 5073 小时 |
Lv4.逐梦者 (版主)
- 梦石
- 0
- 星屑
- 9532
- 在线时间
- 5073 小时
- 注册时间
- 2013-6-21
- 帖子
- 3580
|
本帖最后由 RyanBern 于 2017-10-13 21:45 编辑
状元说我的图是 GPL 的,因此我有义务把这张图怎么来的给写下来。
方法可能有点让楼主失望,仍然需要借助计算机完成,不过这里面的道理可不是一个简单的穷举法能说的清的。认识楼上 taroxd 和我的人可能知道,我们专业是一样的,因此脑回路可能非常像(我就不说状元的脑回路有 5% 是我调教出来的哼
作为一个数学系科学计算专业的学生,拿到这样的问题首先考虑的就是建立数学模型。楼上的状元已经给了一个很好的例子,但是那个建模非常简单,并且和原问题不等价。用数学语言来说,状元提供的解法只是原问题解的一个上界,从约束上来看是将约束集扩大来寻找一个必要条件。什么?说人话?那我这样讲:状元的做法只能告诉你就算你累秃了也不会找到一个比他所说的更好的解。我们对此自然是不太满意的。所以我这篇回贴实际就是告诉大家:上面问题的最优解就是 61,方案如图所示。
接下来就是我的计算过程。如果觉得没信心听懂的可以不用往下看了。拿到这个问题,我们的首要任务就是将它变成一个优化问题进行求解。对于优化问题,最重要的有两个要素:
- 目标函数:你的优化目标是什么-
- 自变量:目标函数所依赖的变量,以及对这些变量有无限制。
在我们这个问题里面,我们要优化的无非就是田地的最大块数,而变量就是我们在何处放置“水”。这个关系非常明确,一旦我们确定了所有“水”的位置,那我们就自动确定了该区域所能种植的田地数量。
下面就是用数学的语言描述这个问题。田地的最大快数非常好量化,但是在何处放置“水”这个变量本身不是数学对象。因此我们可以引入一个 81 个分量的向量 x 来表示各个位置上是否是水。如果是则对应位置为 1,如果不是则对应位置是 0。在这里我们将一个二维的表变成了一个一个长向量,看起来有些不直观。不过我们总有办法将这些对应的位置串起来,例如我们先将其拆成 9 行,然后首尾相接就可以做成一个长向量。
现在的问题来了,如何利用给定的一个向量来计算此方案下田地的块数?这就需要我们建立一个函数,将这个向量变成一个数字。这个函数应该有简单的结构。例如,我们现在考虑在 (i, j) 位置设置“水”,假设它在区域内部,那么它四周的块就可以安全地放置田地。从映射角度看来,(i, j) 位置对应着向量 x 的第 (9*i+j) 个分量(为了方便,i 和 j 从 0 开始计数),而这样放置,它四周的点 x(9*i+j-1),x(9*i+j+1),x(9*(i-1)+j),x(9*(i+1)+j) 都会在这个点的影响内。如果用向量 x 表示某个地方是否是水,用向量 y 表示某个地方被水或者田地占用情况,那么我们可以构造这样的线性映射
如果直观点表示的话,A 的形状大概是这样,蓝点表示 1 其他地方表示 0
但是我们可能注意到了,映射 A 的作用就是表示某块地被占用的情况,大致可以分为两种
- 该片地被“水”占用,这从 A 的对角元可以看出。
- 该片地被“预计的田地”占用,这从 A 的非对角线可以看出。注意,如果一片田左右逢源,或者是和“水”重合了,那么这个位置要计算多次。
这样的 y 是不满足我们的要求的,我们想法是一片田地只算一次占用就好,并且去掉“水”的面积(因为显然水上不能种田)。这等价于我们将 y (=Ax) 中的所有比 1 大的元素都变成 1 就好。然后因为水的位置我们是知道的,变成 1 之后再减去 x,剩下的向量就是纯田地的信息了,最后我们把他们加起来,得到我们的目标函数值。
总之,我们写下我们的优化问题(这里 e 是一个全是 1 的向量,等价于对后面的分量求和):
它等价于下面的问题,原因比较显然,这里就不证明了(方法是引入辅助变量 z)
这是一个整数规划问题,可以调用比较成熟的求解器进行求解。在这里我使用了商业软件 Gurobi 在 MATLAB 环境下进行求解,代码我贴在最后。得到的解大致就是这个样子了,蓝色是水,绿色是田。
function [X, Out] = MC_gurobi(A, opts) %% Importing options init; %% Importing Gurobi API addpath(opts.path); %% Define model n = size(A, 1); model.obj = ones(2*n, 1); model.A = sparse([A, -eye(n)]); model.sense = '<'; model.rhs = zeros(n, 1); model.lb = [zeros(n,1); -ones(n,1)]; model.rb = [ones(n,1); ones(n,1)]; model.vtype = 'I'; model.modelname = 'MC-minimum'; %% Set parameters param.method = -1; param.outputflag = 0; %% call solver tt = tic; Res = gurobi(model, param); tt = toc(tt); %% Exporting solutions x = Res.x; X = x(1:n); Out.tt = tt; Out.flag = Res.status; Out.fval = Res.objval; Out.itr = Res.baritercount; %% nested function function init if ~isfield(opts, 'path'); opts.path = '/opt/gurobi701/linux64/matlab'; end; end end
function [X, Out] = MC_gurobi(A, opts)
%% Importing options
init;
%% Importing Gurobi API
addpath(opts.path);
%% Define model
n = size(A, 1);
model.obj = ones(2*n, 1);
model.A = sparse([A, -eye(n)]);
model.sense = '<';
model.rhs = zeros(n, 1);
model.lb = [zeros(n,1); -ones(n,1)];
model.rb = [ones(n,1); ones(n,1)];
model.vtype = 'I';
model.modelname = 'MC-minimum';
%% Set parameters
param.method = -1;
param.outputflag = 0;
%% call solver
tt = tic;
Res = gurobi(model, param);
tt = toc(tt);
%% Exporting solutions
x = Res.x;
X = x(1:n);
Out.tt = tt;
Out.flag = Res.status;
Out.fval = Res.objval;
Out.itr = Res.baritercount;
%% nested function
function init
if ~isfield(opts, 'path'); opts.path = '/opt/gurobi701/linux64/matlab'; end;
end
end
dim = 9; n = dim ^ 2; A = gallery('poisson', dim); for i = 1:n; A(i, i) = -1; end; [x, Out] = MC_gurobi(A, []); y = A * x; X = reshape(x, 9, 9); Y = reshape(y, 9, 9); axis off; %rectangle('Position', [1, 1, 9, 9], 'FaceColor', [0, .7, 0]); for i=1:dim for j=1:dim pos = [i, j, 1, 1]; if X(i, j) == 1 rectangle('Position', pos, 'FaceColor', [0, 0, .7]); elseif Y(i, j) < 0 rectangle('Position', pos, 'FaceColor', [0, .7, 0]); else rectangle('Position', pos); end end end
dim = 9;
n = dim ^ 2;
A = gallery('poisson', dim);
for i = 1:n; A(i, i) = -1; end;
[x, Out] = MC_gurobi(A, []);
y = A * x;
X = reshape(x, 9, 9);
Y = reshape(y, 9, 9);
axis off;
%rectangle('Position', [1, 1, 9, 9], 'FaceColor', [0, .7, 0]);
for i=1:dim
for j=1:dim
pos = [i, j, 1, 1];
if X(i, j) == 1
rectangle('Position', pos, 'FaceColor', [0, 0, .7]);
elseif Y(i, j) < 0
rectangle('Position', pos, 'FaceColor', [0, .7, 0]);
else
rectangle('Position', pos);
end
end
end
|
评分
-
查看全部评分
|