Рубрики
Без рубрики

Программное обеспечение Está Cada VEZ Mais EM TUDO, E A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A “Das Coiss” ém próximo Passo No Caminho Da NoStay 001: Как работают словари Python? SA Sociedade Tecnológica.

В этом посте мы рассмотрим, как в Python представлен словарь, и HASH-схема его использует для … Tagged с 100Дамсофкодом, Python, программированием, информатикой.

В этом посте мы рассмотрим, как в Python представлен словарь, и HASH-схема, которую он использует для достижения поиска постоянного времени.

{
    "name": "Rahul Jha",
    "alias": "RJ722",
    "music": "country",
    "cheat_diet": "pasta",
}

Когда мы сначала создаем словарь, он внутренне поддерживает список. Этот список используется для хранения «данных» об элементах в словаре, и содержит связи в следующем формате:

(
   hash(key),
   key,
   value,
)

Каждый кортеж, упомянутый как «строка», представляет собой одну пару ключ-значение в словаре. Когда в словаре добавляется новая пара, он добавляется в этот список. Это формирует «базу данных» словаря.

Для нашего примера выше база данных будет выглядеть так:

[(7409878836864405608, 'name', 'Rahul Jha'),
 (2620441572017060194, 'alias', 'RJ722'),
 (205288631654089410, 'music', 'country'),
 (405166953458871902, 'cheat_diet', 'pasta')]

Наряду с созданием базы данных, когда создается новый словарь, кусок памяти «заблокирован». Это используется для адресации ключей, чтобы сказать, какая строка в базе данных должна быть возвращена для соответствующего ключа. Больше на нем позже.

Размер этого чанка определяет количество Открытые адреса доступный. Если все открытые адреса используются, новый кусок памяти (обычно двойной размер) выделяется, и любые значения, хранящиеся в этих адресах, скопированы. Как правило, это сделано, когда адреса две трети (или 66%) полные.

Ради этого примера, давайте представляем «база данных» списком Предметы и заблокированная кусок памяти списком адреса :

items = []
n = 8  # number of open addresses available
addresses = [None] * n

Адресация

Чтобы найти ключ в словаре, не нужно искать по всей базе данных. Таким образом, невозможно поддерживать постоянное время поиска.

Пары обращаются вместо этого. Адрес/положение каждого значения в базе данных вычисляется на основе [HASH] ключа.

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

Если хэси на два или более ключа сталкиваются, методика под названием Открытый адрес Мультигирование помогает решить адрес. Перед тем, как понять многосигнать, давайте сначаем посмотрим на более простую технику:

Линейное зондирование

Если есть столкновение для адреса Я , значение, проверьте I + 1 адрес. Если еще одно значение хранится в I + 2 Проверьте на I + 3 И так далее, пока вы не найдете пустой слот:

def insert(key, value):
    h = hash(key)
    items.append((h, key, value))
    position = len(items) - 1 # the index of this row

    address = h % n
    while addresses[address] is not None:
        address = (address + 1) % n

    # Store the position at which the tuple can be found
    addresses[address] = position 

Давайте попробуем это:

insert("name", "Rahul Jha")
insert("alias", "RJ722")
insert("music", "country")
insert("cheat_diet", "pasta")

pprint(addresses)
#  [(7409878836864405608, 'name', 'Rahul Jha'),
#   (2620441572017060194, 'alias', 'RJ722'),
#   (205288631654089410, 'music', 'country'),
#   (405166953458871902, 'cheat_diet', 'pasta')]

pprint(items)
#  [0, None, 1, 2, None, None, 3, None]

Аналогично, при взгляде, нужно процитировать соседние адреса, пока не будет найденную ключею:

def lookup(key):
    h = hash(key)
    address = addresses[h % n]
    while items[address][1] != key:
        address = (address + 1) % n
    return items[address][2]

print(lookup("name") ) # "Rahul Jha"

Однако, если большое количество адресов сталкивается, линейное зондирование может привести к Катастрофический линейный счастье , вызывая длинные цепи поиска и замедления поиска.

Обратите внимание, что приведенная выше реализация является неполной: удаление ключей может создавать «отверстия», которые сломают цепочку, и прощупывание не сможет найти ключи, которые имели столкновение. Решением является использование Отметить слот как Манекен служить заполнителем. Этот метод известен как «Кнут – алгоритм D».

Открыть адрес мультигинга

Вместо того, чтобы процитировать соседние адреса, новый адрес генерируется повторной перемешивающейся, основываясь как на предыдущем хеш-значере, так и на количество зондов.

Мы добавляем псевдослучайное число на адресный зонд – генерируемый «простым генератором случайного номера»: I * I + 1 Отказ

Это, однако, снова сканирует адреса в фиксированном порядке. Чтобы избежать этого, исходное хеш также добавляется в случайное число. Затем это хеш смещено 5 битов на каждый рецидив. Таким образом, последовательность зависит (в конечном итоге) на каждый бит в хэш-коде и псевдо-скремблировании более агрессивна:

def insert(key, value):
    h = hash(key)
    items.append((h, key, value))
    position = len(items) - 1 # the index of this row

    perturb = h
    address = h % n
    while addresses[address] is not None:
        address = (5 * address + 1 + perturb) % n
        perturb >>= 5

    # Store the position at which the tuple can be found
    addresses[address] = position 

Перевозка вперед тот же пример, что и выше, мы можем генерировать адреса:

insert("name", "Rahul Jha")
insert("alias", "RJ722")
insert("music", "country")
insert("cheat_diet", "pasta")

pprint(items)
#  [(-6473745080427136004, 'name', 'Rahul Jha'),
#   (-1887375983119989115, 'alias', 'RJ722'),
#   (-4671665682772832904, 'music', 'country'),
#   (3463812311448012107, 'cheat_diet', 'pasta')]

pprint(addresses)
# [2, None, None, 3, 0, 1, None, None]

В ходе поиска мы разрешаем столкновения с использованием одного и того же рисунка рецидива:

def lookup(key):
    perturb = h = hash(key)
    address = addresses[h % n]
    while items[address][1] != key:
        address = (5 * address + 1 + perturb) % n
        perturb >>= 5
    return items[address][2]

print(lookup("name"))  # "Rahul Jha"

Python использует две таблицы: один для хранения хеш/ключа/значение плотно, и отдельный, редкая таблица индексов, которые векторы, которые векторы плотного стола.

Это не всегда так. Поскольку Рэймонд Хеттинтер объясняет блестяще в его разговоре «Современные словари» (который также предоставил корм для этого поста), словари в Python 2.7 не было двух таблиц. Таблица не было никакого стола для адресации, только данные – большой редкий стол. Это приводит к большому количеству пространства, впустую в пустых отверстиях, и, следовательно, словари, будучи гигантским до Python3.6 (в котором эта векторная схема была добавлена Hettinger).

Веселый вопрос: почему вы думаете, что ключевые хэши также хранятся вместе с данными?

Оригинал: “https://dev.to/rj722/day-001-how-do-python-dictionaries-work-5aa2”