張志波,童中翔,王超哲,李建勛,李 彬
(1.空軍工程大學 航空航天工程學院,陜西 西安710038;2.山東交通職業學院 機電工程系,山東 濰坊261206)
Matlab作為一款優秀的數學計算工具軟件,已經廣泛應用于各個領域,但其也存在著代碼效率低,執行速度慢,難以生成脫離Matlab環境的應用程序等諸多缺點。因此,廣大學者對于如何在其它編程環境下利用Matlab的數學工具進行了廣泛研究探討。在所見的方法中,利用Matcom來實現在VC等編譯環境中直接利用Matlab函數最為方便簡單,很多文獻和技術書籍都做了大量的介紹。中國地質大學的劉迎等利用Matcom的函數對合成數據和實測超聲波信號進行了可視化分析處理[1];上海交通大學的倪靜靜利用Matcomh函數實現了卡爾曼濾波[2];劉維在其編著的書籍 《精通Matlab與C/C++混合程序設計》中用一章的篇幅對其進行詳細梳理講解[3]。但是長期以來,這些方法都局限于利用Matcom提供的基本函數,對于需要將自定義函數作為參數的高級函數卻鮮有人知道如何自由地利用。例如:非線性擬合、數值積分、常微分方程求解、函數極值求解等函數。
目前對于需要傳入自定義函數作為參數的高級函數調用,一般采取的是通過Matcom將對應的m文件轉換為C/C++文件。如首都師范大學的李寶方、中國工程物理研究院機械制造工藝研究所的郭超、空軍工程大學的陳慕春、山東理工大學的侯運鑫、海軍工程大學的黃曉穎等通過轉換的C/C++文件,實現了函數擬合、神經網絡運算等等復雜的數值計算,開發了相應的功能程序[4-9]。這種應用Matcom將m文件轉換為C/C++的方法,通用性較差,每個函數都必須單獨轉換。而且實踐中發現此種方法,只允許在程序中存在一個自定義函數,對于有多個自定義函數的情況,此種方法無法解決。通過對Matcom調用自定義函數的內部機理和其提供的編譯頭文件matlab.h進行詳細分析,本文提出了一種直接在編譯環境中編寫函數,然后將其作為參數輸入的方法。這種方法可以自由地輸入自定義函數,而且允許一個程序中有多個不同的自定義函數,極大地提高了工作效率,降低了數值計算方面的難度,圓滿解決了上訴諸多問題。
利用Matcom動態鏈接庫編程,需要安裝Matcom和在編譯環境中配置相關文件,參考文獻 [3]中有詳細的說明,這里不再詳述。這樣配置后可以實現許多無需傳入自定義函數作為參數的函數應用,比如矩陣計算、矩陣分解、矩陣求逆、多項式擬合、多項式方程根的求解等,關于這方面的基本應用可查閱Matcom提供的函數手冊。如果此時按照Matcom函數手冊的說明,調用高級函數,例如數值積分,編譯無法通過,提示出錯 “feval was not linked with the program”。分析該出錯提示,很容易發現是因為函數feval沒有成功連接到工程中,導致編譯無法通過。
通過文獻 [10]提供的方法,可以在軟件的安裝目錄下找到文件feval.h和feval.cpp文件,其中就包含了feval函數的定義。但是將此文件添加進工程中后,仍不能自由地傳入自定義函數。要解決這個問題,必須分析Matcom調用自定義函數的內部機理,然后重新設計程序流程。
通過分析Matcom翻譯m文件得到的feval函數文件,結合Matlib.h頭文件,可知Matcom在調用需要傳入自定義函數作為參數的高級函數時,有一套內置的程序流程,如圖1所示。
在用戶調用ode45、quad等高級函數時,在調用用戶自定義函數之前,會隱式的調用feval函數。通過調用feval函數來隱式的調用用戶自定義函數。因為,feval函數實際上通過翻譯已有的Matlab函數文件.m文件得到的,其在翻譯過程中會將用戶自定義的函數包含進feval.cpp文件中。在feval.cpp文件中表現為代碼語句 “#include"myfun.h"” 和 ”y1 = myfun (varargin.safebr (1),varar-gin.safebr(2))”(由于在工程中翻譯而成的自定義函數為myfun,所以此處為此語句,該語句并不具有唯一性)。而“myfun.h”文件實際上就是用戶自定義函數的頭文件。通過包含頭文件myfun.h,vc等編譯器在編譯代碼時也就知道了自定義函數的地址,也就是本工程中的自定義全局函數myfun,從而調用用戶自定義函數。

圖1 原始計算流程
通過分析feval.cpp文件,可以得到如下結論:feval文件內嵌了自定義函數的頭文件,將自定義函數與feval函數綁定在一個文件中,其能夠調用的函數并不是真正意義上的自定義函數。要解決這一問題,就必須在feval文件中預留一個自定義函數的輸入接口,讓用戶能自由的將自定義函數地址傳入feval函數中,從而解決自由傳入自定義函數這一問題。
通過分析Matcom調用自定義函數的內部機理,可以按照如下的方法來實現利用VC++等編譯環境中直接編寫的自定義函數作為輸入參數進行數值積分、常微分方程求解等多種高級應用。
在feval函數中增加各種類型的函數指針,包括一元函數、二元函數等常用的自定義函數類型,從而增加一個自定義函數的外部輸入接口。在feval函數調用函數指針過程中,通過ode45、quad的高級函數傳入的參數來區別使用的函數指針類型。通過修改feval函數文件形成新的程序流程,如圖2所示,也就能實現所期望的功能。具體使用方法為,在調用ode45、quad等高級函數前,首先顯式的將自定義函數地址傳給feval中對應類型的函數指針,然后再按照自定義函數的類型,調用Matcom提供的高級函數。
通過上文的流程設計,可以很容易地修改原有“feval.cpp”文件中的代碼,實現需要的功能。具體的實現步驟為:
(1)按照文獻 [10]提供的方法,生成原始文件 “feval.h”和 “feval.cpp”。
(2)修改 “feval.cpp”文件,增加函數指針變量兩個,改寫部分代碼。

圖2 本文設計的計算流程
1)刪除語句 ‘#include"feval_myfun.h"’和 ‘#include"myfun.h"’,也就是刪除feval函數綁定的自定義函數。
2)在文件起始處添加一個頭文件、兩個函數指針變量和一個函數類型的標記變量,功能為提供自定義函數的外部接口,內容如下:

3)替換部分語句。
將語句:

替換為:

替換的目的為通過傳入的函數參數來區別自定義函數的類型。
將語句:

替換為:

替換的目的為將原始的直接調用自定義函數的方式改為通過函數指針來調用的方式。
4)將所有的語句 “#line”替換為 “//#line”,即注釋掉該行語句,這些為Matcom翻譯過程中留下的原始matlab語句。
說明一下,由于文件目錄不同,替換的代碼會有所差別。通過以上操作,我們需要的文件feval.h和feval.cpp就準備完畢。使用時需要在工程中添加這兩個文件,并且在需要調用的地方,聲明外部函數指針變量myfun1和myfun2。聲明代碼如下:

調用高級函數時,首先顯式的將函數地址賦值給類型相對應的函數指針,然后正常調用ode45、quad等高級函數。這里要特別強調,此時輸入高級函數中的函數名并不是自定義函數名,而是使用的函數指針的名稱,用于feval函數內部識別正確的函數指針。具體的使用可參見下文的算例說明。
通過將自定義函數作為參數,可以利用Matcom提供的動態鏈接庫實現許多復雜的數值計算功能,例如非線性函數擬合、數值積分、常微分方程求解、函數極值求解等,其調用的方法和需要輸入的參數和Matlab中基本一樣,只是根據C++語言的特性在形式上做了些改變。通過本文的方法,這些函數的調用方法也基本相同,只是在調用之前需要將函數地址賦值給feval.cpp文件中的函數指針變量。為了更加簡單地說明該方法如何使用,現以常見的數值積分和解常微分方程為例,詳細說明自定義的一元函數、二元函數的調用方法。
以求解式y=cosx+xex在區間[-π,π]上的積分為例進行說明。
首先需要編寫自定義函數。編寫自定義函數時需要特別注意,自定義函數必須為靜態的成員函數,或者是全局函數,只有這樣才能將函數地址賦值給函數指針。
此函數的聲明代碼如下:

函數具體實現代碼如下:

調用Matcom動態鏈接庫中的函數quad實現數值積分,具體代碼和說明如下:
myfun1=&fun1;//將自定義函數地址賦值給feval.cpp中的一元函數指針變量
Mm y=quad (TM ("myfun1"),-pi,pi);//調用函數quad,參數TM ("myfun1")表示傳入為一元函數,用于feval函數內部識別自定義函數類型

函數運行后,輸出結果如圖3所示。通過積分函數的原函數y=sinx+xex-ex可求得精確解為49.736911787753602,比較可知計算結果精確度很高。

圖3 數值積分結果
以Matlab中提供的解常微分方程的例子為例,說明使用方法。微分方程如下

需要求取時間范圍 [0,12],初值分別為0,1,1時的解。同一元函數的使用方法一樣,首先需要編寫自定義函數。函數聲明如下:

函數具體實現代碼如下:

調用Matcom動態鏈接庫中的函數ode45,實現利用四階、五階Runge-Kutta單步算法求解常微分方程。調用代碼和說明如下:

代碼運行的結果如圖4所示。將其與Matlab幫助文件提供的輸出結果 (圖5)比較,可見在VC中完美的實現了解常微分方程。

使用Matcom提供的動態鏈接庫解決數值計算問題具有很大的靈活性和簡單性,但是由于Matcom的所有計算幾乎都是來自于Matlab,也就是基于矩陣運算的思想。因此在編寫自定義函數時,需要特別注意,必須按照Matcom的規范進行書寫。這里將一些需要注意的問題和使用中的一些技巧進行說明,以便大家更容易使用。
自定義的函數,函數返回值和形參必須是Matcom可以識別的內部數據格式,也就是Mm的數據格式,而不能使用int、float、double等類型的數據格式。比如名為“functionExample”的一元函數就應該寫成如下的形式:

在函數的具體實現部分,肯定少不了乘法和除法運算。在進行編寫時,必須注意乘法和除法的代碼書寫,區別一般乘法和點乘,一般除法和點除。例如求解函數x·sin(x)的積分,按照C++語言的規則和Matcom對于自定義函數的要求,代碼應該如下:

但是,按照這種方法編寫的自定義函數,利用上文提供的求解積分方法會提示出錯,并不能得到正確的積分結果。這就是因為這里的乘法應該是點乘運算,而不是一般意義的乘法。在Matcom提供的函數庫中,利用times()函數來實現點乘運算。所以此處自定義函數的代碼應該如下:

乘法如此,除法也不例外,在編寫代碼時也必須分清一般除法和點除的區別,Matcom函數庫中的函數rdivide()來實現點除運算。
這種利用Matcom提供的動態鏈接庫進行快速數值計算的方法是通過改變Matcom調用自定義函數的流程來實現的,因此在使用過程中可以利用Matcom軟件提供很多幫助,降低難度,減少一些錯誤的發生。
(1)利用Matcom將Matlab寫成的函數文件,也就是.m文件,直接翻譯成C++文件,生產成自定義函數,能有效的降低錯誤。
(2)通過分析Matcom翻譯M文件而成的C++文件,能快速掌握Matcom提供的函數庫的使用方法。
(3)將Matlab和 Matcom有效的結合。Matcom作為一款插件性質的軟件,自帶的函數幫助文件有限,可以充分利用Matlab強大的幫助文件,快速掌握相關函數的功能和使用方法。利用Matcom將已有的M文件翻譯成C++文件能將Matlab強大的數值計算能力快速高效的移植到C++平臺,實現脫離Matlab平臺的快速數值計算。
本文設計的計算方法,實現了在VC++等編譯環境中直接編寫自定義函數作為Matcom內置函數的輸入參數,極大限度地利用Matcom提供的動態鏈接庫。該方法與目前的方法相比,具有以下優勢:
(1)函數直接在編譯環境中編寫,無需 Matcom轉化m文件,代碼簡潔高效,可讀性更強。
(2)擺脫了目前使用方法中一個程序內只能使用一個自定義函數的局限性,工程應用范圍更廣。
(3)該方法只需要 Matcom提供的動態鏈接庫及相應的頭文件,可以完全脫離Matcom軟件,獨立性更強,對于C++builder等C++編譯環境同樣適用。
研究人員只要熟悉Matlab的函數調用,就可以利用該方法快速地解決實際的數值計算問題,為脫離Matlab環境的數值計算領域提供了一種快速高效的解決方案。
[1]LIU Ying,SHI Xueming,CHEN Xiaoling,et al.Application of mixed programming with MATCOM and VC++in data visualization processing [J].Chinese Journal of Engineering Geophysics,2007,4 (5):455-459 (in Chinese). [劉迎,師學明,陳曉玲,等.VC與MATCOM聯合編程在數據可視化處理中的應用 [J].工程地球物理學報,2007,4 (5):455-459.]
[2]NI Jingjing,WANG Junpu,JIN Zhihua,et al.New method For KALMAN filter implementation using matcom and visual C++ [J].Computer Applications and Software,2008,25(5):175-176 (in Chinese). [倪靜靜,王俊璞,金志華,等.利用Matcom和Visual C++實現卡爾曼濾波的新方法 [J].計算機應用與軟件,2008,25 (5):175-176.]
[3]LIU Wei.Specialize in hybrid programming bacede on Matlab and C++ [M].2nd ed.Beijing:Beihang Univercity Press,2007:166-224 (in Chinese).[劉維.精通 Matlab與C/C++混合程序設計 [M].2版.北京:北京航空航天大學出版社,2007:166-224.]
[4]LI Baofang,GUAN Yong,SHEN Xiaoben,et al.Determination of optimal fitting to function based on VC++ and Matcom[J].Computer Engineering and Design,2007,28 (12):2980-2982(in Chinese).[李寶方,關永,沈孝本,等.基于VC++和Matcom混合編程的函數最佳擬合的確定 [J].計算機工程與設計,2007,28 (12):2980-2982.]
[5]GUO Chao,ZHOU Danchen.Man-hour quota system based on genetic neural network [J].Computer Applications and Software,2010,27 (8):205-208 (in Chinese).[郭超,周丹晨.基于遺傳神經網絡的工時定額系統 [J],計算機應用與軟件,2010,27 (8):205-208.]
[6]CHEN Muchun,WANG Xu,DONG Xiaolong.Application of VC++ and MATCOM in airplane performance program development [J].Fire Control and Command Control,2008,33(3):44-45 (in Chinese). [陳慕春,王旭,董小龍.VC++和MATCOM結合在飛機基本飛行性能程序開發中的應用[J].火力與指揮控制,2008,33 (3):44-45.]
[7]HOU Yunxin,ZHANG Guixiang,SHAO Mei,et al.Dealing with trend item of collected signals based on Matlab and VC++[J].Journal of Shandong University of Technology (Natural Science Edition),2009,23 (1):53-59 (in Chinese). [侯運鑫,張桂香,邵梅,等.基于Matcom與VC++的信號趨勢項處理 [J].山東理工大學學報 (自然的科學版),2009,23(1):53-59.]
[8]HUANG Xiaoying,TONG Yude,BIAN Shaofeng.Implementation for simulation of ICCP based on matcom [J].Journal of Geomatics Science and Technolog,2011,28 (3):186-189(in Chinese).[黃曉穎,童余德,邊少鋒.基于 Matcom混合編程的ICCP算法仿真實現 [J].測繪科學技術學報,2011,28 (3):186-189.]
[9]HU Shaoquan,ZHANG Chao.Development of vibration signal analysis module based on VC&MATLAB [J].Computer&Digital Engineering,2011,39 (7):161-164 (in Chinese). [胡紹泉,張超.基于VC和MATLAB振動信號分析模塊開發 [J].計算機與數字工程,2011,39 (7):161-164.]
[10]GU Shefeng,CUI Ruihua.The method of calling the user defined function as the input argument in mixed programming with MATCOM and VC+ + [J].Computer Programming Skills & Maintenance,2009 (22):21-23 (in Chinese). [谷社峰,崔瑞華.MATCOM與VC++混合編程中自定義函數作為輸入參數的調用方法 [J].計算機編程技巧與維護,2009 (22):21-23.]