Я был с помощью словаря в качестве таблицы поиска, но я начал задаваться вопросом, если список будет лучше для моего приложения -- количество записей в моей таблице подстановки вовсе'т такой большой. Я знаю, списки, массивы C # под капотом, который заставил меня сделать вывод, что поиск в списке с помощью всего нескольких элементов будет лучше, чем в словаре (доступ несколько элементов в массиве быстрее, чем вычисления хэша).
Я решила профиля альтернатив, но результаты меня удивили. Список поиска был только лучше с одного элемента! См. Следующий рисунок (логарифмический график):
Так вот появляется вопрос: почему поиск в списке проанализировать так плохо? Что я упускаю?
На стороне вопрос, еще что-то, что обратил мое внимание немного на "разрыв" в дикт поиск времени после примерно 1000 записей. Я проложил дикт просмотра один раз, чтобы показать его.
С. С. 1 я знаю, о о(n) против O(1) амортизированной времени для массивов и хэш-таблицы, но это, как правило, на небольшое количество элементов перебора массива лучше использовать хэш-таблицу.
С. С. 2 Вот код, который я использовал, чтобы сравнить dict и список поиска раз:
import timeit
lengths = [2 ** i for i in xrange(15)]
list_time = []
dict_time = []
for l in lengths:
list_time.append(timeit.timeit('%i in d' % (l/2), 'd=range(%i)' % l))
dict_time.append(timeit.timeit('%i in d' % (l/2),
'd=dict.fromkeys(range(%i))' % l))
print l, list_time[-1], dict_time[-1]
С. С. 3 С Помощью Python 2.7.13
Я знаю, списки, массивы C # под капотом, который заставил меня сделать вывод, что поиск в списке с помощью всего нескольких элементов будет лучше, чем в словаре (доступ несколько элементов в массиве быстрее, чем вычисления хэша).
Связи c несколько элементов массива дешево, конечно, но вычисления ==
это удивительно тяжеловес в Python. Видим, что Спайк в вашем втором графике? Что'ы стоимость вычислительной ==
для двоих ИНЦ прямо там.
Ваш поиск список нужно вычислить ==
намного больше, чем дикт запросы делать.
Между тем, вычисление хэшей может быть довольно дорогостоящей операцией для многих объектов, но для всех целых чисел здесь ни при чем, они просто хэш для себя. (-1 будет хэш до -2, и больших целых чисел (технически `долго) будет хэш для небольших чисел, но это вовсе'т обратиться сюда.)
Поиск дикт разве'т действительно, что плохого в Python, особенно, когда ваши ключи просто ряд последовательных целых чисел. Все ИНЦ вот хэш для себя, и Python использует пользовательский открытой адресации вместо цепочки, так что все ваши ключи в конечном итоге почти непрерывен в памяти, как если бы вы'd применяется список (т. е. указатели на ключи в конечном итоге в смежных `PyDictEntry по). Процедура поиска очень быстро, и в ваших тестах, он всегда попадает правой клавишей на первый зонд.
Ладно, вернемся к спайку в графе 2. Спайк в lookup раза на 1024 записи, во второй графе-это потому, что для всех меньших размеров, чисел вы искали все <= 256, так что все они пали в диапазоне с CPython's малый число кэш. Эталонная реализация Python сохраняет каноническое число объектов для всех целых чисел от -5 до 256 включительно. Для этих чисел, Python был в состоянии использовать быстрое сравнение указателей, чтобы избежать прохождения через процесс (удивительно тяжеловес) вычислительной техники ==
. Для больших чисел, аргумент " в " был не один и тот же объект в соответствие целое число в dict, и питону пришлось пройти через весь процесс==
.
Короткий ответ заключается в том, что списки использование линейного поиска и предсказывает использования амортизируется за O(1) Поиск.
Кроме того, обыски дикт можете пропустить проверку на равенство, когда 1) хэш-значений Дон'т соответствовать или 2) когда есть совпадение. Списки только выиграет от личности-предполагает оптимизацию равенства.
Еще в 2008 году я сделал доклад на эту тему, где вы'll найти все подробности:
Грубо говоря логика для списков поиск:
for element in s:
if element is target:
# fast check for identity implies equality
return True
if element == target:
# slower check for actual equality
return True
return False
Словарь по логике примерно:
h = hash(target)
for i in probe_sequence(h, len(table)):
element = key_table[i]
if element is UNUSED:
raise KeyError(target)
if element is target:
# fast path for identity implies equality
return value_table[i]
if h != h_table[i]:
# unequal hashes implies unequal keys
continue
if element == target:
# slower check for actual equality
return value_table[i]
Словарь хэш-таблиц, как правило, от одной трети до двух третей полной мере, поэтому они, как правило, имеют несколько столкновений (несколько поездок вокруг петли показано выше), независимо от размера. Кроме того, проверить хэш-значение предотвращает излишне медленно проверяет равенство (шанс впустую проверить равенство составляет около 1 в 2**64).
Если ваше время фокусируется на целые, есть некоторые другие эффекты в игре, а также. Что хэш инт-сам инт, так что хеширование-это очень быстро. Кроме того, это означает, что если вы'ре хранение последовательные целые числа, есть, как правило, нет конфликтов вообще.
Вы говорите "в связи c несколько элементов в массиве быстрее, чем вычисления хеш и".
Простое правило хеширования для строк может быть просто сумма (С по модулю в конце). Это дистанционного обслуживания, который может сравнить благоприятно с характером сравнений, особенно когда есть длинный матч по префиксу.