Entwickler-Ecke

Algorithmen, Optimierung und Assembler - "Intelligente" Suche in Strings


Gausi - Di 19.07.05 15:31
Titel: "Intelligente" Suche in Strings
Normalerweise sucht man ja so als Delphi-Programmierer mit dem schönen Befehl pos o.Ä. nach Substrings in größeren Strings. Ist ja alles schön und gut.
Ich möchte jetzt aber eine gewisse Fehlertoleranz zulassen. GROß/kleinschreibung ignoriere ich schon, bevor ich groß rumerzähle, ein paar Beispiele:

Suchbegriff: REM
gewünschte Ergebnisse (z.B.): REM, R E M, R.E.M.
oder:
Herbert Grönemeyer -> Herbert Grönemeier, Herbert Groenemeyer,
Seeed -> Seed
Die Ärzte -> Die Aerzte, Ärzte, Aerzte
Die Toten Hosen -> Die Roten Rosen (!), Hosen, Toten Hosen
Und weiter gehts mit AC/DC, A-Ha, A-Teens, BroSis, B3, Guns 'N Roses, H-Blockx,...

Ich möchte also nicht nur, dass eine exakte Übereinstimmung gefunden wird, sondern auch, dass "ähnliche" Strings gefunden werden - was auch immer ähnlich genau bedeutet.

Vieles würde man mit viel Gewalt hinkriegen (alle Leer/Sonderzeichen per Stringreplace durch '' ersetzen), aber für elegant oder effizient halte ich das nicht. Und kleine Rechtschreibfehler (Alanis Mor(r)is(s)et(t)e, Grönemei(?)er, Mittermai(?)er, H-Blocks(?), Se(e)ed) bekommt man damit auch nicht weg.

Jemand ne Idee?


jasocul - Di 19.07.05 15:40

Hi Gausi,
in Datenbanke gibt es doch phonetische Suchfunktionen. Das geht doch ganz grob in die Richtung. Geh doch mal auf die Suche, ob es dazu entsprechende Algos gibt.


retnyg - Di 19.07.05 15:55

nur ne idee : lies jedes wort als ganzes und mach jeweils einen vergleich. wenn z.b. 80% der zeichen ['a'..'z'] übereinstimmen, ist es ein treffer, sonst nicht. punkte und sonderzeichen sollten also ausgefiltert werden


Gausi - Di 19.07.05 16:10

@Jasocul: Punkt1: Scheint sehr kompliziert zu sein, da die Strings wohl in einzelne Laute zerlegt werden müssen. Da ich aufgrund der Anwendung auch mit mehreren Sprachen und vielen Sonderfällen zu tun habe, kaum durchführbar - fürchte ich. Oder zumindest unangemessener Aufwand.

@retnyg:

Quelltext
1:
2:
3:
A e r z t e
| | | | | |
Ä r z t e
Da sind genau Null Zeichen gleich, der Rest total unterschiedlich. Oder wie meintest du das? Gut bei

Quelltext
1:
2:
A C(-)D C
A C...D C
würds funktionieren...

Über phonetische Suche bin ich aber auf das [http://de.wikipedia.org/wiki/Lewenstein-Distanz] und das [http://de.wikipedia.org/wiki/Sequenzalignment] gestoßen. Könnte interessant sein...


ManuelGS - Di 19.07.05 16:17

Deinen Suchstring aufsplitten!
Z.B. alá Dos nach "Gr*nem*er" suchen lassen, d.h.:
1. nach "gr" (kleinGROSS hast du ja schon...)
2. von da aus nach "nem"
3. und schließlich nach "er"

String zusammensetzen (von "gr" bis "er"). Der gefundene String darf nicht größer als length(Gr*nem*er)+x sein. x ist deine "Fehlertoleranz" in Zeichenanzahl.

Wie du auf die * kommst...nach "ae", "ä", "th", "q" etc. suchen und mit "*" ersetzen oder gleich in einem Array speichern. (Erst nach den größeren Strings suchen ("th" vor "t").

Wäre jetzt mal mein Ansatz...
Wenn eine Band allerdings "Skrhtäßth" heißt, taugt das ganze allerdings nichts...


jasocul - Di 19.07.05 16:25

Das mit den Datenbanken (SoundEx) sollte auch nur ein Gedankenansatz sein.
Ich glaube, da gibt es ein paar Standard-Ansätze iirc:

Alle Umlaute in normale Vokale wandeln. (Ä -> A)
Vokalfolgen auf den führenden Vokal reduzieren. (ae -> a)
Doppelkonsonanten auf einen reduzieren, (sch -> s, th -> t, ck -> k, ch -> k, ...)
Lautverschiebungen anpassen (Dehnungslaute u.ä.)
Berücksichtigung der üblichen Tippfehler (da wird es dann aber wohl richtig heftig)

Da wirst du dann wohl ein paar Umwandlungstabellen pflegen müssen. :roll:

Einige Dinge dürften sprachenspezifisch sein.


Gausi - Di 19.07.05 16:47

user profile iconjasocul hat folgendes geschrieben:
Berücksichtigung der üblichen Tippfehler (da wird es dann aber wohl richtig heftig)
Deswegen hab ich ja den Thread aufgemacht :lol:

user profile iconjasocul hat folgendes geschrieben:
Da wirst du dann wohl ein paar Umwandlungstabellen pflegen müssen.
Und genau das möchte ich nach Möglichkeit auch vermeiden. Ich bin kein Phonetik-Experte, und das ist mir - sorry - so ohne weiteres zu schwammig. Und Bücher über Phonetik werde ich deswegen nicht durchackern.

@ManuelGS: Ist natürlich ne Idee, die mir im Wesentlichen auch schon gekommen ist, aber das halte das irgendwie für Frickelei :nixweiss:


jasocul - Di 19.07.05 16:56

Vielleicht geht folgender Ansatz:
Erstmal alle Konsonanten durch 'X' ersetzen und alle Vokale durch 'A'.
Danach alle Doppelvokale und Doppelkonsonanten auf "1" reduzieren.
Daraus die 1. Ergebnisliste erstellen.
Damit hättest du den gröbsten Filter erstellt.
Daraus eine Verfeinerung bilden (also die Genauigkeit der Suche wieder erhöhen).
Dieses solange durchführen, bis ein exakter Treffer vorliegt (sollte man vielleicht am Anfang schon prüfen) oder die Ergebnismenge leer ist.

Die Regeln für die Verfeinerung müsste man dann allerdings noch definieren. Da habe ich heute aber keine Zeit mehr für.


retnyg - Di 19.07.05 17:16

user profile iconGausi hat folgendes geschrieben:

@retnyg:

Quelltext
1:
2:
3:
A e r z t e
| | | | | |
Ä r z t e
Da sind genau Null Zeichen gleich, der Rest total unterschiedlich. Oder wie meintest du das?

die buchstaben erzt sind in beiden vorhanden (das sind 80% übereinstimmung).
dazu ist noch ihre reihenfolge gleich.
ausserdem sollte deine software von haus aus intern nur mit ae statt ä arbeiten.


Gausi - Di 19.07.05 17:34

user profile iconretnyg hat folgendes geschrieben:

die buchstaben erzt sind in beiden vorhanden (das sind 80% übereinstimmung).
dazu ist noch ihre reihenfolge gleich.
Glaub ich gerne, seh ich ja auch auf einem Blick, aber wie macht man das im Programm?

user profile iconretnyg hat folgendes geschrieben:
ausserdem sollte deine software von haus aus intern nur mit ae statt ä arbeiten.
Ja, und was mach ich dann mit

Quelltext
1:
2:
3:
M o r r i s s e t t e
| | | X X X X X X
M o r i s e t t e
...? da kommt dann wieder: alle doppelten Buchstaben durch einen ersetzen...:? Ist sicherlich eine ganz nette Idee, aber irgendwie sagt die mir noch nicht ganz zu...das ist halt wieder auf eine bestimmte Art von Fehler zugeschnitten. Und wenn ich das alles hintereinander überprüfe, dann rechne ich ne halbe Sekunde an einem Vergleich rum - und das is zuviel. Ich würde ja erst den String bearbeiten müssen, bevor ich mit dem eigentlichen Vergleich anfange. Besser wäre es doch, wenn ich anfange, zu vergleichen, und recht schnell feststelle, dass weiteres Arbeiten nix bringt...

Jasocul hat übrigens per ICQ den Vorschlag geäußert, dass ich daraus eine Art Wettbewerb starte: Wer schreibt den besten intelligenten Suchalgorithmus?


Spaceguide - Di 19.07.05 18:37

Guck dir mal den Algorithmus für den minimalen Editierabstand an. Damit kannst du die Ähnlichkeit zweier Zeichenketten berechnen.


retnyg - Di 19.07.05 19:08


Delphi-Quelltext
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:
function intCompare(s1, s2: string) :boolean;
// compares wors on a similar content
// (c) retnyg

  function reformat (s:string):string;
  var l,i : integer;
  begin
     l:=length(s);
     result := '';
     for i := 1 to l do
       case upcase(s[i]) of
         'A'..'Z': result := result + upcase(s[i]);
         'Ä','ä': result := result + 'AE';
         'Ö','ö': result := result + 'OE';
         'Ü','ü': result := result + 'UE';
       end;
  end;

type ad = array [0..26of dword;
var
//    count1, count2: ad;
//    pcount: ^ad;
    usedletters1: string;
    usedletters2: string;
    plen : pinteger;
    i,l,l2:integer;
    p, p2: pstring;

begin
  result := false;

  if (length(s1) = 0or (length(s2) = 0then exit;

  s1:=reformat(s1);
  s2:=reformat(s2);

  l := length(s1);
  l2 := length(s2);

//  if (l * 100) div (length(s2) * 100)
  plen := @l;
  p := @s1;
  p2 := @usedletters1;
//  pcount:= @count1;
//  zeromemory(pcount,sizeof(ad));

  setlength(p2^,0);

  for i := 1 to plen^ do begin
     if pos(p^[i],p2^) = 0 then
      p2^ := p2^ + p^[i];
//     inc(count1[ord(p^[i])-65]);
  end;

  plen := @l2;
  p := @s2;
  p2 := @usedletters2;
//  pcount:= @count2;
//  zeromemory(pcount,sizeof(ad));

  setlength(p2^,0);

  for i := 1 to plen^ do begin
     if pos(p^[i],p2^) = 0 then p2^ := p2^ + p^[i];
//     inc(count1[ord(p^[i])-65]);
  end;

  if usedletters1 = usedletters2  then begin result := true; exit; end;

end;



Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TForm1.Button3Click(Sender: TObject);
begin
  if intCompare('Morrisette''moriset'then showmessage('same word');
  if intCompare('ärzte''Aerzte'then showmessage('same word');
end;


GTA-Place - Di 19.07.05 19:32

Hey, coole Funktion! Sogar "mmoorriisseettee" wird erkannt.


retnyg - Di 19.07.05 20:22

user profile iconSpaceguide hat folgendes geschrieben:
Guck dir mal den Algorithmus für den minimalen Editierabstand an. Damit kannst du die Ähnlichkeit zweier Zeichenketten berechnen.
wo gibts den ?


Gausi - Di 19.07.05 20:37

Einer der wiki-Links von mir weiter oben zeigt den, wenn auch nicht direkt unter dem Namen. Den bearbeite ich gerade, und passe den ggf. weiter an...


retnyg - Di 19.07.05 20:42

ist dir mein code nicht gut genug :mrgreen:
kannst du mir mal den genauen link posten pls ?


Gausi - Di 19.07.05 21:02

im wesentlichen das hier [http://de.wikipedia.org/wiki/Needleman-Wunsch-Algorithmus].

zu deinem Code: erste Versuche haben Fehler und Fehlverhalten ergeben. Mit Leerzeichen kommt der z.B. nicht wirklich klar. Und richtig verstanden hab ich den auf Anhieb auch nicht. Zuviel mit Zeigern drin ;-) Und dann kam dieser Editier-Algo dazwischen...Wenn das ne Sackgasse wird, komme ich aber auf dich zurück!


matze - Mi 20.07.05 09:06

soweit ich das mitbekommen habe, suchst du quasi den SoundEx Algorithmus. den kann delphi (zumindest ab version 7) aber schon selber.


Gausi - Mi 20.07.05 10:17

Aarrrggghhh.. :motz: :autsch:

Da probiert man ellenlang rum, und dann gibts da ne fertige Funktion? Tatsächlich liefert das Teil ganz vernünftige Ergebnisse. Zu REM findet er z.B. Rem, R.E.M., das Album 'Reim' von Matthias Reim und Titel wie 'Rain'... ein bissel Feinjustierung tut da wohl noch gut.

Trotzdem hat mich jetzt der Eifer gepackt, und ich versuch das nochmal mit der Editierdistanz. Ganz zufrieden bin ich mit SoundEx noch nicht...ich bekomm das mit der Länge der Strings nicht richtig hin...bei Suche nach "Hosen" spuckt er nix aus - der stört sich wohl an dem "die Toten", was häufig davor steht. Und um das mit pos zu verbinden, muss ich die phonetische Repräsentation passend kürzen, aber da hab ich noch nix zu gefunden :?


matze - Mi 20.07.05 10:26

user profile iconGausi hat folgendes geschrieben:
Aarrrggghhh.. :motz: :autsch:
Da probiert man ellenlang rum, und dann gibts da ne fertige Funktion?

Sorry. Ich wollte dir nicht den Spaß vermiesen :wink:

Du könntest aber ja den titel anhand der Leerzeichen in einezelne Wörter aufsplitten und dann diese einzel wörte "Soundexen".


Gausi - Mi 20.07.05 14:11

Keine Angst... den Spass lass ich mir durch sowas nicht verderben. :wink:
Hab das mit der Editierdistanz mal probiert, und für meine Zwecke modifiziert:

Delphi-Quelltext
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))
    // Umlaute würden ja sonst doppelt bestraft: einmal für a-ä und dann für "e nach nix"
    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;
{ Diese Funktion berechnet eine modifizierte Editierdistanz,
  bzw. bricht die Berechnung ab, wenn die Editierdistanz größer
  wird als ein vorgegebener Wert}

var A: array of array of integer;
  i, j, min: integer;
  ls1,ls2:integer;
begin
{ s1: Der String, der gesucht wird
  s2: Der String, in dem gesucht wird
  Array A, was zur Berechnung der Distanz verwendet wird:

      String_in_dem_gesucht_wird
    s 01...                    |
    t  0                       |
    r   0...                   |
    1 .........................X <-Ergebnis }


  ls1 := length(s1);
  ls2 := length(s2);
  setlength(A,ls1+1);
  for i:=0 to length(A)-1 do
  setlength(A[i],ls2+1);

{ Ziel: Der zu suchende String soll in dem anderen möglichst gut enthalten sein
  Daher: Nullte Zeile des Arrays mit 0 initialisieren}

  for j := 0 to ls2 do
    A[0,j] := 0;

{ Änderungen am Suchstring sollen aber bestraft werden
  Daher: Nullte Spalte mit Werten füllen, die u.a. vom char an der betreffenden
        Stelle abhängen }

  for i := 1 to ls1 do
    A[i,0] := A[i-1,0] + costGap(s1[i]);

{ Bedeutung des Parameters 'tolerance':
  Die Editierdistanz kann von oben nach unten nur immer weiter zunehmen
  Wenn man das Minimum der Werte einer Zeile schon größer als die Toleranz ist,
  dann kann abgebrochen werden.}

  min := tolerance + 1;
  for i := 1 to ls1-1 do // Zeilenweise arbeiten
  begin
    min := tolerance + 1;
    for j := 1 to ls2 do // jede Zelle der Zeile durchgehen, davon gibt es |s2| viele
    begin
      A[i,j] := min3(
        A[i-1,j] + costGap2(s1[i]),  // insertion von s1[i] in s2, bzw. löschen in s1
        A[i,j-1] + costGap(s2[j]) ,  // löschen in s2, bzw. insertion in s1, in der letzten Zeil soll das ok sein
        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;
{ Die letzte Zeile verdient besondere Aufmerksamkeit:
  Ein löschen von Buchstaben aus dem s2-String soll hier nicht mehr bestraft werden
  -> finde "die toten" in "die toten hosen"}

  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.


StefanH - 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;


Gausi - 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 ;-)


matze - 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...


Gausi - 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 ;-)


paddes19 - 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 - 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

http://delphi.helli.de/Tips___Tricks/Unsharp_compare/unsharp_compare.html

K.A. Ob das was für dich ist.


paddes19 - Di 13.09.05 09:59

Super, danke für die schnelle Antwort. :D
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 - 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.


paddes19 - 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 - 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 - Mi 14.09.05 07:30

Vielen Dank für die Antwort, :D , da kann ich jetzt eine
Weile rumprobieren.Mal schauen ob das klappt.