【问题标题】:Python3.3: Square-root optimizationPython3.3:平方根优化
【发布时间】:2013-02-21 07:33:08
【问题描述】:

这里是 Python3.3 上的代码:

import sys, re, math
str1 = str(sys.stdin.readlines())
Data = re.findall('\\b\\d+\\b', str1)

for i in reversed (Data):
    print('%.4f' % math.sqrt(float(i)))

如您所见,该程序从输入中获取数据(多行随机字符串),并搜索该字符串包含的每个数字。 之后只返回它找到的每个数字的平方根。

好吧,算法有效,但速度不够快,我不知道如何优化它。 请帮帮我。我需要做什么来优化上面的代码?

【问题讨论】:

  • “数字”是指数字吗?另外,您能否向我们展示一些示例输入/输出?
  • "但不够快" -- 您正在处理的文件有多大?它有多快?您需要多快?
  • 你有多少输入?您是否配置文件以找到热点?
  • 我不认为你的问题是 math.sqrt。您可以非常快速地计算数百个数字。我在不到两秒的时间内计算并打印了 10000 个。
  • 一定要倒序吗?为什么奇怪的str(sys.stdin.readlines())?这将类似于['line1\n', 'line2\n]

标签: python optimization python-3.x square-root


【解决方案1】:

这是一个否定的结果。我尝试使用一些技巧使其更快,但它只是快一点。

import sys, re, math

def find_numbers(f):
    for line in f:
        for word in line.split():
            if word.isdigit():
                yield float(word)

lst = list(find_numbers(sys.stdin))
lst.reverse()
for x in lst:
    print('%.4f' % math.sqrt(x))

我认为反转列表可能会使其变慢,但是当我只打印数字而不反转时,它并没有太大的区别。

对于 Python,最快的解决方案是在 PyPy 中运行上述代码。

这不是一个非常困难的问题,如果您需要速度,您可能需要用 C 代码编写解决方案。 C 代码将尽可能快地解决这个问题。

【讨论】:

  • 如果常见的情况是数字,请尝试将if word.isdigit() 替换为try: yield int(word); except ValueError: pass。我不知道为什么每个人都产生浮点数,因为唯一可能的结果是整数。
  • 如果需要重复数字,您也可以记住math.sqrt
  • 这个解决方案有效,非常感谢!!!工作需要 1.85 秒,其中 ofc。适合我的目标。
  • 这里的关键思想是只收集数字。您的原始代码收集了一个列表中的所有输入行,然后将该列表转换为一个大字符串,然后使用re.findall() 收集一个子字符串列表,然后将其转换为一个数字列表。这段代码创建了一个生成器来查找数字,然后将每个数字字符串转换为数字并生成它;然后仅由平方根值构建一个列表。对我来说,这并没有比你的版本快多少,但我认为我的计算机中有很多 RAM,而你的内存更少,或者我的 RAM 更快或其他什么。
【解决方案2】:

您可以尝试使用 Numpy 加载和处理文件:

import numpy as np
for i in reversed(np.fromfile(sys.stdin, sep=' ')**0.5):
    print i

作为 Python 的高性能数值库,我希望它是您可用的最快的解决方案。

【讨论】:

【解决方案3】:

您要求使用 Python,但这可以在 C 中很好地完成。这个 C 程序不会反转数字,但您可以简单地通过 tac 程序管道输出,这类似于 cat,但反转了行。

在我的测试中,这大约是 NumPy 解决方案的 3 倍,是我的 Python 解决方案或原始解决方案的大约 6 倍。

#include <ctype.h>
#include <math.h>
#include <stdio.h>

int
main()
{
    char buf[1024];
    char ch;
    float f;
    int i, n;

    for (i = 0;;)
    {
        ch = getchar();

        if (i > sizeof(buf))
        {
            fprintf(stderr, "number too long!\n");
            return 1;
        }

        if (isspace(ch) || EOF == ch)
        {
            if (i > 0)
            {
                buf[i] = '\0';
                n = atoi(buf);
                f = sqrtf(n);
                printf("%0.4f\n", f);
                i = 0;
            }

            if (EOF == ch)
                return 0;

            continue;
        }

        buf[i++] = ch;
    }
}

【讨论】:

  • 应该能够简单地使用scanf 来获取每个数字,而无需与所有isspace 业务打交道。 (while(scanf("%d", &amp;i) == 1) printf("%0.4f\n", sqrtf(i));)
  • 我知道如何用 C/C++ 解决它,但我需要 python 3.3。无论如何感谢您的帮助:)))
  • @nneonneo,有时候你累了就写你知道的!我写过很多次这样的小状态机。但是,是的,scanf() 解决方案更简单!
  • 如何敢于为一个 python 问题提供 C 解决方案。 +1 开箱即用的思维。 :-)
  • @cfi,我喜欢 Python,如果可以的话,我会用它来做任何事情。但有时 C 是完成这项工作的好工具! :-)
【解决方案4】:

更新:对于发布 steveha's much earlier answer 的副本表示歉意。大量谈论我的阅读技巧。只是因为我对 i/o/buffering/runtime 效果的思考,现在仍然将这个答案留在网上。

原帖:

我无法相信 Python 应用一个正则表达式并计算一个平方根所花费的时间比从标准输入读取一行并在标准输出(或任何 I/O 上输出结果)所花费的时间要长)。

由于某个时间点的 I/O 将来自一个硬盘驱动器,并且会转到另一个硬盘驱动器或用户的眼睛,这应该是限制因素。

I/O 通常被缓冲以提高速度。通常缓冲区会被突发填充,然后 cpu 在等待设备提供更多数据时处于空闲状态。

这将为您的应用程序生成一个生成器。编写一个生成器,逐行读取输入,并立即按需提供一个 sqrt 数。我怀疑这会比任何合理的现代硬件上的整体 I/O 速度慢。如果您使用的是特殊设备(如嵌入式、uController、Raspberry Pi 等,请告诉我们)

您可以做的一项优化是预编译正则表达式。当您对每个测试使用相同的正则表达式时,让我们只解析一次正则表达式。您在问题中的示例很好,因为您正在执行re.findall()。我只是为其他读者详细说明。

import sys, re, math

pattern = re.compile(r'\b\d+\b')

def fh_numbers_to_sqrt(fh):
    for line in fh:
        for i in re.findall(pattern, line):
            yield math.sqrt(float(i))

numbers_g = fh_numbers_to_sqrt(sys.stdin)
for num in numbers_g:
    print('%.4f' % num)

这允许所有正则表达式和数学运算与 I/O 时间交错。

现在,我们根本无法真正优化和集成的一件事是reverse。该算法必须等到最后一个元素才能反转。

所以我们可以把调用代码改成:

numbers_g = fh_numbers_to_sqrt(sys.stdin)
for num in reverse(list(numbers_g)):
    print('%.4f' % num)

并希望这比您最初拥有的更快。 同样,这应该更快的唯一原因是因为我们已经将正则表达式解析和计算的运行时间隐藏在从标准输入读取数据所需的挂钟时间内。这仍应受 I/O 限制。实际上,reverse 可能不会真正添加到整体运行时,因为它可能会与标准输出上发生的 I/O 交错。看看挂钟,这个算法可能根本不会用完任何时间。 :-)

要证明或否定我的整个帖子,您可以用time.time() 衡量从脚本开始到Data = re.findall 行之前以及从那时到结束需要多长时间。如果我是正确的,那么数据读取将花费大部分时间。如果没有,也值得测量所有正则表达式搜索所需的时间。让我们知道。我很好奇……

【讨论】:

  • +1 以获得所有解释。我不得不认为使原始代码变慢的原因是在内存中不必要地构建大东西:首先是所有输入行的列表,然后是代表该列表的字符串,然后是另一个字符串数字列表……绝对更好随时解析文本,解析后丢弃文本,只保留数字。这就是为什么要使用生成器的原因,您在解释这一点方面做得比我做得更好。
【解决方案5】:
import sys, re, math
str1 = str(sys.stdin.readlines())
Data = re.findall('\\b\\d+\\b', str1)

d2 = [round(math.sqrt(float(i)),4) for i in reversed (Data)]

for i in d2:
    print(i)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-04
    • 2016-08-13
    • 1970-01-01
    • 2017-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多