Выполнение различных методов для тестирования на палиндром [Python] - PullRequest
3 голосов
/ 07 января 2012

Сегодня я дурачился с парой загадок программирования.Столкнувшись с задачей тестирования строки, чтобы определить, является ли она палиндромом, я придумал несколько способов сделать это.Основы этих трех методов показаны ниже (большая часть кода для тестирования и тестирования не приводится).

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 букву к центру слова и повторяете.

Основная часть вычислений с помощью этого метода будет состоять из проверки условий, разрезания индекса и сравнения.Существует также переменная счетчика, которая является постоянной частью вычисления для нарезки индекса.

Метод 2 также использует индексирование символов в строке.Первый и последний символы сравниваются, затем отбрасываются, и эти шаги повторяются до тех пор, пока палиндром не будет гарантирован (или опровергнут).

Затраты будут аналогичны методу 1, с некоторыми отличиями: добавление преобразования из str-> список, извлечение элементов из списка и минус переменная счетчика.

Метод 3 переворачивает заданную строку, а затем сравнивает ее с оригиналом. Существуют различные способы обратить строку (list.__reversed__() и т. Д.), Но я показал только одну такую ​​возможность: преобразовать строку в список, а затем объединить каждый элемент этого списка в НАЧАЛОновая строка.

При использовании различных методов обращения строки могут быть разные операции и, соответственно, затраты.Для выбранного мной метода у нас есть стоимость вырезания каждого элемента из списка и конкатенации его с переменной str.

Мои вопросы:

Какой из этих методов будет самым быстрыми почему?Кроме того, есть ли способ повысить эффективность этих методов?(По касательной, как вы тестируете скорость выполнения модулей в Python?)

Ответы [ 4 ]

2 голосов
/ 07 января 2012

Используйте модуль timeit для тестирования скорости, модуль profile для статистики производительности и модуль dis для разборки байт-кода.

Сценарий ниже демонстрирует, как можно использовать модули.

Из выводов следует обратить внимание на то, насколько количество вызовов функций может повлиять на общую производительность (и, конечно, то же самое касается количества инструкций байт-кода).

Надеюсь, это (и немного больше экспериментов) должно дать вам достаточно подсказок о том, как повысить эффективность ваших функций.

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        
2 голосов
/ 07 января 2012

Вы не можете выполнить проверку палиндрома быстрее, чем O (n) (n - длина входной строки).Любые дополнительные усилия (стеки, перестановка строки и т. Д.) Не принесут вам никакого улучшения производительности, а только потребуют память.

1 голос
/ 07 января 2012

Модуль timeit может быть использован. 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 голосов
/ 07 января 2012

Для синхронизации небольших фрагментов кода, подобных этим функциям, timeit может быть очень полезным. Тогда вы сами сможете найти, какой из них быстрее:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...