python #loops #time
#python #циклы #время
Вопрос:
Для класса, в котором я нахожусь, нас просят написать метод грубой силы для поиска 2 элементов в списках S1, S2, которые добавляют к указанному значению x. До сих пор у меня это было записано как таковое:
@timing
def brute_force_nested(x, s1 : list, s2 : list) -> bool:
for a in s2:
for b in s1:
if a b == x:
return True
return False
@timing
def brute_force_inline(x, s1 : list, s2 : list) -> bool:
return bool([ (a,b) for a in s2 for b in s1 if a b == x ])
Но когда я запускаю их в терминале, я получаю очень большую разницу во времени:
>>> brute_force_nested(5123, S1, S2):
func:brute_force_nested took: 0.007085800170898438 sec and gave: True
>>>func:brute_force_inline(5123, S1, S2)
func:brute_force took: 3.0208868980407715 sec and gave: True
Почему это происходит? У меня сложилось впечатление, что встроенный синтаксис был по сути просто синтаксическим сахаром для выписанных вложенных циклов, но что-то явно отличается, и я не знаю, что.
Комментарии:
1. ваш вложенный цикл for останавливается, как только он находит первое совпадение благодаря возврату; понимание списка создает список со ВСЕМИ совпадениями, а затем предоставляет ему функцию bool
Ответ №1:
Циклы действительно равны, но не условие для его разрыва. В первом вложенном цикле код останавливается при достижении первого равенства. Во втором все тесты вычисляются до тех пор, пока не будут исчерпаны все комбинации.
Эквивалентом первого цикла с синтаксисом понимания было бы использование генератора и any
, который остановится при достижении первого истинного значения
return any((a b==x for a in s2 for b in s1))
Комментарии:
1. @Jab спасибо! Набрав на моем телефоне, нелегко получить хороший обзор;)
Ответ №2:
Это потому, что вы только перебираете списки в первом fuinction и возвращаетесь к первому значению. Во-вторых, вы создаете список, а затем оцениваете истинное значение этого списка. Чтобы они были сопоставимы, вам необходимо использовать any
и понимание генератора.
def brute_force_inline(x, s1 : list, s2 : list) -> bool:
return any(a b == x for a in s2 for b in s1)
P.S Технически оба ваших подхода представляют собой вложенные циклы, один из которых просто выполняется с использованием понимания.
Это также можно сделать более эффективно, используя itertools.product
:
from itertools import product
def brute_force_inline(x, s1 : list, s2 : list) -> bool:
return any(sum(ab) == x for ab in product(s2, s1))
Комментарии:
1. Как вы представляете
product
, что один из них будет быстрее?2. Потому что вложенный цикл for выполняется на C по сравнению с собственным python. В этом случае разница в эффективности незначительна.
3.
for a, b in
работает в Python столько же. Но всегда приходится присваивать два значения вместо одного, что делает его менее эффективным. Можете ли вы показать ввод, где он более эффективен?4. Рассмотрим мое редактирование.