前一段时间在工作中遇到这样一个实际问题,项目主体部分是用C++写的,但其中有一小部分算法是用MATLAB写的。于是乎,我第一想法是如何用C++把MATLAB中的代码实现出来。
这时,一位前辈语重心长地和我说:“可以去网上找一下MATLAB Coder。”
当时听完一脸懵逼,什么是MATLAB Coder?它有什么用?
于是在网上查阅了一些资料之后,发现MATLAB Coder实际上是MATLAB自带的一个应用程序。这个应用程序的牛X之处在于它可以直接将MATLAB代码转换成C/C++代码。
就在我以为使用MATLAB Coder可以轻松地转换时,但是在实际使用过程中依然遇到一些雷区,这些雷区是在网上查不到。
为了防止小伙伴继续“踩雷”,今天详细地讲解一下如何使用MATLAB Coder将MATLAB代码转换成C/C++代码?如何避免一些“雷区”?
01 | 准备工作
在使用MATLAB Coder前,我们需要先对MATLAB代码进行一些处理,主要包括:
(1)删除MATLAB代码中所有的注释(这是第一个“雷区”,必须要把MATLAB代码中注释全部删干净,否则无法成功转换)。
(2)删除MATLAB代码中画图和打印部分的代码,简而言之,就是把什么plot、disp之类的代码全部删除掉。如果不删除这些代码,则会导致无法成功转换,这是第二个“雷区”。
(3)如果在MATLAB代码中想要把一个未知行(列)数的数据储存在矩阵中,常规的做法是先令这个储存的矩阵A为空。
A=[];
假设要添加的若干行数据,每次添加的数据都为B。然后可以用如下的代码将要添加的数据添加进来。
A=[A;B];
这其实是最容易忽视的一个“雷区”,C/C++不允许你MATLAB存在这么牛X的方法,因此如果MATLAB代码中存在上述类型添加数据的代码,那么是无法成功转换的。
解决方案:先求出究竟需要添加多少行数据?然后再添加数据。
假设经计算需要添加m行数据,且添加数据的列数都为n,那么可以先初始化储存的矩阵A,然后再通过计数器的方式循环添加数据。
A=zeros(1,n);
count=1;
for i=1:m
A(count,:)=B;
count=count+1;
end
02 | 封装主函数
因为主函数为脚本函数,在转换之前需要先将脚本函数转换为一个常规的函数,即有明确输入和输出的函数。
以遗传算法(GA)求解旅行商问题(TSP)MATLAB代码讲解这篇推文的主函数为例,原来主函数的代码如下:
%
% @作者:随心390
% @微信公众号:优化算法交流地
%
tic
clear
clc
%% 输入数据
x=[38.24,39.57,40.56,36.26,33.48,37.56,38.42,37.52,41.23,41.17,36.08,38.47,38.15,37.51,35.49,39.36,38.09,36.09,40.44,40.33,40.37,37.57];
y=[20.42,26.15,25.32,23.12,10.54,12.19,13.11,20.44,9.100,13.05,-5.210,15.13,15.35,15.17,14.32,19.56,24.36,23,13.57,14.15,14.23,22.56];
vertexs=[x;y]';
n=length(x); %城市数目
h=pdist(vertexs);
dist=squareform(h); %距离矩阵
%% 遗传算法参数设置
NIND=50; %种群大小
MAXGEN=1000; %迭代次数
Pc=0.8; %交叉概率
Pm=0.2; %变异概率
pSwap=0.2; %选择交换结构的概率
pReversion=0.5; %选择逆转结构的概率
pInsertion=1-pSwap-pReversion; %选择插入结构的概率
N=n; %染色体长度=城市数目
%% 种群初始化
Chrom=InitPop(NIND,N);
%% 优化
gen=1; %计数器
bestChrom=Chrom(1,:); %初始全局最优个体
bestL=RouteLength(bestChrom,dist);%初始全局最优个体的总距离
BestChrom=zeros(MAXGEN,N); %记录每次迭代过程中全局最优个体
BestL=zeros(MAXGEN,1); %记录每次迭代过程中全局最优个体的总距离
while gen<=MAXGEN
%二元锦标赛选择
SelCh=BinaryTourment_Select(Chrom,dist);
%OX交叉
SelCh=Recombin(SelCh,Pc);
%变异
SelCh=Mutate(SelCh,Pm,pSwap,pReversion,pInsertion);
%将Chrom更新为SelCh
Chrom=SelCh;
%计算当前代所有个体总距离
Obj=ObjFunction(Chrom,dist);
%找出当前代中最优个体
[minObj,minIndex]=min(Obj);
%将当前代中最优个体与全局最优个体进行比较,如果当前代最优个体更好,则将全局最优个体进行替换
if minObj<=bestL
bestChrom=Chrom(minIndex,:);
bestL=minObj;
end
%记录每一代全局最优个体,及其总距离
BestChrom(gen,:)=bestChrom;
BestL(gen,:)=bestL;
%显示外层循环每次迭代的信全局最优路线的总距离
disp(['第' num2str(gen) '次迭代:全局最优路线总距离 = ' num2str(bestL)]);
%画出每次迭代的全局最优路线图
figure(1);
PlotRoute(bestChrom,x,y)
pause(0.01);
%计数器加1
gen=gen+1;
end
%% 打印每次迭代的全局最优个体的总距离变化趋势图
figure;
plot(BestL,'LineWidth',1);
title('优化过程')
xlabel('迭代次数');
ylabel('总距离');
toc
封装之后的主函数如下:
%% 封装后的主函数
%输入x,y:城市的x,y坐标
%输入NIND:种群大小
%输入MAXGEN:迭代次数
%输入Pc:交叉概率
%输入Pm:变异概率
%输入pSwap:选择交换结构的概率
%输入pReversion:选择逆转结构的概率
%输入pInsertion:选择插入结构的概率
%输出bestChrom:全局最优个体
function bestChrom=GA_TSP(x,y,NIND,MAXGEN,Pc,Pm,pSwap,pReversion,pInsertion)
%% 输入数据
vertexs=[x;y]';
n=length(x); %城市数目
h=pdist(vertexs);
dist=squareform(h); %距离矩阵
N=n; %染色体长度=城市数目
%% 种群初始化
Chrom=InitPop(NIND,N);
%% 优化
gen=1; %计数器
bestChrom=Chrom(1,:); %初始全局最优个体
bestL=RouteLength(bestChrom,dist);%初始全局最优个体的总距离
BestChrom=zeros(MAXGEN,N); %记录每次迭代过程中全局最优个体
BestL=zeros(MAXGEN,1); %记录每次迭代过程中全局最优个体的总距离
while gen<=MAXGEN
%二元锦标赛选择
SelCh=BinaryTourment_Select(Chrom,dist);
%OX交叉
SelCh=Recombin(SelCh,Pc);
%变异
SelCh=Mutate(SelCh,Pm,pSwap,pReversion,pInsertion);
%将Chrom更新为SelCh
Chrom=SelCh;
%计算当前代所有个体总距离
Obj=ObjFunction(Chrom,dist);
%找出当前代中最优个体
[minObj,minIndex]=min(Obj);
%将当前代中最优个体与全局最优个体进行比较,如果当前代最优个体更好,则将全局最优个体进行替换
if minObj<=bestL
bestChrom=Chrom(minIndex,:);
bestL=minObj;
end
%记录每一代全局最优个体,及其总距离
BestChrom(gen,:)=bestChrom;
BestL(gen,:)=bestL;
%显示外层循环每次迭代的信全局最优路线的总距离
disp(['第' num2str(gen) '次迭代:全局最优路线总距离 = ' num2str(bestL)]);
%画出每次迭代的全局最优路线图
figure(1);
PlotRoute(bestChrom,x,y)
pause(0.01);
%计数器加1
gen=gen+1;
end
%% 打印每次迭代的全局最优个体的总距离变化趋势图
figure;
plot(BestL,'LineWidth',1);
title('优化过程')
xlabel('迭代次数');
ylabel('总距离');
end
03 | 新建一个脚本函数
在封装完主函数后,删除掉原来的脚本函数GA_TSP。再新建一个脚本函数TEST,在这个脚本函数中给出封装后的主函数的输入数据,具体代码如下:
clear
clc
x=[38.24,39.57,40.56,36.26,33.48,37.56,38.42,37.52,41.23,41.17,36.08,38.47,38.15,37.51,35.49,39.36,38.09,36.09,40.44,40.33,40.37,37.57];
y=[20.42,26.15,25.32,23.12,10.54,12.19,13.11,20.44,9.100,13.05,-5.210,15.13,15.35,15.17,14.32,19.56,24.36,23,13.57,14.15,14.23,22.56];
NIND=50;
MAXGEN=1000;
Pc=0.8;
Pm=0.2;
pSwap=0.2;
pReversion=0.5;
pInsertion=1-pSwap-pReversion;
bestChrom=GA_TSP(x,y,NIND,MAXGEN,Pc,Pm,pSwap,pReversion,pInsertion);
04 | 开始转换
这里我们已经将注释、画图和打印部分的代码全部删除完毕。
STEP1:点击MATLAB Coder
STEP2:添加封装后的GA_TSP函数。
STEP3:根据提示修改代码
这里我们将OX函数进行修改,修改后的OX函数代码如下:
function [a,b]=OX(a,b)
L=length(a);
while 1
r=randi([1,L],1,2);
r1=r(1);
r2=r(2);
if r1~=r2
s=min([r1,r2]);
e=max([r1,r2]);
a0=zeros(1,L+e-s+1);
b0=zeros(1,L+e-s+1);
a0(1:e-s+1)=b(s:e);
a0(e-s+2:L+e-s+1)=a;
b0(1:e-s+1)=a(s:e);
b0(e-s+2:L+e-s+1)=b;
for i=1:length(a0)
aindex=find(a0==a0(i));
bindex=find(b0==b0(i));
if length(aindex)>1
a0(aindex(2))=[];
end
if length(bindex)>1
b0(bindex(2))=[];
end
if i==length(a)
break
end
end
a=a0;
b=b0;
break
end
end
STEP4:重复前3步操作,在STEP2点击NEXT后,直接来到定义输入变量类型这一步。
STEP5:检验运行环境
发现又报错了,按照要求修改OX函数,这次报错的原因是OX函数代码中的第10行和第11行,这两行代码是常见的第三个“雷区”,修改后的OX函数如下:
function [a0,b0]=OX(a,b)
L=length(a);
while 1
r=randi([1,L],1,2);
r1=r(1);
r2=r(2);
if r1~=r2
s=min([r1,r2]);
e=max([r1,r2]);
a0=zeros(1,L+e-s+1);
b0=zeros(1,L+e-s+1);
a0(1:e-s+1)=b(s:e);
a0(e-s+2:L+e-s+1)=a;
b0(1:e-s+1)=a(s:e);
b0(e-s+2:L+e-s+1)=b;
for i=1:length(a0)
aindex=find(a0==a0(i));
bindex=find(b0==b0(i));
if length(aindex)>1
a0(aindex(2))=[];
end
if length(bindex)>1
b0(bindex(2))=[];
end
if i==length(a)
break
end
end
break
end
end
继续重复前几步到STEP5,又发现了新的报错。
报错信息是C中if语句中的条件不能像MATLAB这么写,于是对Mutate函数做出一些修改,修改后的函数如下:
function SelCh=Mutate(SelCh,Pm,pSwap,pReversion,pInsertion)
NSel=size(SelCh,1);
for i=1:NSel
if Pm>=rand
index=Roulette(pSwap,pReversion,pInsertion);
route1=SelCh(i,:);
flag=zeros(1,3);
flag(1)= index==1;
flag(2)= index==2;
flag(3)= index==3;
if flag(1)
route2=Swap(route1);
elseif flag(2)
route2=Reversion(route1);
else
route2=Insertion(route1);
end
SelCh(i,:)=route2;
end
end
再一次重复前几步到STEP5,这一次终于成功了。
STEP6:点击Gnerate生成C代码,在MATLAB代码文件夹下多了2个文件和1个文件夹,转换出的C代码在codegen文件夹->lib文件夹->GA_TSP文件夹下。
至此,使用MATLAB Coder应用程序成功将遗传算法(GA)求解旅行商问题(TSP)MATLAB代码讲解这篇推文的MATLAB代码转换为C代码。
知乎 | bilibili:随心390
长按识别关注我们