Недавно я ответил на вопрос на сайте-партнере, который попросил функцию, которая считает все четные числа числа. Один из других ответов содержал две функции (которые пока были самыми быстрыми):
def count_even_digits_spyr03_for(n):
count = 0
for c in str(n):
if c in "02468":
count += 1
return count
def count_even_digits_spyr03_sum(n):
return sum(c in "02468" for c in str(n))
Кроме того, я рассмотрел использование списков и list.count
:
def count_even_digits_spyr03_list(n):
return [c in "02468" for c in str(n)].count(True)
Первые две функции практически одинаковы, за исключением того, что первая использует явный цикл подсчета, а вторая использует встроенную sum
. Я бы ожидал, что второй будет быстрее (на основе, например, этого ответа), и это то, во что я бы порекомендовал превратить первое, если бы его попросили пересмотреть. Но, оказывается, все наоборот. Тестируя это с некоторыми случайными числами с увеличением количества цифр (таким образом, вероятность того, что любая отдельная цифра будет даже приблизительно 50%), я получаю следующие моменты времени:
Почему руководство for
циклу намного быстрее? Это почти в два раза быстрее, чем использование sum
. А поскольку встроенная sum
должна быть примерно в пять раз быстрее, чем ручное суммирование списка (согласно связанному ответу), это означает, что на самом деле она в десять раз быстрее! Является ли экономия от необходимости только добавлять один к счетчику для половины значений, потому что другая половина отбрасывается, достаточно, чтобы объяснить эту разницу?
Используя if
в качестве фильтра, вот так:
def count_even_digits_spyr03_sum2(n):
return sum(1 for c in str(n) if c in "02468")
Улучшает синхронизацию только до того же уровня, что и понимание списка.
При расширении таймингов до больших чисел и нормализации к for
синхронизации циклы, они асимптотически сходятся при очень больших количествах (> 10k цифры), вероятно, из - за времени str(n)
принимает: