1. 程式人生 > >c++生成算式並計算(《構建之法》第一章課後第一題)

c++生成算式並計算(《構建之法》第一章課後第一題)

c++實現計算器(自動生成算式並計算)

要滿足的需求有以下幾個:

  1. 自動生成隨機的四則運算算式,包含括號和小數。
  2. 對生成的算式計算出結果。
  3. 算式、結果分別儲存到不同的檔案。

一 生成算式

由上述需求可知,算式中有運算子('+','-','*','/','(',')'),整數,小數(重點是有小數點),同時滿足程式模組化設計的要求,所以使用STL中的string類儲存生成的算式。
生成算式時有幾點需要注意:

  • 首先生成一個數字,然後運算子('+','-','*','/')和數字交替出現,可以限定長度為奇數,在偶數位置(位置從0開始)生成數字,奇數位置生成運算子。
  • 生成數字有整數有小數。使用一個隨機變數控制生成的小數和整數的比例。
  • 生成小數時控制好位數、大小。
  • 生成括號時注意一個左括號一定要有一個右括號對應,因此設定一個變數儲存剩餘要生成的右括號的數量,即每生成一個左括號該變數加一,沒生成一個右括號該變數減一。同時控制除非到了算式最後,不會出現一對括號只括住一個數字的情況。
    在程式中,以上這些由string GenerateExpression()實現,同時它還會呼叫int GenerateRightBracket()int GenerateLeftBracket()char GenerateOperator()double GeneratNumber()int GenerateInt()

二 中綴表示式轉為逆波蘭式

逆波蘭式即字尾表示式,棧可以方便的在計算機中計算字尾表示式的值。

將一個普通的中序表示式轉換為逆波蘭表示式的一般演算法是:
首先需要分配2個棧,一個作為臨時儲存運算子的棧S1(含一個結束符號),一個作為輸入逆波蘭式的棧S2(空棧),S1棧可先放入優先順序最低的運算子#,注意,中綴式應以此最低優先順序的運算子結束。可指定其他字元,不一定非#不可。從中綴式的左端開始取字元,逐序進行如下步驟:

  • 若取出的字元是運算元,則分析出完整的運算數,該運算元直接送入S2棧
  • 若取出的字元是運算子,則將該運算子與S1棧棧頂元素比較,如果該運算子優先順序(不包括括號運算子)大於S1棧棧頂運算子優先順序,則將該運算子進S1棧,否則,將S1棧的棧頂運算子彈出,送入S2棧中,直至S1棧棧頂運算子低於(不包括等於)該運算子優先順序,最後將該運算子送入S1棧。
  • 若取出的字元是“(”,則直接送入S1棧頂。
  • 若取出的字元是“)”,則將距離S1棧棧頂最近的“(”之間的運算子,逐個出棧,依次送入S2棧,此時拋棄“(”。
  • 重複上面的1~4步,直至處理完所有的輸入字元
  • 若取出的字元是“#”,則將S1棧內所有運算子(不包括“#”),逐個出棧,依次送入S2棧。

以上來自逆波蘭式-百度百科,完成以上步驟,S2棧便為逆波蘭式輸出結果。不過S2應做一下逆序處理。

在本程式中,使用佇列(queue)取代棧S2,省去逆序處理的步驟,這些步驟由函式queue<string> ConvertToRpn(string s,map<string,int>p,map<char,int>p_char)實現。

三 計算逆波蘭式

新建一個表示式,如果當前字元為變數或者為數字,則壓棧,如果是運算子,則將棧頂兩個元素彈出作相應運算,結果再入棧,最後當表示式掃描完後,棧裡的就是結果。

計算功能由double Operation(queue<string> q)實現,同時它還會呼叫double Calculate(double n1, double n2, char c)

三 儲存到檔案

程式執行結果包括單純的中綴表示式和含結果的中綴表示式,分別儲存到不同的檔案。兩檔案分別如下:


完整程式碼

/*
    構建之法第一章習題一,實現自動生成四則運算的算式並判斷使用者計算的正誤。
    計劃分三步走:
    1.自動生成算式
    2.輸入算式轉換為逆波蘭式
    3.計算算式結果
*/
#include<iostream>
#include<string>
#include<sstream>
#include<stack>
#include<queue>
#include<map>
#include<fstream>
using namespace std;

//將中綴表示式轉換為逆波蘭式
queue<string> ConvertToRpn(string s,map<string,int>p,map<char,int>p_char)
{
    int length = s.length();
    string temp_s="";
    string temp_for_push;
    stack<string>sk1;
    queue<string>sk2;
    sk1.push("#");
    for (int i = 0; i < length;)
    {
        if (isdigit(s[i]))
        {//判斷字元是否是0~9的數字
            while (isdigit(s[i]) || s[i] == '.')
            {
                temp_s = temp_s + s[i];
                i++;
            }
            sk2.push(temp_s);
            temp_s.clear();
        }
        else
        {
            if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/'||s[i]=='^')
            {
                if (p_char[s[i]] >p[sk1.top()])
                {
                    temp_for_push.clear();
                    temp_for_push = temp_for_push + s[i];
                    sk1.push(temp_for_push);
                    i++;
                }
                else
                {
                    while (p_char[s[i]] <= p[sk1.top()])
                    {
                        sk2.push(sk1.top());
                        sk1.pop();
                    }
                    temp_for_push.clear();
                    temp_for_push = temp_for_push + s[i];
                    sk1.push(temp_for_push);
                    i++;
                }
            }
            else if (s[i] == '(')
            {
                temp_for_push.clear();
                temp_for_push = temp_for_push + s[i];
                sk1.push(temp_for_push);
                i++;
            }
            else if(s[i]==')')
            {
                while (sk1.top() != "(")
                {
                    sk2.push(sk1.top());
                    sk1.pop();
                }
                sk1.pop();
                i++;
            }
        }
        if (i == length)
        {
            while (sk1.size() != 1)
            {
                sk2.push(sk1.top());
                sk1.pop();
            }
        }
    }
    return sk2;
}

//生成隨機小數
double GeneratNumber()
{
    double number;
    int temp;
    number = ((double)rand()) / ((double)(rand()/50));
    temp = number * 10;
    number = ((double)temp) / 10;
    number = number - (int)number + (int)number % 49;
    return number;
}

//生成隨機整數
int GenerateInt()
{
    double int_number;
    int_number = rand() % 49;
    return int_number;
}

//計算逆波蘭式中簡單表示式
double Calculate(double n1, double n2, char c){
    double result = 0;
    if (c == '+'){
        result = n1 + n2;
    }
    else if (c == '-'){
        result = n2 - n1;
    }
    else if (c == '*'){
        result = n1*n2;
    }
    else if (c == '/'){
        result = n2 / n1;
    }
    return result;
}

//計算逆波蘭式
double Operation(queue<string> q)
{
    stack<double> temp_for_digit;
    char temp_for_char;
    double temp_for_push = 0;
    double num1, num2;
    double temp_result = 0;
    int length = q.size();
    stringstream ss;
    while (q.size() != 0)
    {
        if (isdigit(q.front()[0]))
        {
            ss << q.front();
            ss >> temp_for_push;
            temp_for_digit.push(temp_for_push);
            q.pop();
            ss.clear();
        }
        else
        {
            temp_for_char = q.front()[0];
            q.pop();
            num1 = temp_for_digit.top();
            temp_for_digit.pop();
            num2 = temp_for_digit.top();
            temp_for_digit.pop();
            temp_result = Calculate(num1, num2, temp_for_char);
            temp_for_digit.push(temp_result);
        }
    }
    return temp_result;
}

//生成隨機運算子
char GenerateOperator()
{
    char result;
    int which = rand() % 6;
    if (which == 0 || which == 4)
    {
        result = '+';
    }
    else if (which == 1 || which == 5)
    {
        result = '-';
    }
    else if (which == 2)
    {
        result = '*';
    }
    else if (which == 3)
    {
        result = '/';
    }
    return result;
}

//生成左括號
int GenerateLeftBracket()
{
    int result = 0;
    int whether_bracket = rand() % 7;
    if (whether_bracket ==1)
    {
        result = 1;
    }
    return result;
}

//生成右括號
int GenerateRightBracket()
{
    int result = 0;
    int whether_bracket = rand() % 7;
    if (whether_bracket <= 5)
    {
        result = 1;
    }
    return result;
}

//生成表示式
string GenerateExpression()
{
    string expression = "";
    string temp_string;
    int count_right_bracket = 0;
    int length = 3;
    int location_for_last_bracket = 0;
    length += 2*(rand() % 15);
    stringstream ss;
    double temp_num;
    int whether_int = 0;
    int whether_bracket = 0;
    for (int i = 0; i < length; i++)
    {
        whether_int = rand() % 5;
        if (i % 2 == 0)
        {
            if (whether_int <= 3)
            {//80%生成整數
                temp_num = GenerateInt();
            }
            else
            {
                temp_num = GeneratNumber();
            }
            ss << temp_num;
            ss >> temp_string;
            expression += temp_string;
            ss.clear();
            if (count_right_bracket&&i>=location_for_last_bracket+3)
            {
                if (GenerateRightBracket())
                {
                    count_right_bracket -= 1;
                    expression += ')';
                }
            }
        }
        else
        {
            expression += GenerateOperator();
            whether_bracket= GenerateLeftBracket();
            if (whether_bracket == 1)
            {
                expression += '(';
                count_right_bracket += whether_bracket;
                location_for_last_bracket = i;
            }
        }
    }
    while ((count_right_bracket--) != 0)
    {
        expression += ')';
    }
    return expression;
}

int main()
{
    map<string, int> priorites;
    priorites["+"] = 1;
    priorites["-"] = 1;
    priorites["*"] = 2;
    priorites["/"] = 2;
    priorites["^"] = 3;
    map<char, int> priorites_char;
    priorites_char['+'] = 1;
    priorites_char['-'] = 1;
    priorites_char['*'] = 2;
    priorites_char['/'] = 2;
    priorites_char['^'] = 3;
    string expression;
    queue<string> RPN;
    double result;
    int count_expression;
    ofstream just_expression, answer;
    just_expression.open("expression.txt");
    answer.open("answer.txt");
    cout << "how many expressions do you want: " << endl;
    cin >> count_expression;
    for (int i = 0; i<count_expression; i++)
    {
        expression = GenerateExpression();
        RPN = ConvertToRpn(expression,priorites,priorites_char);//得到字尾表示式
        result = Operation(RPN);
        just_expression << i+1 << ".  " << expression << endl;
        answer << i+1 << ".  " << expression << " = " << result << endl;
        expression.clear();
        RPN = queue<string>();//清空當前佇列
    }
    just_expression.close();
    answer.close();
    cout << "finished" << endl;
    system("pause");
    return 0;
}