Javascript ES6 złożoność obliczeniowa / czasowa zbiorów

89

Jaką złożoność czasową (w notacji duże-O) zapewnia specyfikacja ES6 dla kolekcji z kluczem (Set, Map, WeakSet i WeakMap)?

Oczekuję, i spodziewam się tego od większości programistów, że specyfikacje i implementacje będą używać powszechnie akceptowanych algorytmów wydajności, w którym to przypadku Set.prototype.has, addi deletewszystkie będą O (1) w przeciętnym przypadku. To samo dotyczy Mapi Weak–odpowiedników.

Nie do końca jest dla mnie jasne, czy złożoność czasowa implementacji była wymagana np. W specyfikacji języka ECMAScript 2015 - wydanie 6 - 23.2 Ustaw obiekty .

O ile nie zrozumiem tego źle (a na pewno jest to bardzo możliwe), wygląda na to, że specyfikacja ECMA nakazuje, aby implementacje (np. Set.prototype.has) Używały algorytmu czasu liniowego ( O (n) ). Wydałoby mi się niezwykle zaskakujące, że bardziej wydajne algorytmy nie byłyby wymagane lub nawet dozwolone w specyfikacji, i byłbym bardzo zainteresowany wyjaśnieniem, dlaczego tak się dzieje.

Brian M. Hunt
źródło
2
Wszystkie algorytmy O (1) są również O (n) , więc dopuszczenie do specyfikacji mniej wydajnych implementacji nie powoduje żadnej szkody. Prawdopodobnie mniej wydajne implementacje mogą być interesujące w systemach z ograniczonymi zasobami, ponieważ najprawdopodobniej wymagają znacznie mniej kodu / przestrzeni.
artur grzesiak 27.06.2015
@arturgrzesiak Wydajność O (1) kolekcji z kluczami jest generalnie osiągana za pomocą mieszania O (1) plus wiadro kolizji O (n). Najgorszy przypadek O (n) jest z większości praktycznych powodów astronomicznie rzadki. Złożoność przestrzenna tych technik jest na ogół O (n).
Brian M. Hunt
1
„Obiekty zestawu muszą być implementowane przy użyciu tablic mieszających lub innych mechanizmów, które średnio zapewniają czasy dostępu podliniowe względem liczby elementów w kolekcji.” - z tej samej strony.
georg

Odpowiedzi:

61

Od tego samego akapitu, do którego linkujesz:

Obiekty zestawu muszą być implementowane przy użyciu [mechanizmów], które średnio zapewniają czasy dostępu, które są podliniowe względem liczby elementów w kolekcji.

To samo zdanie znajdziesz dla Map , WeakMaps i WeakSets .

Wygląda na to, że specyfikacja ECMA nakazuje, aby implementacje (np. Set.prototype.has) używały liniowego O(n)algorytmu time ( ).

Nie:

Struktury danych użyte w tej Setspecyfikacji obiektów mają na celu jedynie opisanie wymaganej obserwowalnej semantyki obiektów Set. Nie ma to być realny model wdrażania.

Obserwowalna semantyka jest głównie związana z przewidywalną kolejnością iteracji (która nadal może być wdrożona wydajnie i szybko ). Rzeczywiście, specyfikacja przewiduje, że implementacja używa tablicy mieszającej lub czegoś podobnego ze stałym dostępem, chociaż drzewa (o logarytmicznej złożoności dostępu) są również dozwolone.

Bergi
źródło
2
Dzięki za wybranie tego. Moje oczy musiały zaszklić, zanim dotarłem do tego akapitu. :) A więc algorytmy, które są albo O (log (n)) albo O (1), ale nie mają innego nakazu (pod warunkiem, że są pod O (n))?
Brian M. Hunt
1
@ BrianM.Hunt: Zgadza się.
Bergi
32

Dla każdego, kto jest ciekawy, zrobiłem bardzo szybki test porównawczy:

const benchmarkMap = size => {
  console.time('benchmarkMap');
  var map = new Map();
  for (var i = 0; i < size; i++) map.set(i, i);
  for (var i = 0; i < size; i++) var x = map.get(i);
  console.timeEnd('benchmarkMap');
}

const benchmarkObj = size => {
  console.time('benchmarkObj');
  var obj = {};
  for (var i = 0; i < size; i++) obj[i] = i;
  for (var i = 0; i < size; i++) var x = obj[i];
  console.timeEnd('benchmarkObj');
}

var size = 1000000;

benchmarkMap(size);
benchmarkObj(size);

Uruchomiłem to kilka razy i uzyskałem następujące wyniki:

(2017 MacBook Pro, 2,5 GHz i7 z 16G RAM)

benchmarkMap: 189.120ms
benchmarkObj: 44.214ms

benchmarkMap: 200.817ms
benchmarkObj: 38.963ms

benchmarkMap: 187.968ms
benchmarkObj: 41.633ms

benchmarkMap: 186.533ms
benchmarkObj: 35.850ms

benchmarkMap: 187.339ms
benchmarkObj: 44.515ms
domdambrogia
źródło
3
@domdambrogia, jeśli oddzielisz ustawienie od uzyskania: Zestaw mapy = 124, Pobierz mapę = 40, Zestaw obiektów = 26, Obiekt Get = 1 (to są współczynniki, a nie ms)
AJP
@AJP Nie zastanawiałem się nad tym, żeby podzielić to na te statystyki. Dzięki za wkład, to dobry wkład. Zobaczę, czy mogę dodać to do mojej odpowiedzi, kiedy będę miał sekundę. Dzięki!
domdambrogia
Byłoby interesujące oddzielenie zadania od czytania, aby dowiedzieć się, który z nich jest szybszy do czytania.
fernandopasik
3
MacBook Pro 2017, 2,5 GHz i7 z 16G RAM ” - uh, to fajne i wszystko, ale który silnik javascript sprawdziłeś?
Bergi,
1
Co ciekawe, gdy dodawanie deleteoperacji i operacji są wymieszane, Mapdziała znacznie lepiej. jsfiddle.net/23hrp0eq
Jorjon