数学建模共享单车问题

附件2中有50辆共享单车(编号为951-1000)的数据。已知其中5辆单车会间歇性地发生故障,从而导致该车在部分时段的骑行速度过慢。请根据附件1,判断附件2中有哪5辆车最有可能是问题车。

导入附件1数据

%% 导入数据

[~, ~, raw0_0] = xlsread('附件1 共享单车数据.xls','Sheet1','B1:B12030');

[~, ~, raw0_1] = xlsread('附件1 共享单车数据.xls','Sheet1','D1:D12030');

[~, ~, raw0_2] = xlsread('附件1 共享单车数据.xls','Sheet1','F1:F12030');

[~, ~, raw0_3] = xlsread('附件1 共享单车数据.xls','Sheet1','H1:H12030');

[~, ~, raw0_4] = xlsread('附件1 共享单车数据.xls','Sheet1','J1:J12030');

raw = [raw0_0,raw0_1,raw0_2,raw0_3,raw0_4];

raw(cellfun(@(x) ~isempty(x) && isnumeric(x) && isnan(x),raw)) = {''};

%% 将非数值元胞替换为 NaN

R = cellfun(@(x) ~isnumeric(x) && ~islogical(x),raw); % 查找非数值元胞

raw(R) = {NaN}; % 替换非数值元胞

%% 创建输出变量

data = reshape([raw{:}],size(raw));

%% 创建表

data1 = table;

%% 将导入的数组分配给列变量名称

data1.bh = data(:,1);

data1.cf_time = data(:,2);

data1.cf_tcd = data(:,3);

data1.dd_time = data(:,4);

data1.dd_tcd = data(:,5);

%% 清除临时变量

clearvars data raw raw0_0 raw0_1 raw0_2 raw0_3 raw0_4 R;

% 备份一下数据

data_bf = data1;

处理第一列的缺失值

data1.bh = fillmissing(data1.bh,'previous');

删除表中包含缺失数据的任何行

data1 = rmmissing(data1);

提取这10个停车点来回之间的数据

num = 10; % 10个停车点

tcd_data = cell(num,num);

for i = 1:num

for j = 1:i

ind1 = (data1.cf_tcd == i & data1.dd_tcd == j); % i-->j

ind2 = (data1.cf_tcd == j & data1.dd_tcd == i); % j-->i

tcd_data{i,j} = data1(ind1|ind2,:);

end

end

计算平均值和标准差

mean_tcd = cellfun(@(x) mean(x.dd_time-x.cf_time), tcd_data,'ErrorHandler',@(varargin) NaN);

std_tcd = cellfun(@(x) std(x.dd_time-x.cf_time), tcd_data,'ErrorHandler',@(varargin) NaN);

生成一个对称矩阵,在此之前将nan改为0

mean_tcd(isnan(mean_tcd)) = 0;

std_tcd(isnan(std_tcd)) = 0;

mean_tcd = mean_tcd + mean_tcd';

std_tcd = std_tcd + std_tcd';

导入附件2数据

%% 导入数据

[~, ~, raw0_0] = xlsread('附件2 待检测的共享单车数据.xls','Sheet1','B1:B619');

[~, ~, raw0_1] = xlsread('附件2 待检测的共享单车数据.xls','Sheet1','D1:D619');

[~, ~, raw0_2] = xlsread('附件2 待检测的共享单车数据.xls','Sheet1','F1:F619');

[~, ~, raw0_3] = xlsread('附件2 待检测的共享单车数据.xls','Sheet1','H1:H619');

[~, ~, raw0_4] = xlsread('附件2 待检测的共享单车数据.xls','Sheet1','J1:J619');

raw = [raw0_0,raw0_1,raw0_2,raw0_3,raw0_4];

raw(cellfun(@(x) ~isempty(x) && isnumeric(x) && isnan(x),raw)) = {''};

%% 将非数值元胞替换为 NaN

R = cellfun(@(x) ~isnumeric(x) && ~islogical(x),raw); % 查找非数值元胞

raw(R) = {NaN}; % 替换非数值元胞

%% 创建输出变量

data = reshape([raw{:}],size(raw));

%% 创建表

data2 = table;

%% 将导入的数组分配给列变量名称

data2.bh = data(:,1);

data2.cf_time = data(:,2);

data2.cf_tcd = data(:,3);

data2.dd_time = data(:,4);

data2.dd_tcd = data(:,5);

%% 清除临时变量

clearvars data raw raw0_0 raw0_1 raw0_2 raw0_3 raw0_4 R;

% 备份一下数据

data_bf2 = data2;

处理第一列的缺失值

data2.bh = fillmissing(data2.bh,'previous');

删除表中包含缺失数据的任何行

data2 = rmmissing(data2);

计算行驶的时间

data2.dif_time = data2.dd_time-data2.cf_time;

3sigma原则确定异常值

diftime = data2.dif_time;

cfd = data2.cf_tcd;

ddd = data2.dd_tcd;

num = length(cfd); % 569

res = zeros(num,1); % 初始化要求的结果

for i = 1:num

sigma = std_tcd(cfd(i),ddd(i));

if diftime(i) < mean_tcd(cfd(i),ddd(i)) - 3*sigma

res(i) = 1;

elseif diftime(i) > mean_tcd(cfd(i),ddd(i)) + 3*sigma

res(i) = 1;

end

end

data2.res = res;

统计这50辆车中,异常值的占比

先对数据进行分组,G是分组编号,M是代表的意义

[G,M] = findgroups(data2.bh);

f = @(x) length(x(x==1))/length(x);

bl = splitapply(f,data2.res,G); % 计算异常值的比例

format long g

sortrows([M,bl],2,'descend')

ans = 50×2

975 0.833333333333333

961 0.8

996 0.636363636363636

977 0.625

981 0.444444444444444

951 0

952 0

953 0

954 0

955 0

编号为[975 961 996 977 981]的这五辆车存在问题。

为确保停车点的正常运行,工作人员每天需骑车从第1个停车点开始,对10个停车点依次进行无重复无遗漏地巡视,途中不作停留,最后回到第1个停车点。为了尽快完成所有停车点的巡视并返回,根据附件1,请给出一条巡视路线(即10个停车点的巡视顺序),并给出巡视一次所需要的最短时间。

factorial(9)

ans =

362880

问题规模不算太大,可以枚举,没太大必要用模拟退火或者遗传算法等智能优化算法!

枚举法:先来生成路径的排列组合(不到1s出结果了)

tic

PATH = perms(2:10);

num = size(PATH,1);

PATH = [ones(num,1),PATH];

TIME = zeros(num,1); % 保存每条路径的时间

for i = 1:num

TIME(i) = calculate_tsp_d(PATH(i,:),mean_tcd);

end

toc

历时 0.432379 秒。

function  result =  calculate_tsp_d(path,d)
% 输入:path:路径(1至n的一个序列),d:距离矩阵(在本题中就是时间~)
    n = length(path);
    result = 0; % 初始化该路径走的距离为0
    for i = 1:n-1  
        result = d(path(i),path(i+1)) + result;  % 按照这个序列不断的更新走过的路程这个值
    end   
    result = d(path(1),path(n)) + result;  % 别忘了加上从最后一个城市返回到最开始那个城市的距离
end
[min_time,ind] = min(TIME);

disp('最优路线是:')

PATH(ind,:)

% ans = 1×10

% 1 10 2 8 5 7 4 9 3 6

disp('最短时间是')

最短时间是

min_time

min_time =

53.4753546422219

 

为应对疫情,工作人员需要每隔4小时对共享单车进行一次消毒。消毒的方式为:从停车点1开始消毒,并骑电动车对10个停车点依次进行无重复无遗漏地消毒(电动车的速度为共享单车的1.5倍),每个停车点停留10分钟,最后返回停车点1。在某个停车点停留时,对出现在当前停车点的所有共享单车,在把手、坐垫和篮筐上喷洒医用酒精进行消毒。由于消毒方式便于操作,停留的10分钟已然够用。同时,鉴于酒精的快速挥发性,消毒不影响借还车的进行。若某次消毒,工作人员在上午9点出发,根据附件1,请给出一条消毒路线(即10个停车点的消毒顺序),使得本次能够被消毒的共享单车数量尽可能地多,并且不影响下一次消毒的进行。

这个题目有一点点歧义,即要不要算上附件二中编号为951-1000的这50辆车

如果算上的话可以这么做:

% data = union(data1,data2(:,1:5));

如果同时还要排除存在问题的这五辆车,可以这样做:

% ind = [975 961 996 977 981];

% for i = 1:length(ind)

% data(data.bh == ind(i),:) = [];

% end

%统计在一天的每一分钟,每个停车点有多少辆车

R = zeros(24 * 60 , 10);

data = data1;

data.cf_time = ceil(data.cf_time);

data.dd_time = ceil(data.dd_time);

bh = unique(data.bh);

num_bh = length(bh);

for k = 1:num_bh

tmp = data(data.bh == bh(k),:);

num = height(tmp); % 有几个记录

if num == 1

R(1:tmp.cf_time, tmp.cf_tcd) = R(1:tmp.cf_time, tmp.cf_tcd)+1;

R(tmp.dd_time:end, tmp.dd_tcd) = R(tmp.dd_time:end, tmp.dd_tcd)+1;

else

for i = 1:num

if i == 1

R(1:tmp.cf_time(i), tmp.cf_tcd(i)) = R(1:tmp.cf_time(i), tmp.cf_tcd(i))+1;

elseif i == num

R(tmp.dd_time(i): end, tmp.dd_tcd(i)) = R(tmp.dd_time(i):end, tmp.dd_tcd(i))+1;

R(tmp.dd_time(i-1): tmp.cf_time(i), tmp.dd_tcd(i-1)) = R(tmp.dd_time(i-1): tmp.cf_time(i), tmp.dd_tcd(i-1))+1;

else

R(tmp.dd_time(i-1): tmp.cf_time(i), tmp.dd_tcd(i-1)) = R(tmp.dd_time(i-1): tmp.cf_time(i), tmp.dd_tcd(i-1))+1;

end

end

end

end

这一问也可以采用枚举法:先来生成路径的排列组合(不到10s出结果了)

tic

PATH = perms(2:10);

num = size(PATH,1);

PATH = [ones(num,1),PATH];

CLS = zeros(num,1); % 保存每条路径能够消毒的车辆数

for i = 1:num

CLS(i) = calculate_num(PATH(i,:),mean_tcd,R);

end

toc

历时 4.385277 秒。

function  result =  calculate_num(path,mean_tcd,R)
%    1    10     9     8     7     6     5     4     3     2
    start_clock = 60 * 9;  % 在上午9点开始消毒
    tlsj = 10; % 每个停车点停留时间是10分钟
    num = length(path);
    result = 0;
    for i = 1:num
       result = result + max(R(start_clock: start_clock+tlsj-1, path(i)));
       if i < num  
           start_clock = ceil(start_clock + tlsj + mean_tcd(path(i),path(i+1)) / 1.5);
       end
    end
end
[max_cls,ind] = max(CLS);

disp('最优路线是:')

最优路线是:

PATH(ind,:)

ans = 1×10

1 3 7 6 2 8 4 10 5 9

disp('能够消毒的车辆数最多是:')

能够消毒的车辆数最多是:

max_cls

max_cls =

943

根据附件1,如何在某个时间段内对停车点的共享单车需求程度进行评估,给出评价标准,并指出从450到600这个时间段,对共享单车需求程度最高的3个停车点。

这里考虑一种最简单的定义需求的关系:

如果某个停车点第x分钟有f(x)辆车,第x+1分钟有f(x+1)辆车

这一分钟的需求量是max(f(x)-f(x+1),0)辆车(车被借走了才有需求)

同理可以考虑一段时间的需求量,只需要进行一个累计求和即可。

r1 = R(450:599,:);

r2 = R(451:600,:);

demand = sum(max(r1-r2,0))

demand = 1×10

123 84 102 95 116 111 112 100 97 95

上面这种需求方式只考虑了借车的需求,如果同时考虑还车的需求,可以这样做:

% r1 = R(450:599,:);
% r2 = R(451:600,:);
% demand = sum(abs(r1-r2))

还有一个小问题:按照这个需求关系的定义:如果某一时刻借出一辆然后同时还回一辆不是需求量就变成零了吗?
解决方法:上面我们生成矩阵的时候时间是以1分钟分割的,如果你把时间划分的更加精细一点,比如以0.1分钟来分割,就可以减小这个误差。但这个时候的R矩阵会比原来的大10倍,这对matlab来说还是能够应付的过来的,大家有兴趣可以自己实现这个代码。

下面是画图(matlab2021a版本可以运行,2017a版本会报错,其他版本请自己测试)

figure1 = figure('NumberTitle','off','Name','Figure','Color',[1 1 1]);

% 创建 axes

axes1 = axes('Parent',figure1);

hold(axes1,'on');

% 创建 bar

b = bar(demand,'DisplayName',' 需求');

% 添加数据点

xtips1 = b(1).XEndPoints;

ytips1 = b(1).YEndPoints;

labels1 = string(b(1).YData);

text(xtips1,ytips1,labels1,'HorizontalAlignment','center',...

'VerticalAlignment','bottom')

% 创建 plot

plot([0,11],[mean(demand),mean(demand)],'DisplayName',' 均值','Tag','mean y','LineStyle','-.',...

'Color',[0 0.5 0]);

box(axes1,'on');

hold(axes1,'off');

% 设置其余坐标区属性

set(axes1,'XTick',[1 2 3 4 5 6 7 8 9 10]);

% 创建 legend

legend(axes1,'show');

数学建模共享单车问题_第1张图片

 

2019年,宁波市发布了文件《关于规范宁波市互联网租赁自行车发展的若干意见》(附件3),文件中提到:“按照交通相宜、规模适度、因地制宜、方便公众的原则,科学编制非机动车停放区域设置规划” 。公司根据文件精神,要撤销2个停车点。具体操作为,在附件1中的所有借还车行为结束后,移除这两个停车点的车辆,并运送到其他城市。请给出撤销停车点的评判标准,并指出应撤销哪两个停车点?

这题就很简单了,可以考虑很多指标

比如上一问定义的需求量 波动情况等

你可能感兴趣的:(数学建模共享单车问题)