利用C语言调用matlab引擎的经历
配置环境
在旁听完一节数学实验课后,对matlab上利用遗传算法编写的产生随机数的代码感到了兴趣,这也使我感受到了matlab丰富且强大的函数功能。但随之而来的也是好奇——能否实现在C语言环境下编写出这一套利用遗传算法产生随机数的程序呢?
于是本人上网搜集了相关资料,很遗憾地发现matlab函数库是属于不被公开的商业机密。但是matlab公司却提供了相关引擎接口,来对接C语言,所以理论上可以通过C语言调用matlab的引擎来执行matlab中的算法。
本人在网上搜集到的调用matlab引擎的方法有二:
- 一是程序运行需要完整的matlab软件进行支持;
- 二是可以将matlab中的.m文件编译为.dll文件,运行时只需MCR即可。(MATLAB Component Runtime 简称 MCR,主要用于把MATLAB编写的东西打包到没装MATLAB的机器上使用。它是一组独立的共享库,通过它能够执行在MATLAB中编写的M文件。MCR支持MATLAB语言的所有功能。)
现暂时尝试第一种方法。而调用引擎的第一步便是要配置好环境。
包含目录的路径配置
本人使用C语言编译环境为VS2019,所以首先是需要将VS的环境配置好。所以便打开要调用matlab的C语言工程文件,点开它的属性,找到“配置属性”中的“VC++目录”,将matlab中的相关文件路径添加进去。本人在matlab目录下找到相关文件:
在extern文件夹中:
有一个让学C的人感到亲切而又熟悉的include文件夹。
点开它,我们发现里面是浩瀚的.h文件海洋。
于是我们便把这个include文件夹路径添加到VS编译器的“包含目录”中。此处“包含”指那个include目录。这里要注意系统位数,matlab与VS文件的操作位数一定要对应。本人所用电脑为64位,安装的matlab亦为64位。
静态数据库的目录配置
库目录的路径添加如出一辙,所添加文件夹为include文件夹下方lib文件夹中,win64中的Microsoft文件夹,里面全是.lib文件。
附加依赖项的添加
然后再在链接器中的输入里,在附加依赖项中加 “libeng.lib;libmat.lib;libmex.lib;libmx.lib;”以便编译器引用所添加的.lib后缀文件们。
系统环境变量的配置
但这还不算完,我们最后还需要到Windows中的“高级系统设置”中找到“环境变量”来设置“系统变量”中的“Path”。
环境变量是个什么东西呢?让我们运行Win+R,在其中输入环境变量path中有的一个文件目录“system32”,会发现系统自动打开了该文件夹;而输入环境变量中没有路径的文件夹名,但系统中存在该文件夹,却不能够打开成功。所以环境变量相当于是告诉系统当用户给的指令不清楚时到里面去找路径,如果有对应的便可以快速打开。
将电脑上matlab的bin,win64文件夹添加进去。
理论上配置环境就完成了。
在测试的时候出现了一个小插曲,那便是本人在执行完以上步骤后发现C中还是无法打开”engine.h”文件。但最后解决了,问题出在VS配置管理器中“活动解决方案平台”用的还是x86位的,将其设置为x64位后报错即可消除。
遗传算法产生随机数的实现
下面是本人写的代码:
//一个在给定区间[a, b]内产生随机数的程序
#include<stdio.h>
#include<math.h>
#include<string.h>
#include"engine.h"
#define Percise 0.001
#define LBOUND 200
#define UBOUND 500
int main(void)
{
puts("Loading...\n");
engine* ep;
if (!(ep = engOpen(NULL)))
{
fprintf(stderr, "\n无法启动MATLAB引擎\n");
return EXIT_FAILURE;
} //判断引擎是否启动成功
double L[] = { LBOUND };
double U[] = { UBOUND };
mxArray*Low,*Up,* Num,*Realnum;
int range = UBOUND - LBOUND;
double number[] = { range/Percise }; //将拥有该精度时区间内数字的个数赋给number数组,number=3
Num = mxCreateDoubleMatrix(1, 1, mxREAL); //初始化了一个mxArray,该数组为1x1的double实数类型
Low= mxCreateDoubleMatrix(1, 1, mxREAL);
Up= mxCreateDoubleMatrix(1, 1, mxREAL);
Realnum = mxCreateDoubleMatrix(1, 1, mxREAL);
memcpy(mxGetPr(Low), L, sizeof(L));
memcpy(mxGetPr(Up), U, sizeof(U));
memcpy(mxGetPr(Num), number, sizeof(number)); //将number数组中的元素拷贝至matlab格式数组mxArray中,现在已有一个mxArray形式的数组了,它叫Num,储存着个数,但现在它还未被写入matlab引擎
engPutVariable(ep, "Num", Num); //将mxArray格式的数组Num输入matlab引擎
engPutVariable(ep, "Low", Low);
engPutVariable(ep, "Up", Up);
engEvalString(ep, "Kbits=fix(log(Num)/log(2))");//让matlab计算Kbits
engEvalString(ep, "Vmax=2^Kbits"); //给matlab输入命令字符串,让其执行
engEvalString(ep, "rndbin=fix(2*rand(1,Kbits))");
engEvalString(ep, "rndbin2dec=bin2dec(num2str(rndbin,'%d'))");
engEvalString(ep, "r=rndbin2dec/Vmax");
engEvalString(ep, "Realnum=Low+(Up-Low)*r");
if (Realnum= engGetVariable(ep, "Realnum")) //获取在matlab中计算的变量Vmax的值,因为在获取前Vmax在此只是一个空的储存空间
{
printf("It can be predicted that matlab has processed our order correctly!\n"); //这里准备获取答案
Num=engGetVariable(ep, "Num");
printf("Num= %f\n\n", *mxGetPr(Num));//为什么Num没有用获得函数“engGetVariabe”便可得到呢?因为Num的赋值是在此利用memcpy写入的
printf("The random number is: %f\n ", *mxGetPr(Realnum));
}
else
printf("Too bad!");
mxDestroyArray(Low);
mxDestroyArray(Up);
mxDestroyArray(Num);
mxDestroyArray(Realnum);
engClose(ep);
system("pause");
return 0;
}
具体的engine函数可以通过自行查看engine.h文件得知。
有一点需要注意的是,从C传递给matlab中的数据类型都是mxArray格式的数组矩阵,这里需要注意,通常赋值的办法便是利用memcpy函数将具体数值拷贝至该数组中。
利用engine函数很方便的一点便是,任何指令都可以通过engEvalString函数以字符串的形式传递给matlab让它直接执行,且算法的中间不需要提前在C中声明并传递给matlab,这也很好地保留了matlab语言简洁易用的特性。