Performance of Various Methods to Test for a Palindrome [Python]

前端 未结 4 1878
梦谈多话
梦谈多话 2021-01-21 09:29

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 t

相关标签:
4条回答
  • 2021-01-21 09:56

    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)
    

    Output:

    ##################################################
    
    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        
    
    0 讨论(0)
  • 2021-01-21 10:02

    For timing small pieces of code like these functions, timeit can be very useful. Then you can find which one is faster yourself :)

    0 讨论(0)
  • 2021-01-21 10:19

    Module timeit can be used. eg.:

    import timeit
    
    for method in 1, 2, 3:
       print method, timeit.timeit('check_palin("victimmitciv", %i)' % method,
                           'from __main__ import check_palin', number=1000000)
    
    0 讨论(0)
  • 2021-01-21 10:22

    You can't do a palindrome check faster than O(n) (n being the length of the input string). Any extra effort (stacks, reversing the string etc.) won't give you any perforce improvement, it only costs memory.

    0 讨论(0)
提交回复
热议问题