程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 任意數學表達式計算

任意數學表達式計算

編輯:關於VC++

1.編程思路

任何表達式可分為3類:1.簡單表達式,即只帶加減乘除的運算表達式,如1+2,1+3/2,1*2*3+4*6等。2.只帶有函數符號不帶括號的表達式,如sin[30],exp[3+4*5],sin[1+sin[2]]等。3.一般表達式,即帶有括號,簡單表達式以及符號函數的表達式,如(1+2+sin[30])*3,2+sin[1+(sin[30]+1)*2]等。

基本算法流程圖:

於是問題歸結為幾個子過程:1.判斷表達式中是否存在括號 2.獲取最內層括號內容 3.無括號計算函數 4.給定字符串位置,用已知字符串代替原字符串內容。

步驟1,2,4分別可以用一個函數實現:

//判斷表達式中是否有括號
bool IsContainBracket(CString str);
//若存在括號,則獲得最內層括號位置
void GetBracketPos(CString str,int *start,int *end);
//用一段字符代替兩個位置之間的內容
CString ReplaceBetweenPos(CString toBeReplace,CString str,int start,int end);

步驟3較復雜,可以分解為幾個子步驟,思路如下:

於是原問題轉換為:1.判斷表達式中是否存在符號函數,程序中支持的函數有:sin,cos,tan,asin,acos,atan,exp,log,sign,pow。2.簡單表達式計算函數。3. 獲得最內層符號函數類型 4. 獲得符號函數的參數 5. 根據參數和符號函數類型計算結果 6.代替過程。

步驟1,3,4,5,6可以有單個函數實現,步驟2為一過程,可以分解為若干簡單步驟,實現流程圖如下:

具體函數如下:

//////////////////////////////////////////////////////
/*
基礎函數 南京航空航天大學 能源與動力學院 莊三少tel:13512524413 09.3.17

*/
//由起始位置和終止位置得到表達式兩者之間的內容
CString GetStrFromStartAndEnd(CString str,int start,int end);
//判斷表達式中是否有括號
bool IsContainBracket(CString str);
//若存在括號,則獲得最內層括號位置
void GetBracketPos(CString str,int *start,int *end);
//用一段字符代替兩個位置之間的內容
CString ReplaceBetweenPos(CString toBeReplace,CString str,int start,int end);
//判斷表達式中是否含有符號函數
bool IsContainSign(CString str);
//獲得最內層符號函數類型,即符號函數中不存在符號函數,分別返回函數名的位置和參數擴號[]的位置
int GetSignStyle(CString str,int *sing_s,int *sign_e,int *p_s,int *p_e);
//獲得參數表達式
CString GetParmString(CString str,int start,int end);
//如果是雙參數的話,則分別得到每個參數的表達式
void GetParmTwo(CString str,int start,int end,CString *s1,CString *s2);
//判斷是否存在加減乘除
bool IsJJCC(CString str);
bool IsAddExist(CString str);//+
bool IsPulsExist(CString str);//-
bool IsTimesExist(CString str);//*
bool IsDivideExist(CString str);//chu
//達到加減乘除的兩個參數
void GetJJCCTwoParm(CString str,int pos,CString *s1,CString *s2,int *p_start,int *p_end);
//若存在加減乘除的話,先判斷符號類型,再獲得加減乘除號的位置,輸入具體體判別類型,將獲得該符號從左向右的第一個位置
//調用判別函數後再調用該函數
int GetJJCCPos(CString str,CString style);
//得到加減乘除號的數目
int GetJJCCNum(CString str,CString name);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/************************************************************************/
/* 中間函數 */
/************************************************************************/
////計算無符號函數,無括號表達式的值
void GetSingleValue(CString str,double &result);
//計算含有符號函數的,無括號的表達式
void GetSignFuncVal(CString str,double &result);
//計算括號內容
void GetBracketResult(CString str,double &result);
////////計算步驟及結果存儲為文件 result.txt
FILE *resultF;
///////////預處理函數,包括將大寫字母轉換為小寫,檢查表達式合法形
BOOL Cheak(CString &str);

2. 有幾點需說明:

程序中需要判斷符號函數的位置,需加上一標記符,故規定表達式中調用符號函數是必須加上[ ],如sin[30],exp[2+3+sin[30]]等。

對於pow和log,為了使程序具有通用行,規定pow和log的參數為兩個,如根號2為pow[2,0.5], log[10,20],log[E,30]等,若輸入參數為單個,程序會出現錯誤報警:表達式非法。

一些基本常識規則在計算表達式是必須遵守,如根號下不能出現負數,即pow[-10,0.5]是違反規則的。

程序中定義了幾個基本常數,PI為3.1415926,E為2.71828。如計算ln20時,可輸入log[E,20]。

程序涉及角度計算,規定用角度,如30度角正弦函數值:sin[30]

程序中函數調用時必須小寫,如SIN,COS,EXP等大寫方式為非法調用;變量如PI,E等一定要大寫;參數X,Y,Z等務必大寫。主要原因是為了防止函數名和變量,常量混合,變量在程序中是通過替換實現的。

程序中若存在符號函數嵌套,需在內層符號函數上加括號,如:pow[2,(sin[30])].對於單個參數的函數,不加括號可以,如sin[sin[30]];對於pow和log必須在嵌套函數中加括號以保證計算的正確性。

程序中很多代碼看起來很怪,但是請勿隨意刪除,每段代碼都是調試後加上去的,目的是保證程序的健壯。如減去負數2--2,在程序中要變換為2+2.等。

每次計算均生成result.txt文件存儲計算過程,另外step[]用於存儲計算步驟,顯示計算內容,最多100步,stepNum為當前部數。

程序中定義了參數輸入函數: double GetResultFrString1(CString str,CString parm,double val);//一個未知參數
double GetResultFrString2(CString str,CString parm1,double val1,CString 
parm2,double val2);//兩個未知參數
double GetResultFrStringN(CString str,CString parm[],double val[],int N);//N個未知

參數

如要計算sin[2*X+30]在1到10的值,實現方法為:

For (int i=1;i<10;i++)
{
 Val= GetResultFrString1(“sin[2*X+30]”,”X”,double(i));
}

如要計算sin[Y*X+30]在X為1到10,Y為20時的值實現方法為:

For (int i=1;i<10;i++)
{
 Val= GetResultFrString1(“sin[Y*X+30]”,”X”,double(i),”Y”,20);
}

3.樣例分析

3.1例一:繪制任意函數的波形

結合本人編寫的DataShow類,用CExpress類計算表達式值,用DataShow顯示繪圖結果。繪圖用點越多,繪圖越精確,但時間較長,本例子中畫100個點,程序運行結果如下:

函數為:100*pow[2,(sin[X])],(注意:符號函數嵌套符號函數需加括號)

函數:100*sign[sin[X]] 注:sign為符號函數,參數為正時1,為負時得-1,為零時得0.

繪圖結果如下:

函數:200*sin[X+30]+100*sin[X/2.0] 繪圖結果:

3.2樣例二:數值計算器

輸入表達式可計算結果;

運行效果如下:

用matlab驗證計算結果:

pow[(log[2,3+2]*2*log[2,3+2]*2),0.5]

程序計算值:4.64386 matlab計算值:(log2(3+2)*2*log2(3+2)*2)^0.5 ans =4.6439

1+2+3/4+sin[30]+log[10,100]

程序計算值:6.25

matlab計算值:1+2+3/4+sin(30*3.1415/180.0)+log10(100) ans =6.2500

(注意:matlab中使用弧度)

為了檢驗程序的正確形使用6層嵌套計算結果並同matlab計算結果比較

matlab計算值

exp(sin(exp(sin(exp(sin(30*3.1415/180.0))*3.1415/180.0))*3.1415/180.0)) ans= 1.0181

(注意:matlab中使用弧度)

4. 關於CExpress的幾點說明

以前上C++課時,老師講過可以用堆棧的方法實現表達式計算,但是那時候沒好好學,所以對堆棧一點也不懂。CExpress純粹是表達式分析,實現所有功能使用的時CString類,靠的是它的成員函數如:Find, Delete,GetAT,SetAT。不存在什麼堆棧,鏈表等問題。所以在效率上有不如堆棧方法等高,但是很實用。比較滿意的一點是能實現一些復雜表達式的計算和函數計算,如:

exp[(56-(((((((sin[(1+29)]+3+2.5)+4)+sin[30]+4.5)+6)+7)+8)+9)-10)]+56-sin[30]+4.5

計算結果:62.7183

網上估計也有一些表達式計算,但是到目前為止還未找到令人興奮的代碼。我用了兩天從算法分析,程序設計,調試BUG,文章寫作總算順利。一直猶豫要不要公布代碼(包括自己很辛苦寫的DataShow類),自己學習的時候從網上得到了很多東西。自己寫的東西若對大家有用,那是編程樂趣所在,顧決定公布代碼。

若你發現Bug請聯系我;若你想獲得代碼(當然在VCKBASE上能得到)請聯系我;若你想和我交流,請聯系我。

聯系方式:TEL:13512524413,mail:[email protected]

南京航空航天大學能源與動力學院 莊三少

5.關於DataShow的說明:

DataShow是本人編寫的一個數據顯示類,可以顯示X,Y軸的數據,用它基本上可以實現一般的數據顯示,在VCKBASE的代碼下載處的C++MFC一般編程問題處可以得到它,裡面有相關文檔,說明,使用方法以及樣例程序。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved