Ein String im String suchen.

Algorithmen, Sprachunabhängige Diskussionen zu Konzepten, Programmiersprachen-Design
Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8859
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Ein String im String suchen.

Beitrag von Xin » Di Sep 27, 2011 10:10 am

Auch wenn ihr alle fleissig an den Tutorials arbeitet, so werkelt dieser Thread in meinem Hinterkopf.

Für unter anderem diesen Thread habe ich gestern - nachdem einer leicht peinlichen Datensicherheitspanne - sehr viele Klassen, die ich niemals für nötig erachtet hätte, in mein Repository eingecheckt.
So besitze ich jetzt z.B. XSD::Primitive::UInt, eine Klasse, die nicht anderes macht, als ein UInt zu wrappen und mit einem XSD::Type::String Parser und einer ToString()-Methode auszustatten, damit ich UInts problemlos per Template auf die Platte schreiben und wieder lesen kann.

Und damit die ganze Mühe nicht umsonst ist (ist sie sowieso nicht. :-), verdiene ich mir hiermit mal ein:

Push :-)
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Ein String im String suchen.

Beitrag von cloidnerux » Di Sep 27, 2011 10:26 am

Mhmm, hab auch immer mal wieder hieran gedacht und daran, das ich bisher noch nix gemacht habe^^
Hab im moment so viel anderes, was ich tun will und muss, was meistens nichts mit Programmieren zu tun hat.
Zudem habe ich mir auch noch vorgenommen, mindestens 5 Artikel zu schreiben^^
Mal sehen ob ich nachher Zeit und Kaffee finde^^.
Redundanz macht wiederholen unnötig.
quod erat expectandum

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8859
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Ein String im String suchen.

Beitrag von Xin » Di Sep 27, 2011 10:38 am

cloidnerux hat geschrieben:Mhmm, hab auch immer mal wieder hieran gedacht und daran, das ich bisher noch nix gemacht habe^^
Hab im moment so viel anderes, was ich tun will und muss, was meistens nichts mit Programmieren zu tun hat.
Ich mache auch zu viele Sachen parallel. Dann geht alles nur sehr langsam vorwärts, aber irgendwann gibt's dann den Clash, wo aufeinmal 5 Dinge gleichzeitig fertig werden, ich auf einmal nicht mehr mehr viel zu tun habe und ich den gleichen Fehler mache wie immer.... dank der vielen Zeit fange ich 5 neue Dinge an...

Am 1. Juni 2008 begann für mich zwei Monate Urlaub... am 5. Juni registrierte ich die Domain 'proggen.org'... ;-)
cloidnerux hat geschrieben:Zudem habe ich mir auch noch vorgenommen, mindestens 5 Artikel zu schreiben^^
Mal sehen ob ich nachher Zeit und Kaffee finde^^.
*daumendrück* ;-)
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

nufan
Wiki-Moderator
Beiträge: 2558
Registriert: Sa Jul 05, 2008 3:21 pm

Re: Ein String im String suchen.

Beitrag von nufan » Fr Okt 19, 2012 1:21 am

*ausgrab* ^^

Hab mich eben wieder mit dem Thema beschäftigt, weil ich schon ewig eine Idee im Kopf hatte. Und zwar verwende ich für meine Suche zeichenweises, rekursives Hashing.
Für die Implementierung verwende ich C++ ohne irgendwelche Zusatzbibliotheken. Kompiliert wird das ganze also einfach mit:

Code: Alles auswählen

$ g++ -o find *.cpp -Wall -O2
Hier eine Proof-of-Concept-Implementierung, die bestimmt noch optimierungsfähig ist:

Code: Alles auswählen

// main.cpp
#include <iostream>
#include <vector>

#include "HashData.h"

int main( int argc, char *argv[] )
{
  if( argc < 3 )
  {
    std::cout << "./find [file] [text]" << std::endl;
    return 1;
  }

  HashData data( argv[1] );
  std::vector<int> results = data.find( argv[2] );
  for( unsigned int i = 0; i < results.size(); i++ )
    std::cout << ( results[i] + 1 ) << ": " << data.line( results[i] ) << std::endl;
  
  return 0;
}

Code: Alles auswählen

// HashData.h
#ifndef HASHDATA_H
#define HASHDATA_H

#include <map>
#include <string>

#include "LetterEntry.h"

class HashData
{

  public:
    HashData( const char *const path );
    std::vector<int> find( const char *const searchItem );
    std::string line( const int index );

  protected:
    void organizeData();

  private:
    void loadFile( const char *const path );
    std::vector<std::string> m_lines;
    std::map<char, LetterEntry> m_data;

};

#endif

Code: Alles auswählen

// HashData.cpp
#include "HashData.h"

#include <fstream>

HashData::HashData( const char *const path )
{
  loadFile( path );
  organizeData();
}


std::vector<int> HashData::find( const char *const searchItem )
{
  return m_data[searchItem[0]].find( searchItem );
}


void HashData::organizeData()
{
  std::vector<std::string>::iterator it;
  int i = 0;
  for( it = m_lines.begin(); it != m_lines.end(); it++, i++ )
  {
      for( unsigned int j = 0; j < it->length(); j++ )
      {
        if( j == 0 || it->at( j - 1 ) == ' ' )
          m_data[it->at( j )].add( it->c_str(), i );
      }
  }
}


std::string HashData::line( const int index )
{
  return m_lines[index];
}


void HashData::loadFile( const char *const path )
{
  std::ifstream is( path );
  int c;

  m_lines.push_back( std::string() );
  while( is.good() )
  {
    c = is.get();
    if( c == '\n' )
      m_lines.push_back( std::string() );
    else
      m_lines.back().push_back( c );
  }

  is.close();
}

Code: Alles auswählen

// LetterEntry.h
#ifndef LETTERENTRY_H
#define LETTERENTRY_H

#include <map>
#include <vector>

class LetterEntry
{

  public:
    void add( const char *const text, const int lineIndex );
    std::vector<int> find( const char *const searchItem );

  private:
    std::map<char, LetterEntry> m_data;
    std::vector<int> m_lineIndexes;

};

#endif

Code: Alles auswählen

// LetterEntry.cpp
#include "LetterEntry.h"

void LetterEntry::add( const char *const text, const int lineIndex )
{
  m_lineIndexes.push_back( lineIndex );
  if( *( text + 1 ) != '\0' )
    m_data[text[1]].add( text + 1, lineIndex );
}


std::vector<int> LetterEntry::find( const char *const searchItem )
{
  std::vector<int> results( m_lineIndexes );

  if( m_data.count( searchItem[1] ) > 0 )
  {
    std::vector<int> prev = m_data[searchItem[1]].find( searchItem + 1 );
    results.insert( results.end(), prev.begin(), prev.end() );
  }

  return results;
}
Beispielaufruf mit der obigen Datei "main.cpp", wobei nach "argv" gesucht wird:

Code: Alles auswählen

$ ./find main.cpp argv
6: int main( int argc, char *argv[] )
8:   if( argc < 3 )
14:   HashData data( argv[1] );
15:   std::vector<int> results = data.find( argv[2] );
Das ganze geht natürlich auch mit Leerzeichen:

Code: Alles auswählen

$ ./find main.cpp "unsigned int"
16:   for( unsigned int i = 0; i < results.size(); i++ )
Als Ergebnis wird die Zeilennummer und der Text der Zeile ausgegeben, das Prinzip wäre aber leicht auch noch auf Zeichen innerhalb der Zeile verbesserbar. Das Löschen von Daten fehlt ebenfalls noch, sollte aber auch in linearem Aufwand möglich sein.

Vorteil: Linearer Suchaufwand (bezüglich des gesuchten Textes), unabhängig von der Länge des gesamten Textes
Nachteil: Hoher Speicherbedarf (ein vielfaches der Eingabe, abhängig von der Anzahl der Einheiten, in dieser Implementierung also Wörter); nicht-zusammenhängende Matches (z.B. "unsigned i" in "unsigned int i" zu finden) dürfte schwerer werden

Bei vielen Ergebnissen wird das zurückgeben der Vektoren in LetterEntry::find() aufwendig... kann ich das noch optimieren? Eigentlich bräuchte ich da ja nur eine flache Kopie, da ich die sowieso in einem neuen Vektor zusammenhänge. Kann ich das außer mit Zeiger auf private Objekte noch anders (eleganter) lösen?

Hab ich damit eigentlich das Rad neu erfunden? ^^ Gibts diesen Algorithmus schon unter einem bestimmten Namen? ^^

Für Fragen bzw. Optimierungs- und Implementierungs-Vorschläge bin ich natürlich offen :)

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8859
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Ein String im String suchen.

Beitrag von Xin » Fr Okt 19, 2012 1:26 pm

Wir haben einen Kandidaten, leider ohne passendes Interface. :-)

Interface: siehe hier.

Der Sommer geht, die Knobel-Zeit beginnt... wer möchte sich noch versuchen?
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

nufan
Wiki-Moderator
Beiträge: 2558
Registriert: Sa Jul 05, 2008 3:21 pm

Re: Ein String im String suchen.

Beitrag von nufan » So Okt 21, 2012 11:20 pm

Xin hat geschrieben:Wir haben einen Kandidaten, leider ohne passendes Interface. :-)

Interface: siehe hier.
Na gut, nochmal mit passendem Interface ^^

Code: Alles auswählen

// LetterEntry.h
#ifndef LETTERENTRY_H
#define LETTERENTRY_H

#include <map>
#include <vector>
#include <string>
#include <cctype>

class LetterEntry
{

  public:
    void add( const char *const text, const char *const id );
    const std::vector<std::string>& find( const char *const searchItem );

  private:
    std::map<char, LetterEntry> m_data;
    std::vector<std::string> m_ids;
    static std::vector<std::string> sm_empty;

};

#endif

Code: Alles auswählen

// LetterEntry.cpp
#include "LetterEntry.h"

std::vector<std::string> LetterEntry::sm_empty;

void LetterEntry::add( const char *const text, const char *const id )
{
  m_ids.push_back( id );
  if( *( text + 1 ) != '\0' && isprint( *( text + 1 ) ) )
    m_data[text[1]].add( text + 1, id );
}


const std::vector<std::string>& LetterEntry::find( const char *const searchItem )
{
  if( *( searchItem + 1 ) == '\0' )
    return m_ids;
  else if( m_data.count( searchItem[1] ) > 0 )
    return m_data[searchItem[1]].find( searchItem + 1 );
  else
    return sm_empty;
}

Code: Alles auswählen

// HashSearch.h
#ifndef HASHSEARCH_H
#define HASHSEARCH_H

#include "SearchBase.h"
#include "LetterEntry.h"

#include <vector>
#include <string>
#include <map>

class HashSearch : public SearchBase
{
  
  public:
    HashSearch();
    void addText( char const *id, char const *text );
    void addPattern( char const *pattern );
    void clearPatterns();
    int seek( char const *filename );
    
  private:
    std::vector<std::string> m_patterns;
    std::vector<std::string> m_ids;
    std::map<char, LetterEntry> m_data;
    int m_numIds;
  
};

#endif

Code: Alles auswählen

// HashSearch.cpp
#include "HashSearch.h"

#include <cstdio>

HashSearch::HashSearch()
{
  m_numIds = 0;
}


void HashSearch::addText( char const *id, char const *text )
{
  for( int i = 0; text[i] != '\0'; ++i )
  {
    if( i == 0 || isspace( text[i - 1] ) )
      m_data[*( text + i )].add( text + i, id );
  }
}


void HashSearch::addPattern( char const *pattern )
{
  m_numIds++;
  m_patterns.push_back( pattern );
}


void HashSearch::clearPatterns()
{
  m_numIds = 0;
  m_patterns.clear();
}


int HashSearch::seek( char const *filename )
{
  std::map<std::string, int> idCount;
  for( std::vector<std::string>::iterator stringIt = m_patterns.begin(); stringIt != m_patterns.end(); ++stringIt )
  {
    std::vector<std::string> results( m_data[stringIt->at( 0 )].find( stringIt->c_str() ) );
    for( std::vector<std::string>::iterator resultIt = results.begin(); resultIt != results.end(); ++resultIt )
      idCount[*resultIt]++;
  }
  FILE *f = fopen( filename, "w" );
  if( f == NULL )
  {
    fprintf( stdout, "Error creating file\n" );
    return -1;
  }
  for( std::map<std::string, int>::iterator mapIt = idCount.begin(); mapIt != idCount.end(); ++mapIt )
  {
    if( mapIt->second == m_numIds )
      fprintf( f, "%s\n", mapIt->first.c_str() );
  }
  fclose( f );
  return idCount.size();
}

Code: Alles auswählen

// SearchBase.h
#ifndef SEARCHBASE_H
#define SEARCHBASE_H

class SearchBase
{
  
  public:
    virtual void addText( char const *id, char const *text ) = 0;
    virtual void addPattern( char const *pattern ) = 0;
    virtual void clearPatterns() = 0;
    virtual int seek( char const *filename ) = 0;

};

#endif
Das main fehlt noch, sonst sollte alles passen.
Ich hab die anderen Codes noch nicht angesehen und bin nicht sicher, ob mein doch relativ aufwendiges Hashing damit mithalten kann. Jedenfalls denke ich, dass meine Chancen mit der Länge der Texte und vor allem der Anzahl der Suchvorgänge steigen.
Eventuell reiche ich noch eine zweite Lösung ein ^^

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8859
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Ein String im String suchen.

Beitrag von Xin » Di Okt 23, 2012 9:14 am

Die Main brauche ich eigentlich auch gar nicht. ^^

Grundsätzilch finde ich es gut, den Quelltext lesen zu können, aber könntest Du bei der nächsten Version zusätzlich ein ZIP-Archiv dranhängen? ^^
Ich werd's morgen mal an die Suchmaschine hängen, meine konnte ich wieder kompilieren, aber hab wohl irgendwas kaputt gemacht. :-(
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: Ein String im String suchen.

Beitrag von fat-lobyte » Di Okt 23, 2012 7:38 pm

Ich hab da grad was in Petto, das eventuell interessant sein könnte. Please stand by.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: Ein String im String suchen.

Beitrag von fat-lobyte » Do Okt 25, 2012 4:09 pm

So, hab jetzt auch mal was zum einreichen.

Gleichmal vornweg: die Grundidee für diesen Algorithmus ist *nicht* von mir.

Unser Bioinformatik-Prof hat uns gezwungen, die wichtigsten Papers zur identifikation homologien in Proteinsequenzen zu lesen. Er meinte zwar "Algorithmen braucht ihr nicht zu lernen", allerdings war ich ein bisschen Neugierig.

Die Grundidee habe ich von einem gewissen Herrn Lipman geklaut: http://www.ncbi.nlm.nih.gov/pubmed/2983426

Der Algorithmus braucht ziemlich viel permanenten Speicher, und zwar ein wenig mehr als textlänge*sizeof(char*). Dafür geht der Look-Up einigermaßen schnell.

Bitte kompilieren mit GCC >= 4.7 oder Clang >= 3.1.
Benötigte flags:

Code: Alles auswählen

-std=c++0x
Vorgeschlagene Optimierungsflags:

Code: Alles auswählen

-O3 -DNDBEBUG -msse4.2 -fno-rtti -fno-exceptions
Natürlich gibts wieder ein Git-Repository dazu: https://github.com/fat-lobyte/proggen_searchwords

ps.: Mit nicht allzuviel Aufwand kann man den Algorithmus so verändern, dass er auch nicht-perfekte Matches findet.
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8859
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Ein String im String suchen.

Beitrag von Xin » Fr Okt 26, 2012 9:52 am

Ich will zusehen, dass ich eure Lösungen heute abend mal in mein Projekt einbinden kann und die mal gegeneinander ausspielen lasse.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Antworten