Kaip veikia maišos lentelė?

Aš ieškau paaiškinimo, kaip veikia maišos lentelė - paprastas anglų kalba, kaip man!

Pavyzdžiui, aš žinau, kad tai užima raktą, apskaičiuoja hash (ieško paaiškinimo, kaip), ir tada atlieka tam tikrą modulį darbui ten, kur jis yra masyve, kur vertė yra saugoma, bet kur mano žinios sustoja.

Ar kas nors gali paaiškinti šį procesą?

Redaguoti: aš ne klausiu konkrečiai apie tai, kaip apskaičiuojamos maišos, bet ir bendrosios hash lentelės apžvalgos.

457
08 апр. „Arec Barrwin“ įkūrė balandžio 8 d 2009-04-08 18:48 '09 at 18:48 PM 2009-04-08 18:48
@ 15 atsakymų

Čia pateikiamas paaiškinimas, susijęs su laikinomis sąlygomis.

Tarkime, kad norite užpildyti biblioteką su knygomis, o ne tik juos užlenkti, bet jūs norite, kad juos vėl lengvai surastumėte, kai jums jų reikia.

Taigi, jūs nuspręsite, kad jei asmuo, kuris nori skaityti knygą, žino knygos pavadinimą ir tikslų pavadinimą, kurį reikia atsisiųsti, tai viskas, ko reikia. Su tokiu pavadinimu asmuo, su bibliotekininko pagalba, turėtų lengvai ir greitai rasti knygą.

Taigi, kaip jūs galite tai padaryti? Akivaizdu, kad jūs galite laikyti tam tikrą sąrašą, kur įdėjote kiekvieną knygą, bet tada turėsite tą pačią problemą, kaip ieškant bibliotekoje, reikia atlikti paiešką sąraše. Žinoma, sąrašas bus mažesnis ir bus lengviau ieškoti, tačiau vis tiek nenorite atlikti nuoseklios paieškos iš vieno bibliotekos (arba sąrašo) galo į kitą.

Jūs norite kažką, kad su knygos pavadinimu galite iš karto suteikti jums reikiamą vietą, todėl viskas, ką jums reikia padaryti, yra tik vaikščioti į dešinę lentyną ir pasiimti knygą.

Bet kaip tai padaryti? Na, su kai kuriais iš anksto numatytais, kai užpildote biblioteką, ir daug darbo, kai užpildote biblioteką.

Užuot pradėję užpildyti biblioteką iš vieno galo į kitą, sukuriate šiek tiek protingą metodą. Jūs paimsite knygos pavadinimą, paleiskite jį per mažą kompiuterinę programą, kuri išskleidžia lentynos numerį ir lizdo numerį. Čia įdėkite knygą.

Šios programos grožis yra tas, kad vėliau, kai asmuo grįžta perskaityti knygą, vėl įvesite programos pavadinimą ir grąžinsite tą pačią lentynos numerį ir lizdų numerį, kuris buvo jums iš pradžių pateiktas, ir tai yra knyga, kurioje yra

Programa, kaip minėjo kiti, vadinama maišos algoritmu arba maišos skaičiavimu ir paprastai veikia įvedant į jį įvestus duomenis (šiuo atveju knygos pavadinimą) ir skaičiuoja iš jo skaičių.

Dėl paprastumo, tarkime, kad ji tiesiog konvertuoja kiekvieną raidę ir simbolį į skaičių ir apibendrina juos visus. Tiesą sakant, viskas yra daug sudėtingesnė, bet dabar palikime ją vieni.

Šio algoritmo grožis yra tas, kad jei dar kartą įvesite tą patį įvestį, jis kiekvieną kartą išspaudžia tą patį skaičių.

Gerai, todėl iš esmės, kaip veikia maišos lentelė?

Toliau seka techniniai dalykai.

Pirma, yra kambario dydis. Paprastai tokio maišos algoritmo išvestis yra tam tikroje didelėje dalyje, paprastai daug didesnėje nei jūsų lentelėje esanti erdvė. Pavyzdžiui, tarkime, kad bibliotekoje yra vietos vienam milijonui knygų. Skaičiavimo rezultatas gali būti nuo 0 iki vieno milijardo, kuris yra daug didesnis.

Taigi, ką mes darome? Mes naudojame tai, kas vadinama moduliniu skaičiavimu, kuris iš esmės sako, kad jei skaičiuojate reikiamą skaičių (t. Y. Iki vieno milijardo), bet norėjote likti daug mažesniame intervale, kiekvieną kartą, kai pasiekiate ribą mažesnis diapazonas, nuo kurio pradėjote. 0, bet jūs turite sekti, kiek toli sekėsi.

Tarkime, maišos algoritmo išvestis yra intervale nuo 0 iki 20, ir jūs gaunate vertę 17 iš konkrečios antraštės. Jei bibliotekos dydis yra tik 7 knygos, skaičiuojate 1, 2, 3, 4, 5, 6, o kai gaunate 7, pradėsite nuo nulio. Kadangi turime suskaičiuoti 17 kartų, turime 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, o galutinis skaičius - 3 .

Žinoma, modulio apskaičiavimas nėra atliktas, jis atliekamas su dalijimu ir likusiu. Likusi 17 dalijimosi dalimi 7 yra 3 (7 eina 2 kartus į 17–14, o skirtumas tarp 17 ir 14 yra 3).

Taigi jūs įdėjote knygą į lizdo numerį 3.

Tai sukelia šią problemą. Susilietimai. Kadangi algoritmas neturi galimybės įdėti knygų taip, kad tiksliai užpildytų biblioteką (arba, jei norite, maišos lentelė), jis visada apskaičiuoja anksčiau naudojamą skaičių. Bibliotekos prasme, kai jūs pateksite į lentyną ir su lizdo numeriu, kuriame norite įdėti knygą, ten jau yra knyga.

Yra įvairių būdų, kaip susidoroti, įskaitant duomenų įvedimą į kitą skaičiavimą, kad galėtumėte gauti kitą vietą lentelėje ( dvigubas maišymas ) arba tiesiog rasti vietą, kuri yra arti to, kas jums buvo suteikta (t. Y. Šalia ankstesnės knygos, lizdas buvo taip pat prieinamas kaip linijinis garsas ). Tai reikštų, kad jums reikės skubėti, kai vėliau bandysite rasti knygą, bet tai dar geriau, nei pradedant nuo vieno bibliotekos galo.

Galiausiai, tam tikru momentu galite į biblioteką įdėti daugiau knygų nei leidžia biblioteka. Kitaip tariant, jums reikia sukurti didelę biblioteką. Kadangi tiksli vieta bibliotekoje buvo apskaičiuota naudojant tikslią ir dabartinę bibliotekos dydį, tai reiškia, kad jei pakeisite bibliotekos dydį, jums gali tekti surasti visas vietas visoms knygoms, nes skaičiavimas atliktas siekiant pakeisti jų vietas

Tikiuosi, kad šis paaiškinimas buvo šiek tiek paprastesnis nei kibirai ir funkcijos :)

874
08 апр. Lasse Vågsæther Karlsen atsakymas 2009-04-08 19:33 '09 19:33 2009-04-08 19:33

Naudojimas ir Lingo:

  1. „Hash“ lentelės naudojamos greitai saugoti ir gauti duomenis (arba įrašus).
  2. Įrašai saugomi krepšeliuose, naudojant maišos raktus
  3. Hash klavišai apskaičiuojami taikant išrinkimo algoritmą pasirinktai reikšmei ( pagrindinei vertei), įrašytam į įrašą. Ši pasirinkta vertė turi būti bendra visiems įrašams.
  4. Kiekvienas kibiras gali turėti keletą įrašų, suskirstytų į tam tikrą tvarką.

Realaus pasaulio pavyzdys:

Hash Co. , įkurta 1803 m. ir neturėjusi jokių kompiuterinių technologijų, iš viso turėjo 300 kortelių failų, skirtų išsamiai informacijai (įrašams) saugoti apie 30 000 klientų. Kiekvienas failų aplankas buvo aiškiai pažymėtas savo kliento numeriu, unikaliu numeriu nuo 0 iki 29 999.

Tuo metu registracijos tarnautojai turėjo greitai gauti ir išlaikyti klientų įrašus darbo darbuotojams. Darbuotojai nusprendė, kad būtų efektyviau naudoti nuosaikų metodiką jų įrašams saugoti ir gauti.

Norėdami užregistruoti kliento įrašą, registratoriai turi naudoti unikalų kliento numerį, nurodytą aplanke. Naudodamiesi šiuo kliento numeriu, jie moduliuoja maišos mygtuką iki 300, kad nustatytų saugojimo spintą, kurioje jis yra. Atidarius saugojimo spintą, jie pastebės, kad jame yra daug aplankų, užsakytų pagal kliento numerį. Nustačius teisingą vietą, jie paprasčiausiai įdės jį.

Norėdami gauti kliento įrašą, tarnautojai turėjo gauti kliento numerį ant popieriaus lapo. Naudodami šį unikalų kliento numerį ( maišos raktą ), jie moduliuoja jį iki 300, kad nustatytų, kuris kortelės indeksas turi klientų aplanką. Atidarę spintelę, jie pastebi, kad jame yra daug aplankų, užsakytų pagal kliento numerį. Žvelgiant iš įrašų, jie greitai suranda kliento aplanką ir ištraukia jį.

Mūsų realaus gyvenimo pavyzdyje mūsų kibirai yra dokumentų spintos, o mūsų įrašai yra failų aplankai .


Svarbu nepamiršti, kad kompiuteriai (ir jų algoritmai) veikia geriau nei su styginiais. Taigi prieiga prie didelio masyvo naudojant indeksą yra daug greičiau nei nuosekli prieiga.

Kaip minėjo Simonas, manau, kad labai svarbu , jog maišos dalis konvertuotų didelę erdvę (savavališką ilgį, dažniausiai eilutes ir pan.) Ir rodytų ją mažoje erdvėje (žinomo dydžio, paprastai skaičiaus) indeksavimui. Labai svarbu prisiminti!

Taigi, pirmiau pateiktame pavyzdyje, 30 000 galimų klientų arba panašiai yra susieti su mažesne erdve.


Pagrindinė idėja yra suskirstyti visą savo duomenų rinkinį į segmentus, kad pagreitintumėte tikrąją paiešką, kuri paprastai užtrunka ilgai. Pirmiau pateiktame pavyzdyje kiekviename iš 300 kortelių failų (statistiškai) bus apie 100 įrašų. Paieška (nepriklausomai nuo užsakymo) 100 įrašų yra daug greičiau nei ieškoti pagal 30 000.

Galbūt jūs pastebėjote, kad kai kurie iš tikrųjų tai daro. Tačiau vietoj to, kad būtų sukurta maišos metodo generavimo metodika, daugeliu atvejų jie paprasčiausiai naudos pirmąją pavardės raidę. Todėl, jei turite 26 laikymo spinteles, kurių kiekvienoje yra raidės nuo A iki Z, jūs teoriškai ką tik suskirstėte į savo duomenis ir patobulinote registravimo ir paieškos procesus.

Tikiuosi, kad tai padės,

Jeach!

94
08 апр. Jeach atsakymas balandžio 8 d 2009-04-08 20:20 '09, 20:20, 2009-04-08 20:20

Tai pasirodo esanti gana gili teorija, tačiau pagrindinis planas yra paprastas.

Iš esmės, maišos funkcija yra tik funkcija, kuri veda daiktus iš vienos erdvės (pvz., Savavališkų ilgių eilutes) ir suderina juos su vieta, kuri yra naudinga indeksavimui (pavyzdžiui, nepasirašyti sveikieji skaičiai).

Jei turite tik nedaug dalykų maišymui, galite palikti, paprasčiausiai interpretuodami šiuos dalykus kaip sveikus skaičius, ir jūs baigsite (pvz., 4 baitų eilutės)

Tačiau paprastai turite daug daugiau vietos. Jei dalykų, kuriuos leidžiate naudoti kaip raktai, erdvė yra didesnė nei vietos, kurią naudojate indeksavimui (jūsų uint32 ar kažkas kitas), kiekvienam iš jų negali būti unikali vertė. Jei du ar daugiau maišos maišų turi tą patį rezultatą, turėsite atitinkamai tvarkyti atleidimą iš darbo (tai paprastai vadinama susidūrimu ir kaip elgtis su juo arba priklausys nuo to, ką naudojate maišytuvui).

Tai reiškia, kad jūs norite, kad jis vargu ar turėtų tą patį rezultatą, ir jūs tikriausiai taip pat norėtumėte, kad maišos funkcija būtų greita.

Suderinus šias dvi savybes (ir kelis kitus) daugelis žmonių dirbo su juo!

Praktikoje paprastai turėtumėte gebėti rasti funkciją, kuri, kaip žinoma, gerai tinka jūsų programai ir ją naudoja.

Dabar, kad šis darbas taptų maišos lentele: Įsivaizduokite, kad nerūpėjo atminties naudojimu. Tada galite sukurti masyvą tol, kol jūsų indekso rinkinys (pvz., Uint32). Kai prie stalo pridedate kažką, turite „hash“ klavišą ir peržiūrėkite to indekso masyvą. Jei ten nieko nėra, jūs įdėjote savo vertę. Jei ten jau yra kažkas, įtraukite šį naują įrašą į to sąrašo dalykų sąrašą kartu su pakankama informacija (originaliu raktu ar kažkuo protingu), kad sužinotumėte, kuris įrašas tikrai priklauso kuriam raktui.

Taigi, kai einate ilgą laiką, kiekvienas jūsų maišos lentelės (masyvo) įrašas yra tuščias arba yra vienas įrašas arba įrašų sąrašas. Ekstrakcija - tai paprastas indeksavimas į masyvą ir grąžinant vertę, arba po vertybių sąrašo ir grąžinant teisingą.

Žinoma, praktiškai tai negalite padaryti, tai per daug atminties. Taigi jūs darote viską, remiantis retu masyvu (kur vieninteliai elementai yra tie, kuriuos jūs iš tikrųjų naudojate, visa kita yra numanoma).

Yra daug schemų ir gudrybių, kad šis darbas būtų geresnis, tačiau tai yra pagrindai.

64
08 апр. atsakė į simon 2009-04-08 19:11 '09, 19:11 PM 2009-04-08 19:11

Yra daug atsakymų, tačiau nė vienas iš jų nėra labai vizualus, o maišymo lentelės gali lengvai paspaudžiant.

Hash lentelės dažnai įgyvendinamos kaip susietų sąrašų masyvai. Jei pateikiame lentelę, kurioje saugomi žmonių vardai, po kelių įterpimų, jis gali būti įdėtas į atmintį, kaip parodyta žemiau, kur () uždari numeriai yra teksto / vardo maišos funkcijos vertės.

 bucket# bucket content / linked list [0] --> "sue"(780) --> null [1] null [2] --> "fred"(42) --> "bill"(9282) --> "jane"(42) --> null [3] --> "mary"(73) --> null [4] null [5] --> "masayuki"(75) --> "sarwar"(105) --> null [6] --> "margaret"(2626) --> null [7] null [8] --> "bob"(308) --> null [9] null 

Keletas taškų:

  • kiekvienas iš masyvo įrašų (indeksų [0] , [1] ...) vadinamas konteineriu ir veikia - galbūt tuščias - susietas vertybių sąrašas (kitaip tariant, elementai, šiame pavyzdyje, žmonių pavadinimai)
  • kiekviena reikšmė (pvz., "fred" su hash 42 ) yra susieta su segmentu [hash % number_of_buckets] , pavyzdžiui, 42 % 10 == [2] ; % yra modulio operatorius - likusi dalis dalijama iš segmentų skaičiaus
  • keletas duomenų reikšmių gali susidurti ir bendrauti iš vieno segmento, dažniausiai dėl to, kad jų modifikavimo vertės prieštarauja po modulio veikimo (pavyzdžiui, 42 % 10 == [2] ir 9282 % 10 == [2] ), bet kartais dėl to, kad kad maišos vertės yra tokios pačios (pvz., "fred" ir "jane" abu rodomi su 42 paragrafu)
    • Dauguma maišos lentelių susiduria su susidūrimais - su šiek tiek sumažintu našumu, bet be funkcinės painiavos - lyginant visą ieškomo arba įterpto rakto vertę (čia tekstas) su kiekvienu raktu, kuris jau yra susietame sąraše maišos krepšelyje.

Didinant maišos lentelės dydį, įgyvendintą kaip aprašyta aukščiau, jie linkę keisti savo dydį (t. Y. Sukurti didesnį segmentų masyvą, kurti naujus / atnaujintus susietus sąrašus, ištrinti seną masyvą), kad būtų išlaikytas elemento ir segmento santykis (arba atsisiųsti). faktorius), kur nors nuo 0,5 iki 1,0. Hans pateikia faktinę formulę žemiau pateiktame komentare, tačiau apytikslėms reikšmėms: su apkrovos koeficientu 1 ir kriptografinio stiprumo maišos funkcija 1 / e (~ 36,8%) talpyklos bus tuščios, dar 1 / e (~ 36,8%) ) turi vieną elementą, 1 / (2e) arba ~ 18,4% dviejų elementų, 1 / (3! e) apie 6,1% trijų elementų, 1 / (4! e) arba ~ 1,5% keturių elementų, 1 / (5! E) su ~ ~ 3% turi penkis ir tt - Vidutinis tuščių segmentų grandinės ilgis ~ 1,58, nesvarbu, kiek elementų yra lentelėje (t. Y. Jei yra 100 elementų ir 100 segmentų arba 100 milijonų elementų) ir 100 milijonų blokų, todėl sakome, kad paieška / įterpimas / ištrynimas yra O (1) operacijos su pastoviu laiku.

(Pastabos: ne visos maišos lentelės naudoja susietus sąrašus, tačiau dauguma jų yra naudojamos bendram tikslui, nes uždarytas maišymas (kitaip tariant, atviras adresavimas) - ypač su palaikomomis ištrynimo operacijomis - turi mažiau stabilių veikimo savybių su susidūrimo rizika / maišos funkcijos).

Keletas žodžių apie maišos funkcijas

Pagrindinis uždavinys, atliekamas blogiausiu atveju, siekiant sumažinti maišos funkcijų susidūrimus, yra efektyviai atsitiktinai paskirstyti raktus aplink maišos lentelių blokus, visada generuojant tą patį raktą. Net vienas bitas, keičiantis bet kur rakto vietoje, idealiu atveju - atsitiktinai - sukaupia maždaug pusę bitų gautoje maišos verte.

Paprastai man organizuojama, kad matematika būtų pernelyg sudėtinga. Paminėsiu vieną lengvai suprantamą būdą - tai ne pats skalei pritaikytas ar talpinamasis, bet elegantiškas (pavyzdžiui, šifravimas vienkartine klaviatūra!) Kadangi manau, kad tai padeda grąžinti pirmiau minėtas norimas savybes. Tarkime, kad turite 64 bitų double - galite sukurti 8 lenteles, kurių kiekvienoje yra 256 atsitiktiniai skaičiai (t. Y. size_t random[8][256] ), tada naudokite kiekvieną 8 bitų / 1 baitų reprezentacijos fragmentą double atmintis, skirta indeksuoti į kitą lentelę, XOR atsitiktiniai skaičiai, kurių ieškote. Taikant šį metodą lengva matyti, kad double rezultato kiekio keitimas visur lemia kito atsitiktinio skaičiaus paiešką vienoje iš lentelių ir visiškai nekoreguotą galutinę vertę.

Tačiau daugelio bibliotekų maišos funkcijos praleidžia visiškus skaičius be pakeitimų, kurie blogiausiu atveju yra labai linkę į susidūrimus, tačiau tikimasi, kad gana dažnai pasitaikančiais sveikojo skaičiaus klavišais, kurie linkę didėti, jie konvertuojami į nuoseklius segmentus, paliekant mažiau daugiau tuščių nei 36,8% atsitiktinio sugedimo, ir todėl turi mažiau susidūrimų ir mažiau ilgai susietų susidūrimo elementų sąrašų, nei pasiekiama atsitiktiniais žemėlapiais. Taip pat puikiai tinka sutaupyti laiko, reikalingo sukurti stiprią maišą. Kai raktai neišauga, tikimasi, kad jie bus gana atsitiktiniai, jiems nereikės stiprios maišos funkcijos, kad būtų visiškai atsitiktine tvarka parinkta jų vieta segmentuose.