测试回文的各种方法的性能 [Python] [英] Performance of Various Methods to Test for a Palindrome [Python]

查看:47
本文介绍了测试回文的各种方法的性能 [Python]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

今天,我在玩几个编程难题.面对测试字符串以查看它是否为回文的任务,我想到了几种方法来完成此任务.这三种方法的基础描述如下(省略了大部分整理和测试代码).

Today, I was fooling around with a couple of programming puzzles. Faced with the task of testing a string to see whether or not it is a palindrome, I conceived of several ways to accomplish this. The basics of these three methods are depicted below (most neatifying and testing code is omitted).

def check_palin(victim, method):
if method is 1: # check progressively inner chars
    x = 0
    while x < (len(victim)/2): # len/2 is num of iter needed for guarantee
        if victim[x+0] is victim[-(1+x)]: # on pass n, compare nth letter
                                            # and nth to last letter
            x += 1 # then increment the n counter
        else:
            return False
    return True
elif method is 2: # check first and last chars repeatedly
    tmp = []
    for i in victim:
        tmp.append(i) # convert string into list
    while len(tmp) > 1: # if 1 or 0 char left, palin is guaranteed
        if tmp[0] is tmp[-1]: # if the first and last characters are the same letter
            tmp.pop(0)  # remove them both
            tmp.pop(-1)
        else:
            return False
    return True
elif method is 3: # reverse string and compare to original
    tmp = ""
    for i in victim: # for every letter
        tmp = i + tmp # cat it to the beginning, not append          
    return tmp == victim
else:
    return -1

方法 1 利用这样一个事实,即字符串中的字符可以像列表中的元素一样被索引.我们可以这样想象这种方法:你从一个单词的第一个和最后一个字母下面开始;每次迭代时,首先检查手指上方的字母是否相同;如果它们不同,则该词不是回文;如果它们相同,则将每个手指向单词中心移动 1 个字母并重复.

Method 1 takes advantage of the fact that characters in a string can be indexed like elements of a list. We can imagine this method thusly: you start with your fingers under the first and last letters of a word; with each iteration, you first check whether the letters above your fingers are the same; if they are different, the word is not a palindrome; if they are the same, you move each of your fingers 1 letter toward the center of the word and repeat.

此方法的大部分计算将是条件测试、索引切片和比较.还有一个计数器变量,它是索引切片计算的常量部分.

The bulk of computation with this method would be the condition testing, index slicing, and comparisons. There is also a counter variable which is a constant part of a calculation for the index slicing.

方法 2 还使用字符串中的字符索引.比较第一个和最后一个字符,然后丢弃,并重复这些步骤,直到保证(或否定)回文.

Method 2 also employs the indexing of characters in a string. The first and last characters are compared, then discarded, and these steps are repeated until a palindrome is guaranteed (or disproven).

成本与方法 1 类似,但有一些区别:添加了从 str -> 列表的转换,从列表中弹出元素,并减去了一个计数器变量.

Costs would be similar to Method 1, with some differences: the addition of conversion from str -> list, popping elements from a list, and minus a counter variable.

方法 3 反转给定的字符串,然后将其与原始字符串进行比较.有多种方法可以反转字符串(list.__reversed__() 等),但我只展示了一种这样的可能性:将字符串转换为列表,然后将每个字符串连接起来该列表的元素到新字符串的开始.

Method 3 reverses the given string, and then compares it to the original. There are various ways to reverse a string (list.__reversed__(), etc.), but I've only shown one such possibility: converting the string to a list, and then concatenating each element of that list to the BEGINNING of a new string.

对于反转字符串的不同方法,可能涉及不同的操作,从而涉及不同的成本.对于我在这里选择的方法,我们需要将列表中的每个元素切片并将其与 str 变量连接起来.

With different methods for reversing a string, there may be different operations, and thus costs, involved. For my chosen method here, we have the cost of slicing each and every element from a list and concatenating it with a str variable.

这些方法中哪个执行速度最快,为什么?另外,有什么办法可以提高这些方法的效率吗?(顺便说一句,你如何测试 Python 中模块的执行速度?)

Which of these methods would be the fastest executing and why? Also, is there any way to improve the efficiency of these methods? (On a tangent, how do you test the execution speed of modules in Python?)

推荐答案

使用 timeit 模块用于速度测试,profile 模块用于性能统计数据,以及用于字节码反汇编的 dis 模块.

Use the timeit module for speed-testing, the profile module for performance statistics, and the dis module for bytecode disassembly.

下面的脚本演示了如何使用模块.

The script below demonstrates how the modules can be used.

从输出中需要注意的一件事是函数调用的数量对整体性能的影响有多大(当然,字节码指令的数量也是如此).

One thing to notice from the output is how much the number of function calls can affect the overall performance (and, of course, the same thing goes for the number of bytecode instructions).

希望,这(以及更多的实验)应该为您提供有关如何提高函数效率的足够线索.

Hopefully, that (and a little more experimentation) should give you enough clues on how to improve the efficiency of your functions.

from timeit import timeit
from cProfile import runctx
from dis import dis

def analyse(*args):
    victim = 'detartrated'
    number = 1000
    for func in args:
        print('\n%s\n' % ('#' * 50))
        name = func.__name__
        print('test: %s(%r): %r' % (name, victim, func(victim)))
        code = '%s(%r)' % (name, victim)
        duration = timeit(
            code, 'from __main__ import %s' % name, number=number)
        usec = 1000000 * duration / number
        print('time: %s: %.2f usec/pass\n' % (code, usec))
        runctx(code, globals(), locals())
        dis(func)

def check_palin1(victim):
    """ check progressively inner chars """
    x = 0
    # len/2 is num of iter needed for guarantee
    while x < (len(victim)/2):
        # on pass n, compare nth letter and nth to last letter
        if victim[x+0] is victim[-(1+x)]:
            # then increment the n counter
            x += 1
        else:
            return False
    return True

def check_palin2(victim):
    """ check first and last chars repeatedly """
    tmp = []
    for i in victim:
        # convert string into list
        tmp.append(i)
    # if 1 or 0 char left, palin is guaranteed
    while len(tmp) > 1:
        # if the first and last characters are the same letter
        if tmp[0] is tmp[-1]:
            # remove them both
            tmp.pop(0)
            tmp.pop(-1)
        else:
            return False
    return True

def check_palin3(victim):
    """ reverse string and compare to original using a loop """
    tmp = ""
    # for every letter
    for i in victim:
        # cat it to the beginning, not append
        tmp = i + tmp
    return tmp == victim

def check_palin4(victim):
    """ reverse string and compare to original using slice syntax """
    return victim == victim[::-1]

analyse(check_palin1, check_palin2, check_palin3, check_palin4)

输出:

##################################################

test: check_palin1('detartrated'): True
time: check_palin1('detartrated'): 3.80 usec/pass

         9 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 test.py:20(check_palin1)
        6    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


 22           0 LOAD_CONST               1 (0)
              3 STORE_FAST               1 (x)

 24           6 SETUP_LOOP              72 (to 81)
        >>    9 LOAD_FAST                1 (x)
             12 LOAD_GLOBAL              0 (len)
             15 LOAD_FAST                0 (victim)
             18 CALL_FUNCTION            1
             21 LOAD_CONST               2 (2)
             24 BINARY_DIVIDE       
             25 COMPARE_OP               0 (<)
             28 POP_JUMP_IF_FALSE       80

 26          31 LOAD_FAST                0 (victim)
             34 LOAD_FAST                1 (x)
             37 LOAD_CONST               1 (0)
             40 BINARY_ADD          
             41 BINARY_SUBSCR       
             42 LOAD_FAST                0 (victim)
             45 LOAD_CONST               3 (1)
             48 LOAD_FAST                1 (x)
             51 BINARY_ADD          
             52 UNARY_NEGATIVE      
             53 BINARY_SUBSCR       
             54 COMPARE_OP               8 (is)
             57 POP_JUMP_IF_FALSE       73

 28          60 LOAD_FAST                1 (x)
             63 LOAD_CONST               3 (1)
             66 INPLACE_ADD         
             67 STORE_FAST               1 (x)
             70 JUMP_ABSOLUTE            9

 30     >>   73 LOAD_GLOBAL              1 (False)
             76 RETURN_VALUE        
             77 JUMP_ABSOLUTE            9
        >>   80 POP_BLOCK           

 31     >>   81 LOAD_GLOBAL              2 (True)
             84 RETURN_VALUE        

##################################################

test: check_palin2('detartrated'): True
time: check_palin2('detartrated'): 10.57 usec/pass

         30 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 test.py:33(check_palin2)
        6    0.000    0.000    0.000    0.000 {len}
       11    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
       10    0.000    0.000    0.000    0.000 {method 'pop' of 'list' objects}


 35           0 BUILD_LIST               0
              3 STORE_FAST               1 (tmp)

 36           6 SETUP_LOOP              27 (to 36)
              9 LOAD_FAST                0 (victim)
             12 GET_ITER            
        >>   13 FOR_ITER                19 (to 35)
             16 STORE_FAST               2 (i)

 38          19 LOAD_FAST                1 (tmp)
             22 LOAD_ATTR                0 (append)
             25 LOAD_FAST                2 (i)
             28 CALL_FUNCTION            1
             31 POP_TOP             
             32 JUMP_ABSOLUTE           13
        >>   35 POP_BLOCK           

 40     >>   36 SETUP_LOOP              75 (to 114)
        >>   39 LOAD_GLOBAL              1 (len)
             42 LOAD_FAST                1 (tmp)
             45 CALL_FUNCTION            1
             48 LOAD_CONST               1 (1)
             51 COMPARE_OP               4 (>)
             54 POP_JUMP_IF_FALSE      113

 42          57 LOAD_FAST                1 (tmp)
             60 LOAD_CONST               2 (0)
             63 BINARY_SUBSCR       
             64 LOAD_FAST                1 (tmp)
             67 LOAD_CONST               3 (-1)
             70 BINARY_SUBSCR       
             71 COMPARE_OP               8 (is)
             74 POP_JUMP_IF_FALSE      106

 44          77 LOAD_FAST                1 (tmp)
             80 LOAD_ATTR                2 (pop)
             83 LOAD_CONST               2 (0)
             86 CALL_FUNCTION            1
             89 POP_TOP             

 45          90 LOAD_FAST                1 (tmp)
             93 LOAD_ATTR                2 (pop)
             96 LOAD_CONST               3 (-1)
             99 CALL_FUNCTION            1
            102 POP_TOP             
            103 JUMP_ABSOLUTE           39

 47     >>  106 LOAD_GLOBAL              3 (False)
            109 RETURN_VALUE        
            110 JUMP_ABSOLUTE           39
        >>  113 POP_BLOCK           

 48     >>  114 LOAD_GLOBAL              4 (True)
            117 RETURN_VALUE        

##################################################

test: check_palin3('detartrated'): True
time: check_palin3('detartrated'): 2.77 usec/pass

         3 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 test.py:50(check_palin3)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


 52           0 LOAD_CONST               1 ('')
              3 STORE_FAST               1 (tmp)

 54           6 SETUP_LOOP              24 (to 33)
              9 LOAD_FAST                0 (victim)
             12 GET_ITER            
        >>   13 FOR_ITER                16 (to 32)
             16 STORE_FAST               2 (i)

 56          19 LOAD_FAST                2 (i)
             22 LOAD_FAST                1 (tmp)
             25 BINARY_ADD          
             26 STORE_FAST               1 (tmp)
             29 JUMP_ABSOLUTE           13
        >>   32 POP_BLOCK           

 57     >>   33 LOAD_FAST                1 (tmp)
             36 LOAD_FAST                0 (victim)
             39 COMPARE_OP               2 (==)
             42 RETURN_VALUE        

##################################################

test: check_palin4('detartrated'): True
time: check_palin4('detartrated'): 0.65 usec/pass

         3 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 test.py:59(check_palin4)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


 61           0 LOAD_FAST                0 (victim)
              3 LOAD_FAST                0 (victim)
              6 LOAD_CONST               1 (None)
              9 LOAD_CONST               1 (None)
             12 LOAD_CONST               2 (-1)
             15 BUILD_SLICE              3
             18 BINARY_SUBSCR       
             19 COMPARE_OP               2 (==)
             22 RETURN_VALUE        

这篇关于测试回文的各种方法的性能 [Python]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆