Я намагаюся зрозуміти хеш-таблиці - може хтось мені це пояснить - чітко?


25

Я хочу зрозуміти правильне використання та реалізацію хеш-таблиць у php (вибачте).

Десь я читав, що досвідчений програміст створив хеш-таблицю, а потім переглянув її. Тепер я розумію, чому це неправильно, але я не зовсім знаю, чи моє розуміння правильне (якщо ви знаєте, що я маю на увазі).

Тож чи може хтось пояснити мені, як реалізувати хеш-таблицю в php (імовірно, асоціативний масив), і, можливо, ще важливіше, як отримати доступ до значень "за допомогою хеша" і що це насправді означає?

Відповіді:


37

Огляд простого хеш-таблиці

Як оновлення, хеш-таблиця - це спосіб збереження значення під певним ключем у структурі даних. Наприклад, я можу зберігати значення "a"під ключем 1, а потім пізніше отримати його, шукаючи ключ 1у хеш-таблиці.

Найпростіший приклад хеш-таблиці, яку я можу придумати вгорі голови, - хеш-таблиця, яка може зберігати лише цілі числа, де ключем для запису хеш-таблиці є також значення, яке зберігається. Скажімо, ваша таблиця розміру 8, і це в основному масив пам'яті:

---------------------------------
|   |   |   |   |   |   |   |   |
---------------------------------
  0   1   2   3   4   5   6   7  

Функція хешу

Функції хешу дають вам індекс, де зберігати свою вартість. Досить простий хеш - функції для цієї таблиці можна було б додати 1 до значення ви хочете зберегти, а потім мод його на 8 (розмір таблиці). Іншими словами, ваш хеш - функція (n+1)%8, де nє ціле число ви хочете зберегти.

Вкладиші

Якщо ви хочете вставити значення в цю хеш-таблицю, ви викликаєте свою хеш-функцію (у цьому випадку (n+1)%8) на значення, яке ви хочете вставити, щоб дати вам індекс. Наприклад, якщо ми хочемо вставити 14, ми зателефонуємо (14 + 1) % 8та отримаємо індекс 7, тому ми би вставили йому значення в індекс 7.

---------------------------------
|   |   |   |   |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Аналогічно ми можемо вставити 33, 82 та 191 так:

---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Зіткнення

Але що трапиться, якщо ми спробуємо вставити щось, що зіткнеться із записом? 2 має йти в індексі 3, але він бере 82. Існує кілька способів вирішити цю проблему, найпростіший - викликати нашу хеш-функцію знову і знову повторно, поки не знайдемо порожній пробіл.

Тож логіка така:

  1. (2 + 1)% 8 = 3
  2. Індекс 3 заповнений
  3. Підключіть 3 до нашої хеш-функції. ( 3 + 1)% 8 = 4 , який порожній.
  4. Помістіть наше значення в індекс 4 .

Тепер хеш-таблиця виглядає приблизно так, що значення 2 зберігається в індексі 4.

---------------------------------
|191|   |33 |82 |2  |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Мінус цього рішення полягає в тому, що досить скоро наш стіл буде заповнений! Якщо ви знаєте, що розмір ваших даних обмежений, це не повинно бути проблемою, якщо ваша таблиця досить велика, щоб вмістити всі можливі значення. Якщо ви хочете мати більше зусиль, ви можете вирішити зіткнення по-різному. Повернемось туди, де ми були, перш ніж вставляти 2.

---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Якщо ви пам’ятаєте, (2+1)%8дає нам індекс 3, який беремо. Якщо ви не хочете, щоб ваша хеш-таблиця заповнювалася, ви можете використовувати індекс таблиці як пов'язаний список і додавати до списку в цьому індексі. Тож замість того, щоб викликати хеш-функцію ще раз, ми просто додамо до списку в індексі 3:

            -----
            | 2 |
---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Цей список може зростати стільки, скільки дозволить пам'ять. Я можу вставити 18, і він буде просто доданий до 2:

            -----
            |18 |
            -----
            | 2 |
---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Пошук

Пошук значень у вашій хеш-таблиці швидкий, враховуючи, що ваша хеш-таблиця має досить великий розмір. Ви просто викликаєте свою хеш-функцію та отримуєте індекс. Скажімо, ви хочете подивитися, чи є 82 у вашій таблиці. Функція пошуку закликала б (82+1)%8= 3, і перегляне елемент у індексі 3та поверне його вам. Якщо ви подивилися на 16, функція пошуку виглядала б в індексі 1та бачила, що її не існує.

Шукати потрібно і для зіткнення!

Якщо ви спробуєте знайти значення 2, ваша хеш-таблиця повинна використовувати ту саму логіку зіткнення, яку вона використовувала для зберігання даних, як і для отримання даних. Залежно від того, як працює ваша хеш-таблиця, ви або будете хешувати ключ знову і знову, поки не знайдете потрібну запис (або знайдете порожнє місце), або ви будете повторювати ваш зв'язаний список, поки не знайдете елемент (або дійшов до кінця списку)

Підсумок

Отже, хеш-таблиці - хороший спосіб швидкого зберігання та доступу до пар «ключ-значення». У цьому прикладі ми використовували той самий ключ, що і значення, але в хеш-таблицях реального світу ключі не такі обмежені. Функції хешу працюватимуть на клавішах для генерування індексу, і тоді ключ / значення може бути збережено в цьому індексі. Таблиці хешу насправді не призначені для повторення, хоча це можна зробити. Як бачите, у хеш-таблицях може бути багато порожніх пробілів, і повторення через них буде марною тратою часу. Навіть якщо у хеш-таблиці є логіка пропускання порожніх просторів у своєму ітераторі, вам краще підійде структура даних, призначена для ітераторів, як пов'язані списки.


2
ASCII мистецтво FTW!
Анто-

2
Чудова відповідь. Можливо, варто згадати, що метод, де кожен індекс є пов'язаним списком, називається ланцюжком.
alexn

+1 Відмінна відповідь, що вискакував майже з усіх сумнівів у мене з голови. Потрібно задати ще одне питання. Чи використовує кожна реалізація хешування для зберігання цілих чисел? або це використовується для конкретних випадків? якщо так, то які це випадки?
0decimal0

@PHIfounder Я не впевнений, чи повністю я зрозумів ваше запитання, але хеш-функція, яка виконується на ключі, розроблена так, щоб бути загальною, а не стосуватися конкретного типу даних, таких як цілі числа. Якщо ми говоримо про код C, хеш-таблицю можна було б розробити так, щоб вона прийняла (недійсна *) для ключа і значення і зробила хеш-розрахунок на значення вказівника ключа.
Джефф

@Jeff насправді я можу бути дурним, щоб це запитати, але я говорю про внутрішню структуру комп’ютера; чи кожен комп'ютер використовує структуру даних, як хеш-таблицю для зберігання, посилаються на цілі чи не внутрішньо?
0decimal0

7

Уявіть собі бібліотеку з тисячами книг. Потрібно організувати книги так, щоб ви могли знайти їх за назвою якомога швидше.

Один (поширений) спосіб зробити це - сортувати книги за алфавітом. Якщо ваш заголовок починається з слова "G", ви знаходите область "G", тоді шукайте другу букву, скажімо "ö", потім "d", "e", "l", звужуючи ваш пошук і так далі , поки не знайдете книгу. Це, однак, може зайняти багато часу, і, крім того, коли приходять нові книги, вам іноді потрібно реорганізувати макет, щоб звільнити місця для нових приїздів.

Це двійковий пошук. Це добре.

Однак є більш швидкий спосіб зробити це. Скажімо, ви перераховуєте всі книжкові шафи та полиці, а потім для кожної книги ви обчислюєте спеціальну, сподіваємось, унікальну цифру, яка відображається на книжковій полиці / полиці, де книгу слід знайти. Спосіб обчислення "ключа" не має великого значення, якщо він дає випадкове число. Наприклад, ви можете додати коди символів усіх букв у заголовку, а потім розділити їх на якесь просте число (можливо, це не найкращий метод, але все одно працює).

Це хеш. Це набагато швидше, адже вам не потрібно переглядати цілі книжкові шафи та полиці, шукаючи наступну букву в заголовку. Хешинг - це звичайна операція, якщо ви не маєте «зіткнення», коли дві і більше книг вирішують один і той же ключ. Але це добре, ви знаєте, що вони лежать поруч, і, залежно від якості хеш-функції, під одним ключем не повинно бути занадто багато.

Таблиці хешу мають деякі обмеження та примхи (повторна зміна / зміна розміру), що підтримує бінарний пошук навколо як життєздатного конкурента. Не все чорно-біле, що стосується того, який спосіб краще. Але це вже інша історія.

PS Вибачте, що не відповіли на ваше запитання безпосередньо (запишіть хеш-таблицю в PHP), але це деталі, і це називається "програмування";)


2
Мені подобаються некомп'ютерні пояснення проблем, пов'язаних з комп'ютером. +1
габлін

1

Наскільки я знаю, хеш-таблиця в PHP просто реалізується через:

$my_hash = array(
    1 => "Bob",
    2 => "Alice",
    3 => "Jack"
);

Потім ви отримуєте доступ до даних за допомогою дзвінків, таких як:

echo $my_hash[2]; // Will echo "Alice"

Ви використовуєте функцію foreach () для ітерації вмісту масиву.

Найкращий спосіб зрозуміти хеш-таблиці - це прочитати щось на кшталт http://en.wikipedia.org/wiki/Hash_table , але приблизно це зводиться до цього: ліва частина кожного рядка всередині цього виклику масиву () - це ключі . Ці ключі будуть виставлені через хеш-розрахунок, і результат - хеш. Ви, ймовірно, бачили хеші MD5 або SHA раніше, це схоже на це. Конкретна частина цього хеша, як правило, перші символи X, але іноді і повний хеш, буде використовуватися для ідентифікації так званих "відро", які є місцями зберігання значень (права сторона).

Тоді, коли ви отримуєте доступ до свого хештелю, ви користуєтесь клавішею, щоб отримати значення. Ключ знову обчислюється до хеша, і хеш використовується для швидкого пошуку пов’язаного значення. Тож хеш-таблиці дозволяють швидше шукати, ніж просто шукати лінійний, якщо все було просто збережено. Єдиним недоліком є ​​те, що деякі хеш-реалізації страждають від зіткнень, що є однаковим обчисленим хесом для двох різних ключів. Взагалі, це не те, про що потрібно багато хвилюватися.

Я сподіваюся, що це дає деяку інформацію, але будь ласка, спробуйте прочитати більше про цю тему, якщо вас це цікавить. Моє пояснення є дуже рудиментарним, і я впевнений, що в ньому є достатньо дірок, але це повинно вистачити для швидкого пояснення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.