Autor |
Beitrag |
Gausi 
      
Beiträge: 8548
Erhaltene Danke: 477
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Mi 20.07.05 14:11
Keine Angst... den Spass lass ich mir durch sowas nicht verderben.
Hab das mit der Editierdistanz mal probiert, und für meine Zwecke modifiziert:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122:
| const IgnoreChars :Set of Char = ['.',' ','-','_','´','`','/','~'];
function min2(a,b:integer) : integer; begin if a < b then result := a else result := b; end; function min3(a,b,c:integer) : integer; begin if a < b then result := min2(a,c) else result := min2(b,c); end;
function costChange(c1,c2: char): integer; begin if (AnsiLowerCase(c1)=AnsiLowercase(c2)) or ((AnsiLowerCase(c1)='ä') AND (AnsiLowerCase(c2)='a') ) or ((AnsiLowerCase(c1)='ö') AND (AnsiLowerCase(c2)='o') ) or ((AnsiLowerCase(c1)='ü') AND (AnsiLowerCase(c2)='ü') ) or ((AnsiLowerCase(c1)='ß') AND (AnsiLowerCase(c2)='s') ) or ((AnsiLowerCase(c2)='ä') AND (AnsiLowerCase(c1)='a') ) or ((AnsiLowerCase(c2)='ö') AND (AnsiLowerCase(c1)='o') ) or ((AnsiLowerCase(c2)='ü') AND (AnsiLowerCase(c1)='ü') ) or ((AnsiLowerCase(c2)='ß') AND (AnsiLowerCase(c1)='s') ) then result := 0 else result := 1; end;
function costGap(c1: char):integer; begin if c1 in IgnoreChars then result :=0 else result :=1; end;
function costGap2(c1: char):integer; begin if c1 in IgnoreChars then result :=0 else result :=1; end;
function EditierDistanz(s1,s2: string; tolerance:integer) : integer;
var A: array of array of integer; i, j, min: integer; ls1,ls2:integer; begin
ls1 := length(s1); ls2 := length(s2); setlength(A,ls1+1); for i:=0 to length(A)-1 do setlength(A[i],ls2+1);
for j := 0 to ls2 do A[0,j] := 0;
for i := 1 to ls1 do A[i,0] := A[i-1,0] + costGap(s1[i]);
min := tolerance + 1; for i := 1 to ls1-1 do begin min := tolerance + 1; for j := 1 to ls2 do begin A[i,j] := min3( A[i-1,j] + costGap2(s1[i]), A[i,j-1] + costGap(s2[j]) , A[i-1,j-1] + costChange(s1[i],s2[j]) ); if A[i,j] < min then min := A[i,j]; end; if min > tolerance then break; end;
if min > tolerance then begin result := tolerance+1; exit; end;
for j := 1 to ls2 do begin A[ls1,j] := min3( A[ls1-1,j] + costGap2(s1[ls1]), A[ls1,j-1] , A[ls1-1,j-1] + costChange(s1[ls1],s2[j])); end; result := A[ls1,ls2]; end; |
Die funktioniert ganz gut. Als Toleranz nehme ich zur Zeit LengthOhneIgnoreChars(suchstring) Div 5, d.h. für je 5 Buchstaben lass ich einen Fehler (löschen/ersetzen/einfügen) zu.
Großes Manko: Die Funktion ist recht lahm - das Durchsuchen von schlappen 40.000 Titeln nach einem Artist mit 'Hosen' im Namen dauert gut 15 Sekunden - auf nem 1800MHz-Rechner. Da ist SoundEx deutlich schneller.
_________________ We are, we were and will not be.
|
|
StefanH
      
Beiträge: 1144
Win XP
D5 Standard, D7 Pers, D2005 Pers
|
Verfasst: Mi 20.07.05 14:29
@Gausi: mich würde brennend interessieren, wo hier der unterschied ist:
Delphi-Quelltext 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49:
| { ... } function costGap(c1: char):integer; begin if c1 in IgnoreChars then result :=0 else result :=1; end;
function costGap2(c1: char):integer; begin if c1 in IgnoreChars then result :=0 else result :=1; end; |
_________________ "Als es noch keine Computer gab, war das Programmieren noch relativ einfach."(Edsger W. Dijkstra)
"Ich bin nicht von Sinnen, sondern ich rede wahre und vernünftige Worte." (Paulus)
|
|
Gausi 
      
Beiträge: 8548
Erhaltene Danke: 477
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Mi 20.07.05 14:39
Da ist (noch) kein Unterschied. Idee dabei ist, dass ich evtl. ein Einfügen eines Chars anders bewerten kann, jen nachdem, in welchem String ich das für einen Match einfügen muss (also im zu suchenden Muster oder im Textstring, in dem ich suche)
Beispiel:
ich suche 'hosen', und vergleiche grade mit "hossen" (Muster erweitern für einen Match)
ich suche 'hossen', und vergleiche mit "hosen" (Text erweitern)
Ich bin noch nicht ganz sicher, ob da eine unsymmetrische Behandlung evtl. Vorteile bringt, daher hab ich das mal offengelassen 
_________________ We are, we were and will not be.
|
|
matze
      
Beiträge: 4613
Erhaltene Danke: 24
XP home, prof
Delphi 2009 Prof,
|
Verfasst: Do 21.07.05 09:51
wow. cool, dass du es auch ohne soundex kannst. der code sieht echt ut un brauchbar aus. wenn er nur ein wenig schneller wäre...
_________________ In the beginning was the word.
And the word was content-type: text/plain.
|
|
Gausi 
      
Beiträge: 8548
Erhaltene Danke: 477
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Do 21.07.05 10:59
Nur der Klarheit halber: Mein Code oben ist keine alternative Implementierung von SoundEx.
Ich ermittle im Wesentlichen die Anzahl der Schritte, die benötigt werden, um den Suchstring auf ein Teilwort des anderen Strings zu matchen. Ein Schritt ist dabei entweder Löschen eines Buchstaben, Einfügen eines Buchstaben, oder das Ändern eines Buchstaben. Bestimmte Chars dürfen dabei beliebig gelöscht werden (z.B. ./' ;:-_), ohne als Schritt gezählt zu werden.
Da dieser Algorithmus prinzipiell mehr leistet, ist er auch langsamer als SoundEx.
Ich werde gleich nochmal eine Alternative mit SoundEx testen. Habe jetzt genauer verstanden, was SoundEx eigentlich berechnet und habe somit eine bessere Basis für vernünftiges experimentieren 
_________________ We are, we were and will not be.
|
|
paddes19
Hält's aus hier
Beiträge: 4
Win 2000, XP
D5 Prof
|
Verfasst: Di 13.09.05 08:30
Möchte das Thema nochmal aufwärmen.
Ich hab das Problem das meine strings nicht aus
Buchstaben bestehen sondern hauptsächlich aus
Zahlen, somit ist der soundex algorithmus völlig
nutzlos, oder?
Jetzt ist die Frage gibt es ausser dem Editierabstand
noch andere Möglichkeiten eine "unscharfe" Suche durch-
zuführen?
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Di 13.09.05 08:58
Ja, es gibt diverse Versuche, zwei Strings auf Ähnlichkeit zu vergleichen.
Zwei Möglichkeiten wurden hier gezeigt: Soundex (von D.Knuth) und die Transformation in Phoneme. Letzteres führt bei geeigneten Phonemen zu erstaunlich genauen Ergebnissen (vom 'gesunden Menschenverstand' her).
Du benötigst vermutlich einen statischen Vergleich zweier Strings.
Googel mal nach 'fuzzy string match delphi' oder 'unsharp compare' oder 'fuzzy string pattern matching' oder so.
Das hier stach (Autsch!) ins Auge
delphi.helli.de/Tips...unsharp_compare.html
K.A. Ob das was für dich ist.
|
|
paddes19
Hält's aus hier
Beiträge: 4
Win 2000, XP
D5 Prof
|
Verfasst: Di 13.09.05 09:59
Super, danke für die schnelle Antwort.
Ich denke das lässt sich verwenden.
Hab ein ähnliche Funktion geschrieben mit Hilfe
der Levenshtein-Distanz.
Werd jetzt mal die beiden vergleichen.
Ich würde gerne eine Tabelle (ca. 20000) einer Datenbank
durchsuchen. Die Einträge bestehen eben aus Zahlenreihen.
Hat jemand einen Lösungsansatz wie ich vorgehen soll?
Die Tabelle ist momentan nicht sortiert, d.h es wäre
wahrscheinlich segr zeitaufwendig jeden Eintrag zu
vergleichen.
Oder gibt es da ganz andere Möglichkeiten (fertige
Datenbankfunktion).Das Problem ist Soundex lässt
sich nicht verwenden, weil es sich auf Buchstaben
bezieht.
|
|
ManuelGS
      
Beiträge: 173
Win XP HE, Suse Linux
D6, D7, D2005 Personal
|
Verfasst: Di 13.09.05 12:48
@paddes19
Was genau willst du denn machen? Nur 20.000 Zahlen sortieren? Da würden nämlich Standardsuchalgorithmen wahrscheinlich ausreichen. 20.000 ist ja auch keine SO riesige Menge.
_________________ "Leben ist gänzlich Bühne und Spiel; so lerne denn spielen
und entsage dem Ernst - oder erdulde das Leid." - Palladas von Alexandria
|
|
paddes19
Hält's aus hier
Beiträge: 4
Win 2000, XP
D5 Prof
|
Verfasst: Di 13.09.05 12:59
Ob sortiert oder unsortiert kommt darauf an was
für die Suche besser ist.
Das Hauptziel soll sein das die Suche unscharf ist,
d.h. wenn man Zahlendreher drin hat, sollen
trotzdem Ergebnisse geliefert werden.
Beispiel: Ich hab irgendeine EAN (4-003630-022530)
und möchte in einer Tabelle alle ähnlichen EAN's
finden.
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Di 13.09.05 21:25
Mein Vorschlag:
20.000 Datensätze einlesen, dann mit fuzzy search oder Levenshtein-Distanz o.ä. rumexperimentieren. Wir haben sowas für Adressen, das ist auch lustig, weil die Disponenten manchmal 'Dipl Ing. Hubert Kassupke', 'Hubbard Kasupque', 'Ing Kasubke' etc. schreiben. Wir verwenden phoneme. Anschließend wird daraus ein 'Key' erzeugt der definiert, das die Adresse eindeutig ist. Also: Zwei Adressen sind dann eindeutig, wenn Sie einen identische 'Key' besitzen... Man, haben wir rum probiert (war lecker) und danach rumprobiert.
Bei deinen EAN-Codes solltest Du Dir überlegen, welche Fehler auftreten können.
Do solltest eine Funktion schreiben, die zwei Codes anhand der von Dir analysierten Fehler vergleicht, also
-Zahlendreher
-ähnlich klingende Ziffern (bei akustischer Übermittlung bei der Erfassung)
-ähnlich aussehende Ziffern (bei visuellem Abtippen)
-nebeneinanderliegende Ziffern (bei Eingabe über Nummerkblock)
etc
unterschiedlich bewertet. Herauskommen sollte -wie bei der Levenshtein-Distanz- ein Wert, der die Ähnlichkeit bezeichnet.
Dann erstellst Du für jeden Eintrag I alle Distanzen der Eintrage I+1.... N und zeigst die an, die unter/oberhalb eines bestimmten Schwellenwertes liegen, an.
Du wirst nicht umhin kommen, alle Codes miteinander zu vergleichen, ausser, Du findest eine Ordnungsfunktion, die Dir EAN-Codes so sortiert, das ähnliche Codes nebeneinander liegen. Sowas trau ich mir nicht zu.
Las den PC doch die paar Minuten rechnen...
|
|
paddes19
Hält's aus hier
Beiträge: 4
Win 2000, XP
D5 Prof
|
Verfasst: Mi 14.09.05 07:30
Vielen Dank für die Antwort,  , da kann ich jetzt eine
Weile rumprobieren.Mal schauen ob das klappt.
|
|
|