当前位置: 首页 > 面试题库 >

快速评估大量输入值的数学表达式(函数)

詹亮
2023-03-14
问题内容

它们各自的答案使我思考如何有效地解析(或多或少受信任的)用户给出的单个数学表达(按照该答案的大致术语)来自数据库的20k到30k的输入值。我实施了一个快速而肮脏的基准测试,因此可以比较不同的解决方案。

# Runs with Python 3(.4)
import pprint
import time

# This is what I have
userinput_function = '5*(1-(x*0.1))' # String - numbers should be handled as floats
demo_len = 20000 # Parameter for benchmark (20k to 30k in real life)
print_results = False

# Some database, represented by an array of dicts (simplified for this example)

database_xy = []
for a in range(1, demo_len, 1):
    database_xy.append({
        'x':float(a),
        'y_eval':0,
        'y_sympya':0,
        'y_sympyb':0,
        'y_sympyc':0,
        'y_aevala':0,
        'y_aevalb':0,
        'y_aevalc':0,
        'y_numexpr': 0,
        'y_simpleeval':0
        })

#解决方案#1:评估[是的,完全不安全]

time_start = time.time()
func = eval("lambda x: " + userinput_function)
for item in database_xy:
    item['y_eval'] = func(item['x'])
time_end = time.time()
if print_results:
    pprint.pprint(database_xy)
print('1 eval: ' + str(round(time_end - time_start, 4)) + ' seconds')

#解决方案#2a:sympy-evalf( http://www.sympy.org)

import sympy
time_start = time.time()
x = sympy.symbols('x')
sympy_function = sympy.sympify(userinput_function)
for item in database_xy:
    item['y_sympya'] = float(sympy_function.evalf(subs={x:item['x']}))
time_end = time.time()
if print_results:
    pprint.pprint(database_xy)
print('2a sympy: ' + str(round(time_end - time_start, 4)) + ' seconds')

#解决方案#2b:sympy-lambdify( http://www.sympy.org)

from sympy.utilities.lambdify import lambdify
import sympy
import numpy
time_start = time.time()
sympy_functionb = sympy.sympify(userinput_function)
func = lambdify(x, sympy_functionb, 'numpy') # returns a numpy-ready function
xx = numpy.zeros(len(database_xy))
for index, item in enumerate(database_xy):
    xx[index] = item['x']
yy = func(xx)
for index, item in enumerate(database_xy):
    item['y_sympyb'] = yy[index]
time_end = time.time()
if print_results:
    pprint.pprint(database_xy)
print('2b sympy: ' + str(round(time_end - time_start, 4)) + ' seconds')

#解决方案#2c:sympy-使用numexpr [和numpy] lambdify(
http://www.sympy.org

from sympy.utilities.lambdify import lambdify
import sympy
import numpy
import numexpr
time_start = time.time()
sympy_functionb = sympy.sympify(userinput_function)
func = lambdify(x, sympy_functionb, 'numexpr') # returns a numpy-ready function
xx = numpy.zeros(len(database_xy))
for index, item in enumerate(database_xy):
    xx[index] = item['x']
yy = func(xx)
for index, item in enumerate(database_xy):
    item['y_sympyc'] = yy[index]
time_end = time.time()
if print_results:
    pprint.pprint(database_xy)
print('2c sympy: ' + str(round(time_end - time_start, 4)) + ' seconds')

#解决方案#3a:asteval [基于ast]-具有字符串魔术功能(
http://newville.github.io/asteval/index.html

from asteval import Interpreter
aevala = Interpreter()
time_start = time.time()
aevala('def func(x):\n\treturn ' + userinput_function)
for item in database_xy:
    item['y_aevala'] = aevala('func(' + str(item['x']) + ')')
time_end = time.time()
if print_results:
    pprint.pprint(database_xy)
print('3a aeval: ' + str(round(time_end - time_start, 4)) + ' seconds')

#解决方案#3b(纽维尔市):asteval [基于ast]-解析并运行(
http://newville.github.io/asteval/index.html)

from asteval import Interpreter
aevalb = Interpreter()
time_start = time.time()
exprb = aevalb.parse(userinput_function)
for item in database_xy:
    aevalb.symtable['x'] = item['x']
    item['y_aevalb'] = aevalb.run(exprb)
time_end = time.time()
print('3b aeval: ' + str(round(time_end - time_start, 4)) + ' seconds')

#解决方案#3c(M Newville):asteval [基于ast]-解析并使用numpy运行(
http://newville.github.io/asteval/index.html)

from asteval import Interpreter
import numpy
aevalc = Interpreter()
time_start = time.time()
exprc = aevalc.parse(userinput_function)
x = numpy.array([item['x'] for item in database_xy])
aevalc.symtable['x'] = x
y = aevalc.run(exprc)
for index, item in enumerate(database_xy):
    item['y_aevalc'] = y[index]
time_end = time.time()
print('3c aeval: ' + str(round(time_end - time_start, 4)) + ' seconds')

#解决方案#4:simpleeval [基于ast](
https://github.com/danthedeckie/simpleeval)

from simpleeval import simple_eval
time_start = time.time()
for item in database_xy:
    item['y_simpleeval'] = simple_eval(userinput_function, names={'x': item['x']})
time_end = time.time()
if print_results:
    pprint.pprint(database_xy)
print('4 simpleeval: ' + str(round(time_end - time_start, 4)) + ' seconds')

#解决方案#5 numexpr [和numpy](
https://github.com/pydata/numexpr)

import numpy
import numexpr
time_start = time.time()
x = numpy.zeros(len(database_xy))
for index, item in enumerate(database_xy):
    x[index] = item['x']
y = numexpr.evaluate(userinput_function)
for index, item in enumerate(database_xy):
    item['y_numexpr'] = y[index]
time_end = time.time()
if print_results:
    pprint.pprint(database_xy)
print('5 numexpr: ' + str(round(time_end - time_start, 4)) + ' seconds')

在旧的测试机器(Python 3.4,Linux 3.11 x86_64,两个内核,1.8GHz)上,我得到以下结果:

1 eval: 0.0185 seconds
2a sympy: 10.671 seconds
2b sympy: 0.0315 seconds
2c sympy: 0.0348 seconds
3a aeval: 2.8368 seconds
3b aeval: 0.5827 seconds
3c aeval: 0.0246 seconds
4 simpleeval: 1.2363 seconds
5 numexpr: 0.0312 seconds

突出的是 评估 的惊人速度,尽管我不想在现实生活中使用它。第二个最好的解决办法似乎是 numexpr ,这取决于 numpy的
-的依赖,我想避免的,虽然这不是一个硬性要求。下一个最好的方法是 simpleeval ,它是在 ast 周围构建的。另一个基于ast的解决方案
aeval 遭受以下事实的困扰:我必须首先将每个浮点输入值都转换为字符串,而我无法找到解决方法。 sympy
最初是我的最爱,因为它提供了最灵活,看似最安全的解决方案,但最终它与倒数第二个解决方案之间有一段令人印象深刻的距离。

更新1 :有一种使用 sympy的 方法要快得多。请参阅解决方案2b。它几乎与 numexpr 一样好,尽管我不确定 sympy
是否在内部实际使用它。

更新2 :本 sympy 实现现在使用 sympify 而不是 简化 (如建议其主要开发人员,asmeurer
-感谢)。除非明确要求使用 numexpr, 否则它不使用 numexpr (请参阅解决方案2c)。我还基于 asteval
添加了两个明显更快的解决方案(感谢M Newville)。

我必须采取哪些选择措施来进一步加快任何相对安全的解决方案的速度?是否有其他例如直接使用ast的安全(-ish)方法?


问题答案:

由于您询问了asteval,因此 一种方法可以使用它并获得更快的结果:

aeval = Interpreter()
time_start = time.time()
expr = aeval.parse(userinput_function)
for item in database_xy:
    aeval.symtable['x'] = item['x']
    item['y_aeval'] = aeval.run(expr)
time_end = time.time()

也就是说,您可以首先解析(“预编译”)用户输入函数,然后将每个新值x插入符号表中,并使用Interpreter.run()该值评估编译后的表达式的值。就您的规模而言,我认为这将使您接近0.5秒。

如果您愿意使用numpy,则可以使用混合解决方案:

aeval = Interpreter()
time_start = time.time()
expr = aeval.parse(userinput_function)
x = numpy.array([item['x'] for item in database_xy])
aeval.symtable['x'] = x
y = aeval.run(expr)
time_end = time.time()

应该更快,并且在运行时间上可以与媲美numexpr



 类似资料:
  • 问题内容: 什么是实现将采用字符串并根据运算符优先级输出结果的python程序的最佳方法(例如:“ 4 + 3 * 5”将输出19)。我在谷歌上寻找解决这个问题的方法,但是它们都太复杂了,我正在寻找一个(相对)简单的方法。 澄清:我需要比eval()稍微先进的东西-我希望能够添加其他运算符(例如,最大运算符-4 $ 2 = 4),或者,我对此在学术上比对专业更感兴趣-我想知道 该怎么 做。 问题答

  • 问题内容: 我正在试验sympy,遇到了一个我无法解决的问题。 使用scipy,我可以编写一个表达式并为x值数组求值,如下所示: 使用sympy,我可以编写相同的表达式,如下所示: 通过执行以下操作,我可以为单个值评估该表达式: 但是我无法像我使用scipy一样,想出如何对x值数组进行评估。我该怎么做? 问题答案: 首先,目前SymPy不保证支持numpy数组,在这种情况下,这就是您想要的。检查此

  • 本文向大家介绍评估后缀表达式,包括了评估后缀表达式的使用技巧和注意事项,需要的朋友参考一下 为了求解数学表达式,我们需要前缀或后缀形式。将中缀转换为后缀后,我们需要后缀评估算法来找到正确的答案。 在这里,我们还必须使用堆栈数据结构来解决后缀表达式。 从后缀表达式中,找到一些操作数后,将它们压入堆栈。找到某个运算符后,将从堆栈中弹出两个项目,并按正确的顺序执行操作。之后,结果也被压入堆栈中以备将来使

  • 问题内容: 如何解析和评估字符串(例如)中的数学表达式而不调用其数值? 在该示例中,我希望函数接受并返回。 问题答案: 我最终选择了该解决方案,该解决方案可用于对正整数和负整数进行求和(对正则表达式进行少许修改也可用于十进制): 我不确定它是否比eval()快,但是由于必须多次执行该操作,因此与创建javascript编译器实例负载相比,运行此脚本要舒服得多

  • 问题内容: 在我们的项目中,我们需要在没有任何数据库服务器的情况下评估SQL语句。您能否建议一个免费的Java库,该库能够评估基于数学的SQL语句并返回结果? 例如; 输入 输出 可能会被称为 问题答案: 如下面的代码所示,可以使用ZQL来实现。但是我严重建议您选择一个简单的嵌入式数据库,例如H2(此处为示例),而应使用它(项目运行状况要高得多)。 使用H2: 输出: 要使用它,请将其添加到您的:

  • 是OCaml内置的函数。 经验: 我的问题:的求值顺序是什么? 是先将求值为8,然后将求值为11?还是先将求值为函数,然后将用作函数的参数? 谢谢