AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Zusätzliches WOrt in Hashtabelle einfügen?

Ein Thema von schöni · begonnen am 17. Mai 2012 · letzter Beitrag vom 20. Mai 2012
Antwort Antwort
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.120 Beiträge
 
Delphi 11 Alexandria
 
#1

AW: Zusätzliches WOrt in Hashtabelle einfügen?

  Alt 19. Mai 2012, 01:48
Wahrscheinlich wäre eine lineare Suche bei der geringen Anzahl an Schlüsseln nicht langsamer als diese Hashtabelle.
Ausserdem wird enthält das Array RWords jede Menge Luft (23*10*(20+1) = 4830 Bytes); die lineare Suche bräuchte nicht mal ein Viertel davon.
Von der binären Suche in einer sortierten StringListe mal ganz zu schweigen
Ich hab's mal ausprobiert: Fast dreimal so schnell, wie die Lösung mit Hashtable.
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#2

AW: Zusätzliches WOrt in Hashtabelle einfügen?

  Alt 19. Mai 2012, 06:30
Ich denke es hängt auch davon ab, ob die Menge an Schlüsseln sich nie verändert oder ob zur Laufzeit Schlüssel hinzugefügt oder entfernt werden sollen.

a.) feste Menge an Schlüsseln
-> binäre Suche, Interpolationssuche oder quadratische Binärsuche liefert das beste Ergebnis

b.) zur Laufzeit wird eine begrenzte Menge an Schlüsseln hinzugefügt
-> Hashverfahren können genützt werden
Bei zu hohem Füllungsgrad der Hashtabelle wird die Suche aber langsam.

c.) zur Laufzeit dürfen auch Schlüssel gelöscht werden
-> Hashverfahren versagen, weil das Löschen von Schlüsseln nicht erlaubt ist
Andreas
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#3

AW: Zusätzliches WOrt in Hashtabelle einfügen?

  Alt 19. Mai 2012, 08:48
Ich denke es hängt auch davon ab, ob die Menge an Schlüsseln sich nie verändert oder ob zur Laufzeit Schlüssel hinzugefügt oder entfernt werden sollen.

a.) feste Menge an Schlüsseln
-> binäre Suche, Interpolationssuche oder quadratische Binärsuche liefert das beste Ergebnis

b.) zur Laufzeit wird eine begrenzte Menge an Schlüsseln hinzugefügt
-> Hashverfahren können genützt werden
Bei zu hohem Füllungsgrad der Hashtabelle wird die Suche aber langsam.

c.) zur Laufzeit dürfen auch Schlüssel gelöscht werden
-> Hashverfahren versagen, weil das Löschen von Schlüsseln nicht erlaubt ist
Da bin ich aber ganz anderer Meinung (oder beziehst Du dich auf diese konkrete Implementierung?):
Hashtabellen (nicht gerade diese) sind in Puncto Performance bei großen Datenmengen nicht zu schlagen. Die Performance für das Einfügen, Suchen und Löschen ist stehts O(1), sofern eine dynamische Variante genutzt wird.

Nur bei kleinen Datenmengen macht sich der overhead der Hashfunktion bemerkbar, hier sind vor allem einfache Verfahren im Vorteil.

Bei einem Scanner/Lexer/Tokenizer würde ich auf jeden Fall eine Hashmap nehmen, wobei ich nicht nur die Wörter speichern würde, sondern gleich auch noch die Kategorie, also 'reserved word','Zahl','Symbol','Identifier' etc. Der ist zwar anfangs langsamer, aber da ich den beim Scanner befülle, sollte ich unterm Strich besser fahren.

Ich habe es nun einmal verglichen und bei mir ist diese angeblich so blöde Variante die Schnellste.
Verglichen habe ich das Suchen in:
Dieser Implementierung, einer Dictionary, einer sortierte Stringlist und einem unsortieres Array.

Die Idee bei diesem hier gezeigten Verfahren ist es ja, die Wortliste durch eine Unterteilungsfunktion in kleinere Listen zu zerteilen:

Wenn man als Funktion einfach das erste Zeichen nimmt (und die Liste umsortiert), wird die Geschichte um 35% schneller. Und wenn man den ShortString gegen einen normalen String tauscht, wird das 4x so schnell.

Geändert von Furtbichler (19. Mai 2012 um 09:59 Uhr)
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: Zusätzliches WOrt in Hashtabelle einfügen?

  Alt 19. Mai 2012, 13:02
[..] Und wenn man den ShortString gegen einen normalen String tauscht, wird das 4x so schnell.
Aus eigener Erfahrung kann ich sagen, wenn man Strings konsequent mit const als Parameter übergibt ist der Geschwindigkeitsvorteil gegenüber Shortstrings kaum noch spürbar. Was aber immer mega bremst sind Trim und UpperCase.
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#5

AW: Zusätzliches WOrt in Hashtabelle einfügen?

  Alt 19. Mai 2012, 14:35
Hi
Mit Const und AnsiString und UpperCase statt der Schleife ist es nicht mehr 4x so schnell, sondern nur noch 2,5x so schnell
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#6

AW: Zusätzliches WOrt in Hashtabelle einfügen?

  Alt 20. Mai 2012, 05:43
Bei zu hohem Füllungsgrad der Hashtabelle wird die Suche aber langsam.
Da bin ich aber ganz anderer Meinung (oder beziehst Du dich auf diese konkrete Implementierung?):
Hashtabellen (nicht gerade diese) sind in Puncto Performance bei großen Datenmengen nicht zu schlagen. Die Performance für das Einfügen, Suchen und Löschen ist stehts O(1), sofern eine dynamische Variante genutzt wird.
Die Standardimplementierung einer Hash-Tabelle sieht so aus, dass es eine bestimmte Anzahl an Buckets (Einträge in einem Array) gibt.
Die Hashfunktion bildet den Schlüssel auf den Index im Array ab.
Sollte ein Bucket aber schon belegt sein, dann nimmt man den nächsten freien Eintrag. ("Linear Probing")
Bei zu hohem Füllungsgrad; also das Array ist schon zu 80% voll; wird immer seltener ein freies Bucket gefunden.
Es bilden sich ganze Ketten von belegten Buckets, die das Suchen und Eintragen von Schlüsseln verlangsamen.
Bei 100% Belegung müssen für die Suche nach einem nichtvorhandenen Schlüssel ALLE Buckets durchsucht werden.
Natürlich kann man Hashtabellen auch anderst strukturieren, z.B. als Array of TList und damit einige dieser Nachteile vermeiden.


Ansonsten ist nach meinen Messung die lineare Suche bei diesem Beispiel schneller!
Delphi-Quellcode:
for i := 1 to 1000000 do
begin
   IsReserved('test'); // Miss
   IsReserved('string'); // Hit
end;
Die Hashtabelle aus Beitrag #1 benötigt ~ 1,32 Sekunden,
die lineare Suche braucht ~ 1,02 Sekunden.

Wer selbst messen möchte:
Delphi-Quellcode:
function IsReservedLinSearch(S:string):Boolean;
const list : array[0..87] of string =
(
'CONST','OR','TYPE',
'SHL','FOR','DESTRUCTOR','INHERITED','PROPERTY',
'TO','OBJECT','TRY','CDECL',
'VAR','ASSEMBLER',
'AND','FORWARD','THEN','IMPLEMENTATION','LIBRARY','RAISE',
'IF','UNTIL','PUBLISHED','STORED',
'SET','CLASS','INITIALIZATION','THREADVAR',
'LABEL','PROGRAM','SHR','FINALIZATION','NODEFAULT',
'CASE','END','DISPID','INDEX','RESIDENT',
'ABSOLUTE','WHILE','DO','AUTOMATED',
'RECORD','PACKED','INLINE','PUBLIC','PRIVATE','AS','FAR','OVERRIDE', //50
'OF','STRING','NOT','DEFAULT','DYNAMIC','MESSAGE',
'FILE','REPEAT','BEGIN',
'IN','EXTERNAL','INTERFACE','EXPORTS','NAME','STDCALL',
'GOTO','PROCEDURE',
'DOWNTO','ARRAY','PROTECTED',
'FUNCTION',
'MOD','WITH',
'IS','NEAR',
'XOR','CONSTRUCTOR',
'DIV','NIL','EXCEPT',
'ELSE','UNIT','USES','FINALLY','ABSTRACT',
'EXPORT','PASCAL','VIRTUAL'
);
var
  i : Integer;
begin
   S := Uppercase(S);
   for i := low(List) to High(List) do
   begin
      if S = list[i] then
      begin
         Result := True;
         Exit;
      end;
   end;
   Result := False;
end;
Andreas
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#7

AW: Zusätzliches WOrt in Hashtabelle einfügen?

  Alt 20. Mai 2012, 08:32
[QUOTE=shmia;1167302]
Die Standardimplementierung einer Hash-Tabelle sieht so aus, .."Linear Probing"...
Ich sprach aber von einer brauchbaren Hashmap, also z.B. "chaining" und dynamisch.
Hashtabellen (nicht gerade diese) sind in puncto Performance bei großen Datenmengen nicht zu schlagen. ... sofern eine dynamische Variante genutzt wird.
Zitat:
Ansonsten ist nach meinen Messung die lineare Suche bei diesem Beispiel schneller!
Für mich ist der Test nicht aussagekräftig, da er zu wenig Fälle betrachtet. Versuch diesen:
Delphi-Quellcode:
for i := 1 to 1000000 do
  for j := 0 to allReservedWordsCount-1 do
    IsReserved(reservedWords[j]);
Er sucht nach allen reservierten Wörtern. Wir können noch ein paar Wörter hinzupacken, die nicht gefunden werden sollen, wenn Du magst. Oder Du schlägst einen anderen Test vor.
Dein Test zeigt aber sehr deutlich, das lineare Suche bei kleinen Listen nicht zu topppen ist. Eine Liste mit 88 Elemente ist hier aber nicht mehr "klein".

Der Test mit folgender Klasse führt bei mir zu erheblichen Geschwindigkeitszuwächsen und hält sich bezüglich Speicherauslastung in Grenzen:
Delphi-Quellcode:
Unit pascalKeywordList;
Interface
Type
  TKeywordBucket = Record
    data: Array[0..10] Of String;
    dataCount: Integer;
  End;
  // A generic chaining hash map using the first character as hash function.
  TKeywordListHash = Array[Char] Of TKeywordBucket;

  TPascalKeywordList = Class
  private
    keywords: TKeywordListHash;
    Procedure FillList;
  public
    Constructor Create;
    Function IsReserved(Const aWord: String): Boolean;
  End;

Implementation
Const
  keywordCount = 88;
  keywordList: Array[0..keyWordCount - 1] Of String = (
    'CONST', 'OR', 'TYPE', 'SHL', 'FOR', 'DESTRUCTOR', 'INHERITED', 'PROPERTY',
    'TO', 'OBJECT', 'TRY', 'CDECL', 'VAR', 'ASSEMBLER', 'AND', 'FORWARD', 'THEN',
    'IMPLEMENTATION', 'LIBRARY', 'RAISE', 'IF', 'UNTIL', 'PUBLISHED', 'STORED',
    'SET', 'CLASS', 'INITIALIZATION', 'THREADVAR', 'LABEL', 'PROGRAM', 'SHR',
    'FINALIZATION', 'NODEFAULT', 'CASE', 'END', 'DISPID', 'INDEX', 'RESIDENT',
    'ABSOLUTE', 'WHILE', 'DO', 'AUTOMATED', 'RECORD', 'PACKED', 'INLINE', 'PUBLIC',
    'PRIVATE', 'AS', 'FAR', 'OVERRIDE', 'OF', 'STRING', 'NOT', 'DEFAULT', 'DYNAMIC',
    'MESSAGE', 'FILE', 'REPEAT', 'BEGIN', 'IN', 'EXTERNAL', 'INTERFACE', 'EXPORTS',
    'NAME', 'STDCALL', 'GOTO', 'PROCEDURE', 'DOWNTO', 'ARRAY', 'PROTECTED', 'FUNCTION',
    'MOD', 'WITH', 'IS', 'NEAR', 'XOR', 'CONSTRUCTOR', 'DIV', 'NIL', 'EXCEPT',
    'ELSE', 'UNIT', 'USES', 'FINALLY', 'ABSTRACT', 'EXPORT', 'PASCAL', 'VIRTUAL');

{ TPascalKeywordList }

Constructor TPascalKeywordList.Create;
Begin
  FillList;
End;

Procedure TPascalKeywordList.FillList;
Var
  i: Integer;

  Procedure _FillIntoList(Const keyWord: String);
  Begin
    Assert(Length(keyWord) > 1, 'the keyword to be filled in may not be empty');
    With keywords[keyword[1]] Do Begin
      data[dataCount] := keyWord;
      inc(dataCount);
    End
  End;

Begin
  For I := 0 To keywordCount - 1 Do _FillIntoList(keyWordList[i]);
End;

Function TPascalKeywordList.IsReserved(Const aWord: String): Boolean;
Var
  i: Integer;

Begin
   Assert(Length(aWord) > 1, 'the word to be searched may not be empty');
   With keywords[aWord[1]] Do
    For I := 0 To dataCount - 1 Do
      If data[i] = aWord Then Begin
        result := true;
        exit;
      End;
  result := false;
End;

End.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:37 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz