Vysvetlite tieto hašovacie tabuľky v PHP
Explain Those Hash Tables Php
http://www.php.cn/php-weizijiaocheng-363401.html
Jednou z najdôležitejších dátových štruktúr v jadre PHP je HashTable. Pole, ktoré používame, je v jadre implementované pomocou HashTable. Ako sa teda implementuje HashTable v PHP? Nedávno som sa pozrel na dátovú štruktúru HashTable, ale v knihe algoritmov nie je žiadny konkrétny implementačný algoritmus. Práve som si nedávno prečítal zdrojový kód PHP, takže som implementoval implementáciu HashTable do jazyka PHP. Zjednodušená verzia HashTable zhŕňa niektoré z nápadov, poďme ich zdieľať pre všetkých.
Mám na github jednoduchú verziu implementácie HashTable: implementácia HashTable
Okrem toho mám podrobnejšiu anotáciu zdrojového kódu PHP na github. Záujemcovia sa môžu rozhliadnuť okolo seba, dať hviezdičku. Poznámky k zdroju PHP5.4. Pridané anotácie môžete zobraziť prostredníctvom záznamu o potvrdení.
Úvod do tabuľky HashTable
Hašovacia tabuľka je efektívna dátová štruktúra na implementáciu slovníkových operácií.
definícia
Jednoducho povedané, HashTable je dátová štruktúra párov kľúč - hodnota. Podpora vkladania, hľadania, mazania a ďalších operácií. Za určitých rozumných predpokladov je časová zložitosť všetkých operácií v hashovacej tabuľke O (1) (môžete sa na ňu odvolať sami, ak vás zaujíma dôkaz).
Kľúč na implementáciu hašovacej tabuľky
V hashovacej tabuľke sa namiesto použitia kľúčových slov na indexovanie používa funkcia hash na výpočet hašovacej hodnoty kľúča ako dolného indexu a potom sa po vyhľadaní / odstránení vypočíta hašovacia hodnota kľúča, čím sa element rýchlo nájde . s pozícia.
V hashovacej tabuľke môžu rôzne kľúčové slová vypočítať rovnakú hashovaciu hodnotu. Toto sa nazýva „hash konflikt“, čo je prípad, keď sú hash hodnoty dvoch alebo viacerých kľúčov rovnaké. Existuje mnoho spôsobov, ako vyriešiť konflikt hash, otvorené adresovanie, zips atď.
Kľúčom k implementácii dobrej hašovacej tabuľky je preto dobrá hašovacia funkcia a spôsob riešenia hašovacích kolízií.
Funkcia hash
Existujú štyri definície pre posúdenie, či je hashový algoritmus dobrý alebo zlý:> * Konzistencia, ekvivalentné kľúče musia produkovať rovnaké hash hodnoty> * Efektívne, ľahko vypočítateľné> * Uniformita, rovnomerne pre všetky kľúče Hash.
Funkcia hash ustanovuje korešpondenciu medzi kľúčovými hodnotami a hashovými hodnotami, konkrétne: h = hash_func (kľúč). Zodpovedajúci vzťah je znázornený na obrázku nižšie:
Ak chcete navrhnúť dokonalú hashovaciu funkciu, nechajte to urobiť odborníkov. Použili sme iba existujúcu zrelú hashovaciu funkciu. Hašovacia funkcia používaná jadrom PHP je funkcia time33, známa tiež ako DJBX33A, ktorá je implementovaná nasledovne:
1 dva 3 4 5 6 7 8 9 10 jedenásť 12 13 14 pätnásť 16 17 18 19 dvadsať dvadsaťjeden 22 2. 3 24 25 26 27 28 29 |
|
Poznámka: Na dosiahnutie tejto funkcie používa prepínač 8 slučiek +, ide o optimalizáciu slučky for, zníženie počtu spustení slučky a vykonanie zvyšných prvkov, ktoré v prepínači neprechádzajú.
Metóda na zips
Metóda ukladania všetkých prvkov s rovnakou hodnotou hash do prepojeného zoznamu sa nazýva metóda zipsu. Pri vyhľadávaní sa najskôr vypočíta hash hodnota zodpovedajúca kľúču, potom sa nájde zodpovedajúci prepojený zoznam podľa hash hodnoty a nakoniec sa vyhľadá zodpovedajúca hodnota v poradí prepojeného zoznamu. Štruktúrny diagram po špecifickom uchovaní je nasledovný:
Štruktúra HashTable PHP
Po krátkom úvode do dátovej štruktúry hašovacej tabuľky si môžete pozrieť, ako je hašovacia tabuľka implementovaná v PHP.
(Obrázok zo siete, porušenie je odstránené)
Definícia hashtable jadra PHP:
1 dva 3 4 5 6 7 8 9 10 jedenásť 12 13 14 pätnásť 16 17 |
|
nTableSize, veľkosť tabuľky HashTable, ktorá rastie na násobky 2
nTableMask, ktorá sa používa na vykonanie operácie AND s hashovou hodnotou na získanie indexovej hodnoty hash hodnoty, arBuckets je po inicializácii vždy nTableSize-1.
nNumOfElements, počet prvkov, ktoré momentálne vlastní HashTable, funkcia count vráti túto hodnotu priamo.
nNextFreeElement, ktorý predstavuje pozíciu nasledujúceho číselného indexu v poli hodnôt číselných kľúčov
pInternalPointer, interný ukazovateľ na aktuálneho člena, používaný na prechod elementom
pListHead, ktorý ukazuje na prvý prvok tabuľky HashTable a je prvým prvkom poľa
pListTail, ktorý ukazuje na posledný prvok tabuľky HashTable a je posledným prvkom poľa. V kombinácii s vyššie uvedenými ukazovateľmi je to veľmi výhodné pri prechádzaní poľami, ako sú reset a endAPI
arBuckets, pole zoznamov dvakrát prepojených pozostávajúcich z segmentov. Index je generovaný hashom kľúča a nTableMask.
pDestructor, deštruktor používaný na mazanie prvkov v hashovacej tabuľke
Trvalo, identifikujte funkciu alokácie pamäte, ak je TRUE, použite funkciu alokácie pamäte samotného operačného systému, inak použite funkciu alokácie pamäte PHP
nApplyCount, uloží počet rekurzívnych prístupov k aktuálnemu vedru, čím zabráni viacnásobnej rekurzii
bApplyProtection, na identifikáciu toho, či má hash tabuľka používať rekurzívnu ochranu, je predvolená hodnota 1, aby sa použila
Uveďte príklad hodnoty hash kombinovanej s maskou:
Napríklad skutočný hash výrazu „foo“ (pomocou hashovacej funkcie DJBX33A) je 193491849. Ak máme teraz hash tabuľku so 64 viečkami, zjavne ju nemôžeme použiť ako dolný index poľa. Namiesto toho sa použije maska hašovacej tabuľky a potom sa odoberú iba nízke bity hašovacej tabuľky.
1 dva 3 4 |
|
Preto je v hashovacej tabuľke foo uložený vo vektore segmentu s dolným indexom 9 v arBuckets.
Definícia štruktúry vedra
1 dva 3 4 5 6 7 8 9 10 jedenásť |
|
h, hodnota hash (alebo kláves s hodnotou číselného klávesu)
nKeyLength, dĺžka kľúča
pData, ukazovateľ na dáta
pDataPtr, údaje ukazovateľa
pListNext, smerujúci na nasledujúci prvok v zozname arBuckets v HashTable
pListLast, ukazujúci na predchádzajúci prvok v zozname arBuckets v HashTable
pNext, smerujúci na ďalší prvok v zozname segmentov s rovnakou hodnotou hash
pLast, ukazuje na predchádzajúci prvok v zozname segmentov s rovnakou hodnotou hash
arKey, názov kľúča
HashTable v PHP prijíma implementáciu vektora plus dvojnásobne prepojeného zoznamu. Vektor je uložený v premennej arBuckets. Vektor obsahuje ukazovatele na viac segmentov. Každý ukazovateľ smeruje na dvojnásobne prepojený zoznam zložený z viacerých segmentov. Nový prvok sa pridá pred použitím. , to znamená, že nový prvok je vždy v prvej polohe segmentu. Ako vidíte z vyššie uvedeného, implementácia hash tabuľky PHP je dosť komplikovaná. Toto je cena, ktorú musí platiť za použitie ultra flexibilných typov polí.
Príklad tabuľky HashTable v PHP je uvedený nižšie:
Rozhranie API súvisiace s HashTable
zend_hash_init
zend_hash_add_or_update
zend_hash_find
zend_hash_del_key_or_index
zend_hash_init
Krok vykonania funkcie
Nastavte veľkosť tabuľky hash
Nastavte začiatočnú hodnotu ďalších členských premenných štruktúry (vrátane deštruktora pDescructor na uvoľnenie pamäte)
Komentáre k podrobnému kódu kliknite: zdroj zend_hash_init
Poznámka:
1, funkcia pHashFunction sa tu nepoužíva, funkcia php hash používa internú zend_inline_hash_func
2, po vykonaní zend_hash_init skutočne nepridelí pamäť pre arBuckets a nevypočíta veľkosť nTableMask. Skutočné pridelenie pamäte a výpočet nTableMask sa vykoná, keď sa pri vkladaní prvkov vykoná inicializácia kontroly CHECK_INIT.
zend_hash_add_or_update
Krok vykonania funkcie
Skontrolujte dĺžku kľúča
Skontrolujte inicializáciu
Vypočítajte hash a dolný index
Prejdením segmentu, kde sa nachádza hodnota hash, ak sa nájde rovnaký kľúč a je potrebné aktualizovať hodnotu, aktualizujú sa údaje, inak bude pokračovať v ukazovaní na nasledujúci prvok segmentu, kým nebude ukazovať na poslednú pozíciu parametra vedro.
Priraďte segment k novo pridanému prvku, nastavte hodnotu vlastnosti nového segmentu a potom ho pridajte do tabuľky hash.
Ak je hašovací tabuľkový priestor plný, zmeňte veľkosť hashovacej tabuľky
Vývojový diagram vykonávania funkcií
CONNECT_TO_BUCKET_DLLIST je pridať nové prvky do zoznamu segmentov s rovnakou hodnotou hash.
CONNECT_TO_GLOBAL_DLLIST je dvojnásobne prepojený zoznam, ktorý pridáva nové prvky do tabuľky HashTable.
Podrobný kód a komentáre získate kliknutím na anotáciu kódu zend_hash_add_or_update.
zend_hash_find
Krok vykonania funkcie
Vypočítajte hash a dolný index
Traverzom po segmente, kde je umiestnená hash hodnota, ak nájde segment, kde je umiestnený kľúč, vráti hodnotu. V opačnom prípade ukazuje na ďalší segment, kým neukazuje na poslednú pozíciu v zozname segmentov.
Podrobný kód a komentáre získate kliknutím na anotáciu kódu zend_hash_find.
zend_hash_del_key_or_index
Krok vykonania funkcie
Vypočítajte hash a dolný index kľúča
Posuňte vedro tam, kde je umiestnená hodnota hash. Ak sa nájde vedro, kde sa nachádza kľúč, pokračujte tretím krokom. V opačnom prípade ukážte na nasledujúci segment, kým nebude ukazovať na poslednú pozíciu v zozname segmentov.
Ak chcete vymazať prvý prvok, priamo namierte arBucket [nIndex] na druhý prvok, zvyšok operácie je vykonať aktuálny nasledujúci z posledného nasledujúceho aktuálneho ukazovateľa
Upravte súvisiace ukazovatele
Voľná dátová pamäť a pamäť štruktúry vedra
Podrobný kód a komentáre získate kliknutím na anotáciu kódu zend_hash_del_key_or_index.
Analýza výkonu
Výhody hashovacej tabuľky PHP: HashTable PHP poskytuje veľké pohodlie pre operácie s poľami. Či už je to vytváranie poľa alebo pridávanie prvkov alebo mazanie prvkov, hašovacia tabuľka poskytuje dobrý výkon, ale jej nedostatočnosť, keď je množstvo údajov veľké, je zrejmé z časovej a priestorovej zložitosti.
Nestačí takto:
Štruktúra zval, ktorá uchováva údaje, musí alokovať pamäť zvlášť. Túto nadbytočnú pamäť musíte spravovať. Každý zval zaberá 16 bajtov pamäte.
Pri pridávaní segmentu je segment tiež zvláštnym pridelením a vyžaduje tiež 16 bajtov pamäte.
Aby bolo možné vykonať postupný prechod, používa sa na prepojenie celej tabuľky HashTable obojsmerne prepojený zoznam a používa sa veľa ukazovateľov a každý ukazovateľ má tiež 16 bajtov pamäte
Ak sa pri prechode prvok nachádza na konci zoznamu segmentov, musíte tiež prejsť celým zoznamom segmentov, aby ste našli hľadanú hodnotu.
Nedostatky HashTable v PHP spočívajú hlavne v tom, že ukazovatele obojsmerne prepojeného zoznamu a zval a bucket musia alokovať ďalšiu pamäť, čo vedie k veľkému pamäťovému priestoru a veľkej časovej náročnosti pri vyhľadávaní.
Nasleduj
Vyššie uvedené nedostatky sú v PHP7 dobre vyriešené. PHP7 vykonal veľkú transformáciu dátovej štruktúry v jadre, vďaka čomu je PHP oveľa efektívnejšie. Preto sa odporúča, aby vývojári PHP vyvíjali a nasadzovali aktualizácie verzií. Poďme. Prezrite si nasledujúci kód PHP:
1 dva 3 4 5 6 7 8 9 10 jedenásť 12 13 14 pätnásť 16 17 18 |
|
Vyššie uvedené ukážka predstavuje porovnanie spotreby času, keď existuje viac konfliktov hash a žiadne konflikty. Tento kód spustím pod PHP5.4, výsledok je nasledovný
Vloženie 65 536 škodlivých prvkov vyžaduje 43,72204709053 sekúnd
Vloženie 65 536 normálnych prvkov vyžaduje 0,009843111038208 sekúnd
A výsledok fungovania na PHP7:
Vloženie 65 536 škodlivých prvkov vyžaduje 4,4028408527374 sekúnd
Vloženie 65 536 bežných prvkov vyžaduje 0,0018510818481445 sekúnd
Je vidieť, že výkon PHP7 sa výrazne zlepšil v konfliktných aj bezkonfliktných operáciách s poľami. Samozrejme, zjavnejšie sú protichodné zlepšenia výkonu. Pokiaľ ide o to, prečo sa výkon PHP7 tak zlepšil, stojí za to sa mu naďalej venovať.
Nakoniec má autor na stránkach github jednoduchú verziu implementácie HashTable: implementácia HashTable
Okrem toho mám podrobnejšiu anotáciu zdrojového kódu PHP na github. Záujemcovia sa môžu rozhliadnuť okolo seba, dať hviezdičku. Poznámky k zdroju PHP5.4. Pridané anotácie môžete zobraziť prostredníctvom záznamu o potvrdení.