Ответ 1
Пусть проверит его:
import collections
import math
import timeit
def power_bit_length(x):
return 2**(x-1).bit_length()
def shift_bit_length(x):
return 1<<(x-1).bit_length()
def power_log(x):
return 2**(math.ceil(math.log(x, 2)))
def test(f):
collections.deque((f(i) for i in range(1, 1000001)), maxlen=0)
def timetest(f):
print('{}: {}'.format(timeit.timeit(lambda: test(f), number=10),
f.__name__))
timetest(power_bit_length)
timetest(shift_bit_length)
timetest(power_log)
Причина, по которой я использую range(1, 1000001)
вместо всего range(1000000)
заключается в том, что версия power_log
завершится с ошибкой на 0
. Причина, по которой я использую небольшое количество повторений в большом диапазоне, а не много повторений в небольшом диапазоне, заключается в том, что я ожидаю, что разные версии будут иметь разную производительность в разных доменах. (Если вы ожидаете, что будете называть это огромными тысячами бит, конечно, вам нужен тест, который их использует.)
С Apple Python 2.7.2:
4.38817000389: power_bit_length
3.69475698471: shift_bit_length
7.91623902321: power_log
С Python.org Python 3.3.0:
6.566169916652143: power_bit_length
3.098236607853323: shift_bit_length
9.982460380066186: power_log
С pypy 1.9.0/2.7.2:
2.8580930233: power_bit_length
2.49524712563: shift_bit_length
3.4371240139: power_log
Я считаю, что это показывает, что 2**
- это медленная часть здесь; использование bit_length
вместо log
действительно ускоряет работу, но более важно использовать 1<<
вместо 2**
.
Кроме того, я думаю, это яснее. Версия OP требует, чтобы вы сделали ментальный контекст-переключатель из логарифмов в биты, а затем обратно к показателям. Оставайтесь в битах все время (shift_bit_length
) или оставайтесь в журналах и экспонентах (power_log
).