利用Python实现四则运算表达式生成程序

一. 项目基本信息

项目成员:梁华超、林贤杰

项目仓库:Github

二. PSP2.1表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 25
· Estimate · 估计这个任务需要多少时间 20 25
Development 开发 1200 1740
· Analysis · 需求分析 (包括学习新技术) 40 55
· Design Spec · 生成设计文档 40 41
· Design Review · 设计复审 (和同事审核设计文档) 30 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 31
· Design · 具体设计 40 66
· Coding · 具体编码 1100 1520
· Code Review · 代码复审 40 41
· Test · 测试(自我测试,修改代码,提交修改) 60 64
Reporting 报告 70 100
· Test Report · 测试报告 20 24
· Size Measurement · 计算工作量 20 21
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 58
合计   1390 1963

三. 设计实现过程及代码说明

项目文件结构如下:

模块 功能
main.py 主函数
answer.py 计算答案和校对答案
exp_generate.py 表达式生成
remove_dulicate.py 题目判重
suffix_expression.py 将中缀表达式转成后缀表达式和求值

1.分析与设计

本设计涉及到的基本数据类型和表达式有栈,二叉树,逆波兰表达式(后缀表达式)

表达式生成 :

仔细分析表达式有如下特点:

  • 运算数的个数比运算符多一
  • 被除数不能为0
  • 两个操作数不需要加括号

利用python中字符串列表来存储四则表达式,新建一个列表,大小为运算符个数+运算数,然后循环遍历此列表,在偶数位置插入随机的运算数,在奇数位置插入随机的运算符。

括号的插入:

左括号的插入位置是从0到操作数个数的一半之间的一个随机数,右边括号为左括号的位置+1到操作数个数的一半+1。

计算答案:

将中缀表达式转为后缀表达式,再进行求值

题目判重:

将中缀表达式转为后缀表达式,最后转为二叉树,对转换得到的二叉树的左右子树进行自定义排序,最后生成一个字符串,比较2个表达式生成的这个序列,如果一样,则说明这两个式子是重复的。

生成的二叉树如下这样:

2.具体实现

(1) 表达式生成关键代码

需要注意的是除号后面的运算符不能为0,如果生成的是0,即重新生成插入,直到生成不为0的运算符为止。

while i < exp_num:
            random_num_operation = randint(1, config.max_num_of_oper)
            is_need_parenteses = randint(0,1)
            number_of_oprand = random_num_operation + 1 #操作数比操作符的数目多1
            exp = []
            for j in range(random_num_operation + number_of_oprand):
                if j % 2 == 0:
                    #随机生成操作数
                    exp.append(self.generate_operand(randint(0,3), config.num_range))
                    if j > 1 and exp[j-1] == ‘÷‘ and exp[j] == ‘0‘:
                        while True:
                            exp[j-1] = self.generate_operation()
                            if exp [j-1] == ‘÷‘:
                                continue
                            else:
                                break
                else:
                    #生成运算符
                    exp.append(self.generate_operation())

            #判断是否要括号
            if is_need_parenteses and number_of_oprand != 2:
                expression = " ".join(self.generate_parentheses(exp, number_of_oprand))
            else:
                expression = " ".join(exp)

            #判断是否有重复
            if self.is_repeat(exp_list, expression) or suffix_to_value(to_suffix(expression)) == False:
                continue
            else:
                exp_list.append(expression)
                i = i + 1

(2)插入括号代码逻辑

如果生成的括号表达式形如 (1 + 2/3 + 3),则认为是没有意义的括号,需要重新插入。

if exp:
            exp_length = len(exp)
            left_position = randint(0,int(num/2))
            right_position = randint(left_position+1, int(num/2)+1)
            mark = -1
            for i in range(exp_length):
                if exp[i] in [‘+‘, ‘-‘, ‘x‘, ‘÷‘]:
                    expression.append(exp[i])
                else:
                    mark += 1
                    if mark == left_position:
                        expression.append(‘(‘)
                        expression.append(exp[i])
                    elif mark == right_position:
                        expression.append(exp[i])
                        expression.append(‘)‘)
                    else:
                        expression.append(exp[i])
        #如果生成的括号表达式形如 (1 + 2/3 + 3) 则重新生成
        if expression[0] == ‘(‘ and expression[-1] ==‘)‘:
            expression = self.generate_parentheses(exp, number_of_oprand)
            return expression

(3)中缀转后缀和求值

中缀表达式转后缀表达式的逻辑:

  1. 初始化两个栈,分为运算符栈和后缀表达式栈,遍历表达式列表,如果遇到运算符:

    a. 如果运算符栈为空,则直接入栈

    b. 如果运算符栈不为空,则取出栈顶top元素

    • 如果栈顶top元素是左括号或者算术优先级高于栈顶top元素,那么就直接入栈
    • 否则就入栈后缀表达式栈
  2. 如果遇到左括号:
    • 左括号直接入运算符栈
  3. 如果遇到右括号:
    • 如果运算符栈不为空,那么直接出栈,添加到后缀表达式栈,直到遇到左括号
  4. 遇到运算数直接入后缀表达式栈
suffix_stack = []  #后缀表达式结果
    ops_stack = []  #操作符栈
    infix = exp.split(‘ ‘)
    #print(infix)
    for item in infix:
        if item in [‘+‘, ‘-‘, ‘x‘, ‘÷‘]: #遇到运算符
            while len(ops_stack) >= 0:
                if len(ops_stack) == 0:
                    ops_stack.append(item)
                    break
                op = ops_stack.pop()
                if op == ‘(‘ or ops_rule[item] > ops_rule[op]:
                    ops_stack.append(op)
                    ops_stack.append(item)
                    break
                else:
                    suffix_stack.append(op)
        elif item == ‘(‘: # 左括号直接入栈
            ops_stack.append(item)
        elif item == ‘)‘: #右括号
            while len(ops_stack) > 0:
                op = ops_stack.pop()
                if op == "(":
                    break
                else:
                    suffix_stack.append(op)
        else:
            suffix_stack.append(item) # 数值直接入栈

    while len(ops_stack) > 0:
        suffix_stack.append(ops_stack.pop())

(4)题目判重逻辑代码

import operator
import suffix_expression
?
class Node:
    """
    二叉树的结点
    """
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None
?
def generate_binary_tree(exp):
    """
    生成二叉树
    """
    tree_stack = []
    for item in exp:
        #print(item)
        parent = Node(item)
        if not item in  [‘+‘, ‘-‘, ‘x‘, ‘÷‘]:
            #操作数
            tree_stack.append(parent)
        else:
            #运算符
            right = tree_stack.pop()
            left = tree_stack.pop()
            parent.right = right
            parent.left = left
            tree_stack.append(parent)
    #二叉树的根
    parent = tree_stack[-1]
    return parent
?
def tree_is_same(root):
    """
    判断二叉树是否相同
    """
    if not root.left:
        if not root.right:
            return root.value
    elif root.value == ‘+‘ or root.value == ‘x‘:
        left = tree_is_same(root.left)
        right = tree_is_same(root.right)
        if operator.le(left, right):
            #print(root.value + left + right)
            return root.value + left + right
        else:
            return root.value + right + left
    else:
        return root.value + tree_is_same(root.left) + tree_is_same(root.right) 

四. 运行测试

文件说明:

文件 说明
Answer.txt 生成表达式答案文件
Exercises.txt 生成表达式存储的文件
Grade.txt 题目对错数量统计文件

结果:

效能分析:

  • 因为涉及到二叉树递归等操作,所以会有很多时间和空间的开销
  • IO读写也影响运算的时间

五. 项目总结

这次结对编程中,我和林贤杰一起深入分析项目的需求分析,找到实现需求的具体思路,设计具体实现的过程,我负责编码,林贤杰同学在旁边观察协助。在此过程中,我们也遇到了一些问题,也找到了解决的思路。总之,在结对编程中有很大的收获,实现1+1 > 2 。

原文地址:https://www.cnblogs.com/tworld/p/9709610.html

时间: 2024-10-17 09:49:00

利用Python实现四则运算表达式生成程序的相关文章

【个人项目总结】C#四则运算表达式生成程序

S1&2.个人项目时间估算 PSP表格如下: PSP2.1 Personal Software Process Stages Time(Before) Time(After) Planning 计划   · Estimate · 估计这个任务需要多少时间 2 4 Development 开发   · Analysis · 需求分析 (包括学习新技术) 3 5 · Design Spec · 生成设计文档 8 11 · Design Review · 设计复审 (和同事审核设计文档) 1 0 ·

利用正则表达式判断四则运算表达式是否合法

写了一个判断四则运算合法性较验的式子. public static bool CheckExpressionValid(string input) { string pattern = @"^(((?<o>\()[-+]?([0-9]+[-+*/])*)+[0-9]+((?<-o>\))([-+*/][0-9]+)*)+($|[-+*/]))*(?(o)(?!))$"; //去掉空格,且添加括号便于进行匹配 return Regex.IsMatch("

利用python进行数据分析--(阅读笔记一)

以此记录阅读和学习<利用Python进行数据分析>这本书中的觉得重要的点! 第一章:准备工作 1.一组新闻文章可以被处理为一张词频表,这张词频表可以用于情感分析. 2.大多数软件是由两部分代码组成:少量需要占用大部分执行时间的代码,以及大量不经常执行的“粘合剂代码”. cython已经成为python领域中创建编译型扩展以及对接c/c++代码的一大途径. 3.在那些要求延迟性非常小的应用程序中(例如高频交易系统),为了尽最大可能地优化性能,耗费时间使用诸如C++这样更低级.更低生产率的语言进行

四则运算表达式求值の各种心碎

实验三---四则运算表达式求值 一.基本要求: ( 1 ) 利用二叉树后序遍历来实现表达式的转换,同时可以使用实验三的结果来求解后缀表达式的值. ( 2) 输入输出格式: 输入格式:在字符界面上输入一个中缀表达式,回车表示结束. 请输入表达式: 输入一个中缀表达式 输出格式:如果该中缀表达式正确,那么在字符界面上输出其后缀表达式,其中后 缀表达式中两相邻操作数之间利用空格隔开:如果不正确,在字符界面上输出表达式错误提示. 逆波兰表达式为: 输出逆波兰表达式 运算结果为:输出运算后的结果 测试数据

利用 Python 进行数据分析(八)pandas 基本操作(Series 和 DataFrame)

一.reindex() 方法:重新索引 针对 Series   重新索引指的是根据index参数重新进行排序. 如果传入的索引值在数据里不存在,则不会报错,而是添加缺失值的新行. 不想用缺失值,可以用 fill_value 参数指定填充值. 例如: fill_value 会让所有的缺失值都填充为同一个值,如果不想这样而是用相邻的元素(左或者右)的值填充,则可以用 method 参数,可选的参数值为 ffill 和 bfill,分别为用前值填充和用后值填充: 针对 DataFrame   重新索引

《利用python进行数据分析》读书笔记 --第一、二章 准备与例子

http://www.cnblogs.com/batteryhp/p/4868348.html 第一章 准备工作 今天开始码这本书--<利用python进行数据分析>.R和python都得会用才行,这是码这本书的原因.首先按照书上说的进行安装,google下载了epd_free-7.3-1-win-x86.msi,译者建议按照作者的版本安装,EPDFree包括了Numpy,Scipy,matplotlib,Chaco,IPython.这里的pandas需要自己安装,对应版本为pandas-0.

Java四则运算表达式求解

压栈思想计算Java运算表达式 栈的规则是先进后出.利用压栈的思想来计算四则运算表达式是这样的:我们给定两个栈,一个用来存放数字.一个用来存放对应的操作符.假定我们有一个给定的四则运算表达式a+b+c/d*(e+f)-d*a,那我们先把这个表达式拆分成一个个的数字或者是运算符.或者就是括号了.然后我们从左至右遍历每一个元素,遍历过程中遵循步骤和原则如下: (1)遇到数字则直接压到数字栈顶. (2)遇到运算符(+-*/)时,若操作符栈为空,则直接放到操作符栈顶,否则,见(3). (3)若操作符栈顶

利用PYTHON设计计算器功能

通过利用PYTHON 设计处理计算器的功能如: 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 ))- (-4*3)/(16-3*2)) 我的处理计算基本思路是: 解题思路是,需要优先处理内层括号运算--外层括号运算--先乘除后加减的原则:1.正则处理用户输入的字符串,然后对其进行判断,判断计算公式是否有括号,有就先将计算公式进行正则处理,先获取最里层的每一个数据,然后一一计算 所要用到的正则是: inner

个人作业1——四则运算题目生成程序

一.题目描述: 实践能力的提高当然就是得多动手了,那么就从第一个个人项目开始吧,用一周的时间完成一个基于控制台的四则运算程序,实现一个自动生成小学四则运算题目的命令行程序. 从<构建之法>第一章的 "程序" 例子出发,像阿超那样,花二十分钟写一个能自动生成小学四则运算题目的命令行 "软件",满足以下需求: (以下参考博客链接:http://www.cnblogs.com/jiel/p/4810756.html 1. 使用 -n 参数控制生成题目的个数,例