Najszybszym sposobem na znalezienie przedmiotów z kolekcji dopasowane stan na członka smyczkową

głosy
1

Załóżmy, że mam kolekcję (może to być tablica, lista rodzajowe, czy cokolwiek jest najszybszą rozwiązanie tego problemu) z pewnej klasy, nazwijmy to ClassFoo:

class ClassFoo
{
    public string word;
    public float score;
    //... etc ...
} 

Załóżmy, że będzie jak 50.000 elementów w kolekcji, wszystko w pamięci. Teraz chcę uzyskać jak najszybciej wszystkich przypadkach w kolekcji, które przestrzegają warunku na jej członka bar, na przykład tak:

List<ClassFoo> result = new List<ClassFoo>();
foreach (ClassFoo cf in collection)
{
    if (cf.word.StartsWith(query) || cf.word.EndsWith(query))
        result.Add(cf);
}

Jak mogę uzyskać wyniki tak szybko, jak to możliwe? Należy wziąć pod uwagę kilka zaawansowanych technik indeksowania i datastructures?

Domena zgłoszenie tego problemu jest autocompleter, że dostaje zapytania i daje zbiór sugestii wyniku. Zakładamy, że warunek ten nie ma nic bardziej skomplikowane niż to. Zakładamy również, że nie będzie dużo wyszukiwań.

Utwórz 18/09/2008 o 22:45
źródło użytkownik
W innych językach...                            


9 odpowiedzi

głosy
2

Z ograniczeniem, że klauzula warunek może być „coś”, to jesteś ograniczony do skanowania całą listę i stosując warunek.

Jeśli istnieją ograniczenia dotyczące klauzuli stan, wtedy można spojrzeć na organizowanie danych do bardziej efektywnie obsługiwać zapytań.

Na przykład, przykładowy kod z „byFirstLetter” słowniku nie pomaga w ogóle z „endswith” zapytania.

Więc tak naprawdę sprowadza się do tego, co pyta chcesz zrobić przed tymi danymi.

W bazach danych, problem ten jest ciężar „optymalizator zapytań”. W typowej bazy danych, jeśli masz bazę danych bez indeksów, oczywiście każde zapytanie będzie skan tabeli. Jak dodać indeksów w tabeli, optymalizator może użyć tych danych do bardziej wyrafinowanych zapytań planuje lepiej dostać się do danych. To w istocie problem, który opisujesz.

Gdy masz bardziej konkretny podzbiór typów kwerend następnie można zrobić lepszą decyzję, co struktura jest najlepsza. Ponadto, należy wziąć pod uwagę ilość danych. Jeśli masz listę 10 elementów każdy mniej niż 100 bajtów, skan wszystko może okazać się najszybciej, co można zrobić, ponieważ masz taką małą ilość danych. Oczywiście, że nie skaluje się do elementów 1M, ale nawet sprytnych technik dostępu nosić koszt w instalacji, konserwacji (jak utrzymanie indeksu) i pamięć.

EDIT , na podstawie komentarza

Jeśli jest to auto Nieukończenie, jeśli dane są statyczne, a następnie posortować je i używać przeszukiwanie binarne. Nie jesteś naprawdę dzieje się szybciej niż to.

Jeśli dane są dynamiczne, a następnie zapisać go w zrównoważonym drzewie, i szukać tego. To skutecznie wyszukiwanie binarne, a to pozwala zachować dodać dane losowo.

Coś jeszcze jest jakaś specjalność na tych pojęć.

Odpowiedział 18/09/2008 o 23:13
źródło użytkownik

głosy
1

var odpowiedzi = myList.Where (pozycja => item.bar.StartsWith (zapytanie) || item.bar.EndsWith (zapytanie));

To najprostszy moim zdaniem, powinna wykonać dość szybko.

Odpowiedział 18/09/2008 o 22:48
źródło użytkownik

głosy
0

Jeśli zestaw możliwych kryteriów jest stała i mała, można przypisać maskę bitową dla każdego elementu listy. Wielkość maskę bitową jest wielkość zbioru kryteriów. Po utworzeniu elementu / dodać go do listy, sprawdź jakie kryteria spełnia, a następnie ustawić odpowiednie bity w maskę bitową tego elementu. Dopasowując elementy z listy będzie tak proste, jak dopasowanie ich bitmasks z maskę bitową docelowego. Bardziej ogólne metody jest filtr Bloom.

Odpowiedział 19/09/2008 o 23:06
źródło użytkownik

głosy
0

Zależy. Wszystkie obiekty są zawsze będzie załadowany do pamięci? Czy masz skończony limit obiektów, które mogą być załadowane? Twoje zapytania będą musiały rozważyć obiektów, które nie zostały jeszcze załadowane?

Jeśli kolekcja będzie duża, na pewno będę korzystać z indeksu.

W rzeczywistości, jeśli kolekcja może wzrosnąć do dowolnego rozmiaru i nie jesteś pewny, że będziesz w stanie zmieścić to wszystko w pamięci, że patrzę w ORM, bazy danych w pamięci lub innej wbudowanej bazy danych. XPO z DevExpress dla ORM lub SQLite.Net do bazy danych w pamięci przychodzi do głowy.

Jeśli nie chcesz iść tak daleko, zrobić prosty indeks składający się z „bar” referencje członków odwzorowywania odniesień klasowych.

Odpowiedział 18/09/2008 o 23:21
źródło użytkownik

głosy
0

Można tworzyć jakąś indeksu i może dostać się szybciej.

Możemy zbudować indeks takiego:

Dictionary<char, List<ClassFoo>> indexByFirstLetter;
foreach (var cf in collection) {
  indexByFirstLetter[cf.bar[0]] = indexByFirstLetter[cf.bar[0]] ?? new List<ClassFoo>();
  indexByFirstLetter[cf.bar[0]].Add(cf);
  indexByFirstLetter[cf.bar[cf.bar.length - 1]] = indexByFirstLetter[cf.bar[cf.bar.Length - 1]] ?? new List<ClassFoo>();
  indexByFirstLetter[cf.bar[cf.bar.Length - 1]].Add(cf);
}

A następnie użyć go w ten sposób:

foreach (ClasssFoo cf in indexByFirstLetter[query[0]]) {
  if (cf.bar.StartsWith(query) || cf.bar.EndsWith(query))
    result.Add(cf);
}

Teraz być może nie mają pętli jak wielu ClassFoo jak w przykładzie, ale potem znowu musimy utrzymać indeks aktualne. Nie ma żadnej gwarancji, że jest szybciej, ale jest zdecydowanie bardziej skomplikowana.

Odpowiedział 18/09/2008 o 23:01
źródło użytkownik

głosy
0

Jeśli jest coś, gdzie można wypełnić listę raz, a potem zrobić wiele wyszukiwań (tysiące lub więcej), a następnie można utworzyć jakąś słowniku przeglądowej, która odwzorowuje rozpoczyna / kończy wartości do ich rzeczywistych wartości. To byłoby szybkie wyszukiwanie, ale zużywają znacznie więcej pamięci. Jeżeli nie robisz, że wiele wyszukiwań lub wiesz, że będzie zasiedlać listę co najmniej pół-często pójdę z kwerendy LINQ, że CQ sugerowane.

Odpowiedział 18/09/2008 o 22:59
źródło użytkownik

głosy
0

Dla konkretnego przykładu, sortowania kolekcji pomogłoby jak można binarychop do pierwszego elementu, który rozpoczyna się od zapytania i zakończyć na początku, kiedy dojdziesz do następnego, że nie; można również produkować tablicę wskaźników do elementów kolekcji posortowane według odwrocie każdej struny dla drugiego punktu.

W ogóle, jeśli znasz strukturę zapytania z góry, można uporządkować swoją kolekcję (lub zbudować kilka posortowanych indeksów dla swojej kolekcji, jeśli istnieje wiele klauzul) odpowiednio; jeśli nie, to nie będzie w stanie to zrobić lepiej niż przeszukiwanie liniowe.

Odpowiedział 18/09/2008 o 22:56
źródło użytkownik

głosy
0

Nie jestem na moim Java teraz, ale myślę o następujących rzeczy.

Jak tworzysz swoją listę? Być może uda się stworzyć już zamawiać w sposób, który skraca czas porównania.

Jeśli tylko robi prostą pętlę dzięki swojej kolekcji, nie będzie widać dużej różnicy między przechowywanie go jako tablica lub jako połączonej listy.

Do przechowywania wyników, w zależności od sposobu ich zbierania, struktura mogłaby coś zmienić (ale Konstrukcje generyczne zakładając Java są inteligentne, nie będzie). Jak powiedziałem, nie jestem na moim Java, ale zakładam, że lista rodzajowe związane utrzyma wskaźnik ogon. W tym przypadku, nie byłoby naprawdę coś zmienić. Ktoś z większą wiedzą bazowego tablicy vs połączonego realizacji liście i jak kończy się patrząc w kod bajtowy prawdopodobnie mógłby powiedzieć, czy dołączenie do listy połączonej ze wskaźnikiem ogona lub włożeniem do tablicy jest szybsze (moje przypuszczenie byłoby tablica ). Z drugiej strony, trzeba byłoby znać rozmiar swojego zbioru wynikowego lub poświęcić trochę miejsca i sprawiają, że tak duży jak w całej kolekcji jesteś iteracja gdybyś chciał wykorzystać tablicę.

Optymalizacja zapytanie do porównywania przez zastanawianie się, jakie porównanie jest najbardziej prawdopodobne, aby mogło być prawdziwe i robi, że najpierw może również pomóc. czyli: jeśli w ogóle 10% czasu członkiem kolekcji rozpoczyna się od zapytania, a 30% czasu członkiem kończy się zapytaniem, co chcesz zrobić w pierwszej kolejności porównania końcowy.

Odpowiedział 18/09/2008 o 22:56
źródło użytkownik

głosy
0

Nie bardzo rozumiem ... Wszystko, co można zrobić, to naprawdę optymalizacji regułę, że jest to część, która musi być najszybszy. Nie można przyspieszyć pętlę bez prostu rzuca więcej sprzętu na niego.

Można parallelize jeśli masz wiele rdzeni lub maszyn.

Odpowiedział 18/09/2008 o 22:49
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more