Java najszybsze tworzenie i filtrowanie tablicy 2D

Java najszybsze tworzenie i filtrowanie tablicy 2D
AD
  • Rejestracja:około 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:7
0

Witam,

Mam pytanie z javy oraz poniekąd z algorytmiki. Mój problem polega na tym, że potrzebuję rady bardziej doświadczonych programistów Javy. Otóż głównym celem jest najszybsze możliwe tworzenie tablicy dwuwymiarowej a następnie filtrowanie jej wykorzystując kolumny i zwracać pasujące wiersze. Przykład

Załóżmy 1000 wierszy i 5 kolumn: A, B, C, D, E. Wartości to double. Jak najszybciej przefiltrować tablicę przy warunkach typu A > 4 AND B < 11 AND D = WIND.

Problem wydajności jest o tyle istotny, że taka musi być tworzona kilka setek/tysięcy razy przy założeniu, że wierszy jest zazwyczaj dużo (aktualnie największy problem ma nieco ponad 1000 wierszy, ale może się zdarzyć np. 5000) a kolumn całkiem mało (max 30-40). Jak to ugryźć najszybciej?

Rozwiązanie oczywiste to Double[rows][cols] - ale w jaki sposób filtrować taką tablicę, aby wyciągnąć całe wiersze do nowej tablicy? Dynamicznie zmniejszać przeszukiwaną tablicę (w sensie wyciągamy wszystkie, które pasują do A > 4, w kolejnej iteracji mamy już mniej elementów w nowej tablicy i szukamy B < 11)? Czy Double i pętle for to najlepsze rozwiązanie czy może jest jakiś myk na to?

Z góry dzięki za wszystkie pomysły!

Pozdrawiam,
Adam

adf88
  • Rejestracja:ponad 21 lat
  • Ostatnio:prawie 12 lat
0

Myk polega na odpowiednim dobraniu struktur i algorytmów do danego problemu. Ty podsuwasz jakieś tam rozwiązanie, ale my nawet problemu nie znamy.

AD
Problem przedstawiłem bardzo dokładnie. Nic więcej nie ma. Tylko filtrowanie wielu wierszy na podstawie pewnych wartości w kolumnach. Stąd moje pytanie było właśnie o strukturę... Średnio pomocna odpowiedź.
adf88
No chociaż napisz skąd i dokąd płyną dane. Czy wszystko idzie strumieniowo, czy składasz może składasz dane z kawałków, czy są to zawsze świeże dane czy może składujesz je... Jest wiele aspektów. Ty piszesz, że celem jest tablica dwuwymiarowa, później pytasz co zamiast tej tablicy. O co chodzi?
AD
Zerknij na ten post: Java najszybsze tworzenie i filtrowanie tablicy 2D może rozwiąże Twoje wątpliwości. Jakbyś miał jeszcze pytania to daj znać
LN
  • Rejestracja:około 16 lat
  • Ostatnio:11 miesięcy
  • Postów:1398
0

Tam, gdzie masz konkretną wartość to HashMapa i klucze. Generalnie musisz podać więcej szczegółów - przykładowych zapytań.

Poza tym - może załóż sobie indeksy jakieś ?

edytowany 1x, ostatnio: [losowa nazwa]
02
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 8 lat
  • Postów:1176
0

Po samym opisie mozna wnioskowac, ze potrzebujesz indeksow przyspieszajacego wyszukiwanie. Najprosciej - wez jakas baze danych, ktora oferuje tego typu zapytania (w zasadzie kazda) pozakladaj indeksy na kolumny, po ktorych chcesz wyszukiwac, cala prace baza robi za ciebie.
Jezeli koniecznie chcesz sam to implementowac to bedziesz potrzebowal dwoch rzeczy:

  • drzewa przyspieszajacego wyszukiwanie (najprosciej drzewko BST)
  • szybkich operacji na zbiorach (czyli czesc wspolna, sume, etc).
    Rozbijasz zapytanie na proste podzapytania operujace na pojedynczych kolumnach. Wyszkujesz uzywajac drzewa numery indeksow dla poszczegolnych kolumn. Potem zgodnie z zapytaniem robisz czesc wspolna lub sume na zbiorach indeksow, ktore masz z poprzedniego kroku.
    Oczywiscie taki schemat jest daleki od optymalnego ale raczej nie ludz sie, ze zaimplementujesz optymalnie samemu to co robia silniki baz danych.
AD
  • Rejestracja:około 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:7
0

Baza danych jest na wyrost. Po pierwsze takie wyszukania idą w setki / tysiące, po drugie nie ma sensu podciągać bazy pod prosty problem...

Ja już to mam zaimplementowane, tylko niestety jest to trochę za wolne. Cała idea jest mniej więcej taka (pseudokod):

for(e : examples) {
boolean _t = true;
for(cond : warunki){
if (cond != e.column(cond.col)){
_t = false;
break;
}
if(_t) add2list(e);
}

gdzie cond.col - to znany numer kolumny do sprawdzenia

Cały problem polega na tym, że o ile pętle idą dosyć szybko to wyciąganie e.column(cond.col) jest bardzo wolne. Stąd moje pytanie o strukturę, która wyciągnie to w najszybszy sposób, najlepiej O(1) jeśli się da.

02
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 8 lat
  • Postów:1176
2

Przyspieszyc moze ci stworzenie indeksow na wszystkich kolumnach. Indeksem moze byc drzewo BST, w ktorym kluczem sa double z tablicy, a wartosciami sa ich pozycje w tablicy. Wyszukanie w drzewie pojedynczego elementu zajmuje O(log n). Jak masz nierownosc to wyszukujesz poddrzewo a nastepnie dodajesz wszystkie elementy z poddrzewa. Dodatkowa optymalizacja to jest np. zaczynanie wyszukiwania od kolumny, w ktorej przewidujesz, ze zwroci najmniej wartosci - bo wtedy ograniczasz maksymalnie liczbe elementow, ktore musisz wyszukac w pozostalych drzewach (przewidywac mozesz roznymi heurystykami - np. mozesz zaczac od rownosci zamiast od nierownosci). Podczas przeszukiwania dodajesz pozycje do hash setu- w pierwszy zapytaniu dodajesz wszystkie, w kazdym kolejnym zapytaniu wyrzucasz z hash setu pozycje ktorych nie ma w pozostalych zapytaniach.

Pseudo kod moze wygladac tak:

Kopiuj
h[] = construct BST indexes on each column
m = empty hash set
while(queries is not empty)
{
   q = choose most optimal query and remove it from queries
   t = h[q.col]
   iter = iterator that will iterate on all element that satisfies query q in tree t

    mark all elements in m as not used
    while(iter.hasNext())
    {
      e = iter.next();
      if(q is first handled query)
      {
        mark e as used
        m.add(e) //add all elements if query is first handled query
      }
      else
      {
        if(e in m) {mark e as used;}
      }
    }
    remove all elements marked as not used from m
}
result = m

Ewentualnie zamiast drzew BST mozesz sobie posortowac kazda kolumne z osobna i wyszukiwac binarnie.

W O(1) raczej tego nie zrobisz - musialbys miec do czynienia z nieduzymi liczbami calkowitymi (wtedy moglbys zastosowac hash mape jako indeks) a nie z doublami.

edytowany 7x, ostatnio: 0x200x20
AD
  • Rejestracja:około 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:7
0

0x200x20 dzięki za pomysł, sprawdzę jak to będzie działało :)

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:3 minuty
0

Cały problem polega na tym, że o ile pętle idą dosyć szybko to wyciąganie e.column(cond.col) jest bardzo wolne.

A co się dzieje w tym e.column(cond.col)?

Na początek warto wiedzieć:

  • jak często zmieniają się dane w tabeli?
  • jaka jest złożoność zapytań, tzn co w nich jest?
  • czy zapytania się powtarzają, a jeśli tak to jak często?
  • jak dużo jest zapytań?

Jeśli chodzi o BST, to w Javie jest klasa TreeSet i zawiera metody subSet, headSet i tailSet, które wraz z odpowiednim Comparatorem mogą służyć jako metody do robienia zapytań. Po otrzymanym subSecie/ headSecie/ tailSecie najlepiej iterować za pomocą for-eacha (tak powinno być najszybciej).

Tablice w Javie dziedziczą po Object, ale nie nadpisują jego equals() ani hashCode(). Dzięki temu, jeśli zrobisz HashSet<Typ[]> to dostaniesz coś w rodzaju IdentityHashSet<Typ[]> i tutaj się to może przydać.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
AD
  • Rejestracja:około 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:7
0
Wibowit napisał(a):

Cały problem polega na tym, że o ile pętle idą dosyć szybko to wyciąganie e.column(cond.col) jest bardzo wolne.

A co się dzieje w tym e.column(cond.col)?

Dla mnie wygląda to jak geter ze struktury klasy, po przeanalizowaniu kolejnych odniesień jest to geter z klasy zwanej FastVector, która odwołuje się już bezpośrednio do klasy Object. Wywołanie to zwraca zatem element object[index] - index to ten cond.col.

Wibowit napisał(a):

Na początek warto wiedzieć:

  • jak często zmieniają się dane w tabeli?

To ma być tablica 2D, nie tabela. Tablica jest jednym z parametrów wywoływanej metody, gdzie wywołanie metody idzie w tysiące lub nawet setki tysięcy. Nie jest istotne, że to się liczy dajmy na to 4-5h. To nie problem natury "wynik na już", raczej coś co ma się przeliczyć w rozsądnym czasie (czyli max 24h) :) Tablica jako taka nie zmienia się przy tym za każdym wywołaniem metody. Zawsze zmienia się drugi parametr, którym są warunki do wyciągnięcia odpowiednich przykładów.

Wibowit napisał(a):
  • jaka jest złożoność zapytań, tzn co w nich jest?
  • czy zapytania się powtarzają, a jeśli tak to jak często?
  • jak dużo jest zapytań?

Nie ma zapytań, bo to nie tabela :) Nie chcę podciągać pod to bazy danych, bo za dużo tam wywołań... Problem nie jest bazodanowy, tylko strukturalno-algorytmiczny.
Nadal główne pytanie brzmi:

Jak najlepiej dobrać strukturę, aby przeczesywanie i wyciąganie odpowiednich wierszy było najszybsze. Np. o ile szybsza jest struktura (skróty myślowe...) double[][] od Object[][]

Metoda BruteForce, którą napisałem wygląda tak (pseudokod):

Kopiuj
for(przyklady){
	boolean _c = true;
	for(warunek : warunki_do_spelnienia){
		if(sprawdz_warunek(przyklad, warunek)) {
			_c = false; 
			break;
		}
	}
	if(_c) zapisz_przyklad();
}

gdzie

Kopiuj
sprawdz_warunek(przyklad, warunek) {
	if(przyklad.columna(id) warunek.relacja warunek)
		return false; // spelnia warunek, czyli nie wejdzie do ifa ustawiajacego _c = false
	else
		return true; // nie spelnia warunku, odrzucamy przyklad
}

warunek.relacja to element ze zbioru {<=, >, =}

czyli przykład:

czy przyklad[0] (wiersz z tablicy) spełnia np taki warunek, A <= 3.5 AND C > 7.2 AND E == 10.3 (mamy 3 warunki i wszystkie muszą być spełnione, aby przykład przeszedł dalej)

przy założeniu, że kolumny tablicy nazywamy A, B, C, D, E

jeśli coś jest jeszcze niejasne to opiszę :)

edytowany 4x, ostatnio: adalgrim
02
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 8 lat
  • Postów:1176
0

Tablica jako taka nie zmienia się przy tym za każdym wywołaniem metody.

W takim razie zamiast drzew BST mozesz sobie dac zwykla posortowana tablice jako indeks (albo np. tablice permutacji jezeli chcesz zaoszczedzic troche pamieci).

Tablica jest jednym z parametrów wywoływanej metody, gdzie wywołanie metody idzie w tysiące lub nawet setki tysięcy

W takim przypadku indeksy na pewno pomoga (musisz tylko utworzyc te indeksy raz dla stworzonej tablicy a nie za kazdym razem dla zestawow querisow)

Nawiasem mowiac jak w tej tablicy jest tylko kilka tysiecy elementow to spokojnie moglbys mapowac tebele do bazy danych, tworzyc indeksy i wyszukiwac. Bazy danych sa wlasnie optymalizowane do obslugi duzej ilosci zapytan. Podejrzewam, ze te kilkadziesiat tysiecy zapytan nawet slaba baza jak sqlite obsluzyla by w mniej niz kilka minut. No chyba, ze masz jakas ogromna liczbe kolumn (ale skoro je numerujesz literami to raczej nie masz).

edytowany 1x, ostatnio: 0x200x20
AD
  • Rejestracja:około 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:7
0

Kolumn jest stosunkowo mało, liczby wierszy w zasadzie też (co to jest 5000 :)). Ale wywołań jest kilka(naście/dziesiąt) tysięcy (może być więcej). Stąd boję się o wydajność wrzucania tego do bazy i odpytywania. Chodzi oto, że nie tablica jest duża a ogromna jest liczba zapytań - w zasadzie są sprawdzane wszystkie możliwości (funkcję oceniającą mam). Inna sprawa, że faktycznie najłatwiej wtedy wyciągnąć interesujące wiersze, bo wystarczy najprostsze zapytanie z where...

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:3 minuty
0

Hmm, kilkadziesiąt tysięcy elementów * kilkadziesiąt tysięcy zapytań to kilkaset milionów przeskanowanych obiektów. Zakładając, że przeskanowanie jednego obiektu to tysiąc operacji wychodzi w sumie jakieś kilkaset miliardów operacji procesora dziennie. Biorąc pod uwagę to, że dzisiejsze procki mogą robić miliardy operacji na sekundę, wykonanie dziennego obciążenia powinno trwać od kilku do kilkunastu minut. Oczywiście to bardzo z grubsza oszacowania.

Najlepiej gdybyś podał kompilujący się kod obrazujący problem. Tzn struktury danych o wielkościach takich jak masz w rzeczywistości i tak samo zapytania rzeczywistych rozmiarów. Wtedy można by więcej pomóc.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)