Mam dość skomplikowany problem wyszukiwania, który udało mi się zredukować do następującego opisu. Googlowałem, ale nie byłem w stanie znaleźć algorytmu, który wydaje się idealnie pasować do mojego problemu. W szczególności potrzeba pominięcia dowolnych liczb całkowitych. Może ktoś tutaj może mi coś wskazać?
Weźmy na przykład ciąg liczb całkowitych A (1 2 3 4)
Weź różne sekwencje liczb całkowitych i sprawdź, czy którakolwiek z nich pasuje do A w taki sposób.
- A zawiera wszystkie liczby całkowite w testowanej sekwencji
- Kolejność liczb całkowitych w badanej sekwencji jest taka sama w A
- Nie dbamy o żadne liczby całkowite w A, które nie znajdują się w sekwencji testowej
- Chcemy wszystkich pasujących sekwencji testowych, nie tylko pierwszej.
Przykład
A = (1 2 3 4)
B = (1 3)
C = (1 3 4)
D = (3 1)
E = (1 2 5)
B pasuje do A
C pasuje do A
D nie pasuje do A, ponieważ kolejność jest inna
E nie pasuje do A, ponieważ zawiera liczbę całkowitą spoza A
Mam nadzieję, że to wyjaśnienie jest wystarczająco jasne. Najlepsze, co udało mi się zrobić, to skonstruowanie drzewa sekwencji testowych i wykonanie iteracji nad A. Konieczność pominięcia liczb całkowitych prowadzi do wielu nieudanych ścieżek wyszukiwania.
Dzięki
Czytając kilka sugestii, uważam, że muszę wyjaśnić kilka kwestii, które pozostawiłem zbyt niejasne.
Powtarzane liczby są dozwolone, w rzeczywistości jest to dość ważne, ponieważ pozwala na dopasowanie jednej sekwencji testowej A na wiele sposobów
A = (1234356), B = (236), dopasowania mogą wynosić -23 --- 6 lub -2--3-6
Oczekuję, że będzie bardzo duża liczba sekwencji testowych, przynajmniej w tysiącach, a sekwencja A będzie miała zwykle maksymalną długość może 20. 20. Po prostu próba dopasowania każdej sekwencji testowej jeden po drugim przez iterację staje się niezwykle nieefektywna.
Przepraszam, jeśli to nie było jasne.
źródło
Odpowiedzi:
Hmm, mogę wymyślić dwa możliwe algorytmy: liniowy skan sekwencji A lub zbudowanie słownika z ciągłym wyszukiwaniem indeksów.
Jeśli testujesz wiele potencjalnych podsekwencji B względem pojedynczej większej sekwencji A , sugerowałbym użycie wariantu ze słownikiem.
Skan liniowy
Opis
Utrzymujemy kursor dla sekwencji A . Następnie iterację wszystkich elementów w podciąg B . Dla każdego elementu przesuwamy kursor do przodu w A, aż znajdziemy pasujący element. Jeśli nie znaleziono pasującego elementu, B nie jest podsekwencją.
To zawsze działa w O (rozmiar sekw . ) .
Pseudo kod
Tryb rozkazujący:
Funkcjonalny styl:
Przykładowa implementacja (Perl):
Wyszukiwanie w słowniku
Opis
Mapujemy elementy sekwencji A na ich indeksy. Następnie wyszukujemy odpowiednie indeksy dla każdego elementu w B , pomijamy te, które są zbyt małe, i wybieramy najmniejszy możliwy indeks jako dolny limit. Gdy nie znaleziono żadnych wskaźników, B nie jest podsekwencją.
Działa w coś takiego jak O (rozmiar podsekcji · k) , gdzie k opisuje liczbę zduplikowanych liczb
seq
. Plus narzut O (rozmiar sek.)Zaletą tego rozwiązania jest to, że decyzja negatywna może być podjęta znacznie szybciej (aż do stałego czasu), po zapłaceniu kosztów ogólnych związanych z budowaniem tabeli odnośników.
Pseudo kod:
Tryb rozkazujący:
Funkcjonalny styl:
Przykładowa implementacja (Perl):
Wariant wyszukiwania w słowniku: Kodowanie jako skończona maszyna stanowa
Opis
Możemy jeszcze bardziej zmniejszyć złożoność algorytmiczną do O (rozmiar podsekw.), Jeśli wymienimy więcej pamięci. Zamiast mapować elementy na ich indeksy, tworzymy wykres, w którym każdy węzeł reprezentuje element w jego indeksie. Krawędzie pokazują możliwe przejścia, np. Sekwencja
a, b, a
będzie miała krawędziea@1 → b@2, a@1 → a@3, b@2 → a@3
. Ten wykres jest równoważny skończonej maszynie stanów.Podczas wyszukiwania utrzymujemy kursor, który początkowo jest pierwszym węzłem drzewa. Następnie spacer krawędzi dla każdego elementu w podmenu B . Jeśli taka krawędź nie istnieje, B nie jest listą podrzędną. Jeśli po wszystkich elementach kursor zawiera prawidłowy węzeł, wówczas B jest listą podrzędną.
Pseudo kod
Tryb rozkazujący:
Funkcjonalny styl:
Przykładowa implementacja (Perl):
źródło
study
działa i czy zastosowane algorytmy mogłyby mieć tutaj praktyczne zastosowanie?study
wcześniej zbudowałem tabele wyszukiwania znaków do pozycji, podobnie jak moje drugie rozwiązanie.Oto praktyczne podejście, które pozwala uniknąć „ciężkiej pracy” związanej z implementacją własnego algorytmu, a także pozwala „wymyślić koło”: użyj problemu z wyrażeniem regularnym .
Wystarczy umieścić wszystkie liczby A w ciągu, a wszystkie liczby B w ciągu oddzielone wyrażeniem regularnym
(.*)
. Dodaj^
postać na początku i$
na końcu. Następnie pozwól swojej ulubionej wyszukiwarce wyrażeń regularnych wyszukać wszystkie dopasowania. Na przykład kiedyA = (1234356), B = (236)
utwórz reg exp dla B jak
^(.*)2(.*)3(.*)6(.*)$
. Teraz uruchom globalne wyszukiwanie wyrażeń regularnych. Aby dowiedzieć się, w których pozycjach w A twoja podsekwencja pasuje, po prostu sprawdź długość pierwszych 3 podtekstów.Jeśli zakres liczb całkowitych zawiera się w przedziale od 0 do 9, możesz najpierw rozważyć kodowanie pojedynczymi literami, aby to zadziałało, lub musisz dostosować pomysł za pomocą znaku separacji.
Oczywiście, szybkość tego podejścia będzie w dużym stopniu zależeć od prędkości używanego silnika reg exp, ale dostępne są wysoce zoptymalizowane silniki i myślę, że ciężko będzie wdrożyć szybszy algorytm „po wyjęciu z pudełka” .
źródło
Ten algorytm powinien być dość wydajny, jeśli uzyskanie długości i iteracja sekwencji jest wydajna.
sequence
i krócej wsubsequence
sequence
.sequence
równa liczbie w bieżącej pozycjisubsequence
sequence
jedną pozycję dalejsubsequence
na końcusequence
źródło