是否有一种方法可以自动生成有效的算术表达式?

2024-10-01 09:20:41 发布

您现在位置:Python中文网/ 问答频道 /正文

我目前正在尝试创建一个Python脚本,它将自动生成有效的空格分隔的算术表达式。但是,我得到的示例输出如下:( 32 - 42 / 95 + 24 ( ) ( 53 ) + ) 21

虽然空括号完全可以,但我不能在计算中使用这个自动生成的表达式,因为24和53之间没有运算符,并且结尾21之前的+没有第二个参数。在

我想知道的是,有没有一种方法可以使用python解决方案来解释/修复这些错误?(在有人指出这一点之前,我将首先承认,我在下面发布的代码可能是我推出的最差的代码,并且符合……好吧,几乎没有Python的核心原则。)

import random
parentheses = ['(',')']
ops = ['+','-','*','/'] + parentheses

lines = 0

while lines < 1000:
    fname = open('test.txt','a')
    expr = []
    numExpr = lines
    if (numExpr % 2 == 0):
        numExpr += 1
    isDiv = False # Boolean var, makes sure there's no Div by 0

    # isNumber, isParentheses, isOp determine whether next element is a number, parentheses, or operator, respectively
    isNumber = random.randint(0,1) == 0 # determines whether to start sequence with number or parentheses
    isParentheses = not isNumber
    isOp = False
    # Counts parentheses to ensure parentheses are matching
    numParentheses = 0
    while (numExpr > 0 or numParentheses > 0):
        if (numExpr < 0 and numParentheses > 0):
            isDiv = False
            expr.append(')')
            numParentheses -= 1
        elif (isOp and numParentheses > 0):
            rand = random.randint(0,5)
            expr.append(ops[rand])
            isDiv = (rand == 3) # True if div op was just appended
            # Checks to see if ')' was appended
            if (rand == 5):
                isNumber = False
                isOp = True
                numParentheses -= 1
            # Checks to see if '(' was appended
            elif (rand == 4):
                isNumber = True
                isOp = False
                numParentheses += 1
            # All other operations go here
            else:
                isNumber = True
                isOp = False
        # Didn't add parentheses possibility here in case expression in parentheses somehow reaches 0
        elif (isNumber and isDiv):
            expr.append(str(random.randint(1,100)))
            isDiv = False
            isNumber = False
            isOp = True
        # If a number's up, decides whether to append parentheses or a number
        elif (isNumber):
            rand = random.randint(0,1)
            if (rand == 0):
                expr.append(str(random.randint(0,100)))
                isNumber = False
                isOp = True
            elif (rand == 1):
                if (numParentheses == 0):
                    expr.append('(')
                    numParentheses += 1
                else:
                    rand = random.randint(0,1)
                    expr.append(parentheses[rand])
                    if rand == 0:
                        numParentheses += 1
                    else:
                        numParentheses -= 1
            isDiv = False
        numExpr -= 1

    fname.write(' '.join(expr) + '\n')
    fname.close()
    lines += 1

Tags: tofalsetrueifrandomrandintappendexpr
3条回答

我在类似的任务中发现了这个线程,即为符号计算的单元测试生成随机表达式。在我的版本中,我包含了一元函数并允许符号是任意字符串,也就是说,可以使用数字或变量名。在

from random import random, choice

UNARIES = ["sqrt(%s)", "exp(%s)", "log(%s)", "sin(%s)", "cos(%s)", "tan(%s)",
           "sinh(%s)", "cosh(%s)", "tanh(%s)", "asin(%s)", "acos(%s)",
           "atan(%s)", "-%s"]
BINARIES = ["%s + %s", "%s - %s", "%s * %s", "%s / %s", "%s ** %s"]

PROP_PARANTHESIS = 0.3
PROP_BINARY = 0.7

def generate_expressions(scope, num_exp, num_ops):
    scope = list(scope) # make a copy first, append as we go
    for _ in xrange(num_ops):
        if random() < PROP_BINARY: # decide unary or binary operator
            ex = choice(BINARIES) % (choice(scope), choice(scope))
            if random() < PROP_PARANTHESIS:
                ex = "(%s)" % ex
            scope.append(ex)
        else:
            scope.append(choice(UNARIES) % choice(scope))
    return scope[-num_exp:] # return most recent expressions

从前面的答案中复制,我只是提出了一些关于概率为PROP_PARANTHESIS的二元运算符(这有点作弊)。二元运算符比一元运算符更常见,因此我也将其留作配置(PROP_BINARY)。例如:

^{pr2}$

这将产生类似于:

e / acos(tan(a)) / a * acos(tan(a)) ** (acos(tan(a)) / a + a) + (d ** b + a)
(a + (a ** sqrt(e)))
acos((b / acos(tan(a)) / a + d) / (a ** sqrt(e)) * (a ** sinh(b) / b))
sin(atan(acos(tan(a)) ** (acos(tan(a)) / a + a) + (d ** b + a)))
sin((b / acos(tan(a)) / a + d)) / (a ** sinh(b) / b)
exp(acos(tan(a)) / a + acos(e))
tan((b / acos(tan(a)) / a + d))
acos(tan(a)) / a * acos(tan(a)) ** (acos(tan(a)) / a + a) + (d ** b + a) + cos(sqrt(e))
(acos(tan(a)) / a + acos(e) * a + e)
((b / acos(tan(a)) / a + d) - cos(sqrt(e))) + sinh(b)

放入PROP_BINARY = 1.0并使用

scope = range(100)

把我们带回到输出

43 * (50 * 83)
34 / (29 / 24)
66 / 47 - 52
((88 ** 38) ** 40)
34 / (29 / 24) - 27
(16 + 36 ** 29)
55 ** 95
70 + 28
6 * 32
(52 * 2 ** 37)

实际上,只要Ray Toal的响应是形式上正确的,对于这样一个简单的问题,您不必对每个运算符*进行子类化。我想出了以下代码,它运行得很好:

import random
import math


class Expression(object):
    OPS = ['+', '-', '*', '/']

    GROUP_PROB = 0.3

    MIN_NUM, MAX_NUM = 0, 20

    def __init__(self, maxNumbers, _maxdepth=None, _depth=0):
        """
        maxNumbers has to be a power of 2
        """
        if _maxdepth is None:
            _maxdepth = math.log(maxNumbers, 2) - 1

        if _depth < _maxdepth and random.randint(0, _maxdepth) > _depth:
            self.left = Expression(maxNumbers, _maxdepth, _depth + 1)
        else:
            self.left = random.randint(Expression.MIN_NUM, Expression.MAX_NUM)

        if _depth < _maxdepth and random.randint(0, _maxdepth) > _depth:
            self.right = Expression(maxNumbers, _maxdepth, _depth + 1)
        else:
            self.right = random.randint(Expression.MIN_NUM, Expression.MAX_NUM)

        self.grouped = random.random() < Expression.GROUP_PROB
        self.operator = random.choice(Expression.OPS)

    def __str__(self):
        s = '{0!s} {1} {2!s}'.format(self.left, self.operator, self.right)
        if self.grouped:
            return '({0})'.format(s)
        else:
            return s


for i in range(10):
    print Expression(4)

尽管可以改进它来考虑诸如按零除(目前还没有处理)、通过属性定制所有参数、允许maxNumbers参数的任何值等等。在

*我所说的“简单问题”是指“生成有效表达式”;如果您添加了任何其他功能(例如,表达式求值),那么Ray的方法将付出代价,因为您可以以更干净的方式定义每个子类的行为。在

编辑(输出):

^{pr2}$

是的,你可以用Python的方式生成随机的算术表达式。不过,你需要改变你的方法。不要试图生成一个字符串并计算parens数。而是生成一个随机的表达式树,然后输出它。在

我所说的表达式树是一个类的实例,例如,Expression,子类为NumberPlusExpression,MinusExpression, 'TimesExpressionDivideExpression,和{}。除了Number之外,每个都有Expression类型的字段。给每个人一个合适的__str__方法。生成一些随机表达式对象并打印“根”

你能把它从这里拿走还是让我把它编码?在

附录:一些启动程序代码示例。不会生成随机表达式(还没有?)但这可以补充。。。。在

# This is just the very beginning of a script that can be used to process
# arithmetic expressions.  At the moment it just defines a few classes
# and prints a couple example expressions.

# Possible additions include methods to evaluate expressions and generate
# some random expressions.

class Expression:
    pass

class Number(Expression):
    def __init__(self, num):
        self.num = num

    def __str__(self):
        return str(self.num)

class BinaryExpression(Expression):
    def __init__(self, left, op, right):
        self.left = left
        self.op = op
        self.right = right

    def __str__(self):
        return str(self.left) + " " + self.op + " "  + str(self.right)

class ParenthesizedExpression(Expression):
    def __init__(self, exp):
        self.exp = exp

    def __str__(self):
        return "(" + str(self.exp) + ")"

e1 = Number(5)
print e1

e2 = BinaryExpression(Number(8), "+", ParenthesizedExpression(BinaryExpression(Number(7), "*", e1)))
print e2

**附录2**

回到Python真的很有趣。我忍不住实现了随机表达式生成器。它是基于上面的代码构建的。很抱歉硬编码!!在

^{pr2}$

以下是我得到的输出:

(23)
86 + 84 + 87 / (96 - 46) / 59
((((49)))) + ((46))
76 + 18 + 4 - (98) - 7 / 15
(((73)))
(55) - (54) * 55 + 92 - 13 - ((36))
(78) - (7 / 56 * 33)
(81) - 18 * (((8)) * 59 - 14)
(((89)))
(59)

一点都不漂亮。我想这会让很多家长感到厌烦。也许改变在括号表达式和二进制表达式之间选择的概率可能会很好。。。。在

相关问题 更多 >