OK, więc nie brzmię jak idiota, mam zamiar określić problem / wymagania dokładniej:
- Igła (wzór) i stóg siana (tekst do wyszukania) to ciągi zakończone znakiem C w stylu C. Brak informacji o długości; w razie potrzeby należy ją obliczyć.
- Funkcja powinna zwrócić wskaźnik do pierwszego dopasowania lub
NULL
jeśli nie zostanie znalezione żadne dopasowanie. - Przypadki niepowodzenia są niedozwolone. Oznacza to, że każdy algorytm z niestałymi (lub dużymi stałymi) wymaganiami dotyczącymi pamięci masowej będzie musiał mieć przypadek rezerwowy dla błędu alokacji (a wydajność w opiece rezerwowej przyczynia się w ten sposób do wydajności w najgorszym przypadku).
- Implementacja ma być w C, chociaż dobry opis algorytmu (lub link do takiego algorytmu) bez kodu też jest w porządku.
... a także to, co mam na myśli przez „najszybszy”:
- Deterministyczne
O(n)
gdzien
= długość stogu siana. (Ale może być możliwe użycie pomysłów z algorytmów, które są normalnieO(nm)
(na przykład toczący się hash), jeśli są połączone z bardziej niezawodnym algorytmem, aby dać deterministyczneO(n)
wyniki). - Nigdy nie działa (wymiernie; kilka zegarów
if (!needle[1])
itp. Jest w porządku) gorzej niż naiwny algorytm brutalnej siły, szczególnie na bardzo krótkich igłach, które są prawdopodobnie najczęstszym przypadkiem. (Bezwarunkowe, duże obciążenie wstępne przetwarzania jest złe, podobnie jak próba poprawy współczynnika liniowego dla patologicznych igieł kosztem prawdopodobnych igieł). - Biorąc pod uwagę dowolną igłę i stóg siana, porównywalna lub lepsza wydajność (nie gorsza niż 50% dłuższy czas wyszukiwania) w porównaniu z jakimkolwiek innym szeroko stosowanym algorytmem.
- Poza tymi warunkami zostawiam definicję „najszybszego” otwartego. Dobra odpowiedź powinna wyjaśniać, dlaczego uważasz proponowane podejście za „najszybsze”.
Moja obecna implementacja działa mniej więcej od 10% wolniej do 8 razy szybciej (w zależności od danych wejściowych) niż implementacja dwukierunkowa glibc.
Aktualizacja: mój aktualny optymalny algorytm jest następujący:
- W przypadku igieł o długości 1 użyj
strchr
. - W przypadku igieł o długości 2-4 użyj słów maszynowych, aby porównać 2-4 bajty naraz w następujący sposób: Wstępnie załaduj igłę 16- lub 32-bitową liczbą całkowitą z przesunięciami bitów i przełączaj stary bajt / nowe bajty ze stosu siana w każdej iteracji . Każdy bajt stogu siana jest odczytywany dokładnie raz i podlega sprawdzeniu względem 0 (koniec ciągu) i jednym 16- lub 32-bitowym porównaniem.
- W przypadku igieł o długości> 4 użyj algorytmu dwukierunkowego ze złą tabelą zmian (np. Boyer-Moore), która jest stosowana tylko do ostatniego bajtu okna. Aby uniknąć narzutu inicjalizacji tabeli 1kb, co byłoby stratą netto dla wielu igieł o średniej długości, zachowuję tablicę bitową (32 bajty) oznaczającą, które wpisy w tabeli przesunięć są inicjalizowane. Bity, które nie są ustawione, odpowiadają wartościom bajtów, które nigdy nie pojawiają się w igle, dla których możliwe jest przesunięcie o pełną długość igły.
Najważniejsze pytania, jakie mam w głowie, to:
- Czy istnieje sposób na lepsze wykorzystanie złego stołu zmiany biegów? Boyer-Moore najlepiej go wykorzystuje, skanując do tyłu (od prawej do lewej), ale dwukierunkowy wymaga skanowania od lewej do prawej.
- Jedyne dwa realne algorytmy kandydatów, które znalazłem dla ogólnego przypadku (brak warunków braku pamięci lub kwadratowych wydajności), to dwukierunkowe i dopasowywanie ciągów w uporządkowanych alfabetach . Ale czy istnieją łatwe do wykrycia przypadki, w których różne algorytmy byłyby optymalne? Z pewnością wiele z algorytmów kosmicznych
O(m)
(gdziem
jest długość igły) można by użyć dom<100
lub czegoś podobnego. Byłoby również możliwe użycie algorytmów, które są w najgorszym przypadku kwadratowe, jeśli istnieje łatwy test dla igieł, który, jak można udowodnić, wymaga tylko czasu liniowego.
Punkty bonusowe za:
- Czy możesz poprawić wydajność, zakładając, że zarówno igła, jak i stóg siana są dobrze uformowane w UTF-8? (Przy znakach o różnych długościach bajtów, dobrze uformowana struktura narzuca pewne wymagania dotyczące wyrównania łańcucha między igłą a stogiem siana i umożliwia automatyczne przesunięcie o 2-4 bajty w przypadku napotkania niedopasowania bajtu głowy. Ale czy te ograniczenia kupują wiele / cokolwiek poza tym, co maksymalne obliczenia sufiksów, dobre przesunięcia sufiksów itp. już dają Ci różne algorytmy?)
Uwaga: dobrze znam większość dostępnych algorytmów, ale nie wiem, jak dobrze działają w praktyce. Oto dobre odniesienie, aby ludzie nie podawali mi referencji na temat algorytmów jako komentarzy / odpowiedzi: http://www-igm.univ-mlv.fr/~lecroq/string/index.html
strstr
na później, więc tak naprawdę nie zabrałem się za prawidłowe przeczytanie artykułu, który łączysz, ale brzmi to bardzo obiecująco. Dziękuję i przepraszam, że nie oddzwoniłem.