Neden unordered_set yerine set kullanır?


145

C ++ 0x unordered_sethangi boostve diğer birçok yerde kullanılabilir tanıtıyor . Anladığım şey, arama karmaşıklığına unordered_setsahip karma tablo O(1). Öte yandan, arama karmaşıklığı setolan bir ağaçtan başka bir şey değildir log(n). Neden yeryüzünde herkes kullanmak istiyorsunuz setyerine unordered_set? yani setartık bir ihtiyaç var mı?


22
Sorunuz temelde artık bir ağaca ihtiyaç olup olmadığını soruyor.
Vinko Vrsalovic

2
Sanırım ilk satırda açıkça ifade ettim, bu bir şekilde aptalca bir soru. Bir şey
eksiktim

2
Asıl sebep, her şeyin göründüğü kadar S&B olmamasıdır. Aralarında çok fazla griler ve diğer renkler var. Bu kapların araç olduğunu hatırlamanız gerekir. Bazen performans çok önemli değildir ve kolaylık çok daha anlamlı olur. Eğer insanlar en verimli çözümü
aramışlarsa

(Neden yeryüzünde herkes bu adın ima ettiği şeylerin ötesinde vaatleri olan bir uygulama / arayüz için genel bir isim kullanır, olmayanlar için garip bir durum yaratır?)
Greybeard

Yanıtlar:


219

Ne zaman, setin öğeleri üzerinde yineleme yapmak isteyen biri için, sipariş önemlidir.


Kampanya siparişine göre < >mi , yoksa operatörleri kullanarak gerçek karşılaştırmaya göre mi sipariş ediliyor ?
SomethingSomething

2
Varsayılan olarak std :: less kullanılarak sipariş edilir; bunu geçersiz kılabilir ve kendi karşılaştırma operatörünüzü sağlayabilirsiniz. cplusplus.com/reference/set/set
moonshadow

Veya bazen, siparişin önemi olmasa bile, sadece yinelemek istediğinizde.
mfnx

319

Sırasız kümeler, O (1) ortalama erişim sürelerini birkaç şekilde ödemek zorundadır:

  • setkullanan daha az bellek daha unordered_setelemanların aynı sayıda saklamak için.
  • Bir İçin elemanların az sayıda , bir de aramaları setolabilir daha hızlı bir in aramalarının daha unordered_set.
  • Birçok işlemler daha hızlı olmasına rağmen , ortalama durum için unordered_set, genellikle olması garantilidir iyi kötü durum karmaşıklığını için set(örneğin insert).
  • Bu set , öğelere sırayla erişmek istiyorsanız , öğeleri sıralar .
  • Sen edebilirsiniz sözlük sırasında karşılaştırmak farklı setolan s <, <=, >ve >=. unordered_setBu işlemleri desteklemek gerekli değildir.


9
+1, tüm mükemmel noktalar. İnsanlar, karma işlemlerin O (1) ortalama vaka erişim süresine sahip olduğu gerçeğini göz ardı etme eğilimindedir , bu da zaman zaman büyük gecikmeler yaşayabilecekleri anlamına gelir. Ayrım gerçek zamanlı sistemler için önemli olabilir.
j_random_hacker

Ancak iyi puanlar burada ( en.cppreference.com/w/cpp/container/unordered_set/operator_cmp ) sıralanmamış_setleri karşılaştırabileceğimiz belirtiliyor.
Michiel uit het Broek

5
Bir "az sayıda eleman" tanımlayın
Sunjay Varma

4
@SunjayVarma genellikle 100 eleman, ikisi arasında iyi bir kesimdir. Şüpheye düşerseniz, özel kullanım durumunuzda ikisinin test performansının yerini hiçbir şey tutamaz.
Nate

3
@MichieluithetBroek Sadece eşitlik karşılaştırması belirtilir, sipariş verilmez ( <).
lisyarus

26

Bir ağacı karma tabloya tercih ettiğinizde.

Örneğin, karma tablolar en kötü durumda "O (n)" dir. O (1) ortalama bir durumdur. En kötü ağaçlar "O ( log n)" dir.


18
/ Dengeli / ağaçlar en kötü durumda O (ln n) 'dir. O (n) ağaçlar (esas olarak bağlantılı listeler) ile sonuçlanabilir.
strager

5
Oldukça akıllı bir hash fonksiyonu yazabiliyorsanız, neredeyse her zaman O (1) perf'i bir hashtable'dan alabilirsiniz. Kümeniz üzerinde "sırayla" yinelemeniz gerekiyorsa böyle bir karma işlev yazamıyorsanız, bir ağaç kullanmalısınız. Ama bir ağaç kullanmamalısınız çünkü “O (n) en kötü durum performansından” korkuyorsunuz.
Justin L.

6
stager: Bilgiçlik taslayan, evet. Ancak, genellikle dengeli bir ikili arama ağacı olarak uygulanan C ++ kümesinden bahsediyoruz . Karmaşıklık hakkında konuşmak için asıl işlemi belirtmeliydik. Bu bağlamda, arama hakkında konuştuğumuz açıktır.
Mehrdad Afshari

1
Justin L: Bir ağacı tercih etmenizin sadece bir nedeni. Cevabımın çekirdeği ilk satır. Her ne zaman bir karma tabloya bir ağaç veri yapısını tercih ederler. Ağaçların hash masalarına tercih edildiği birçok durum vardır. Karma tablolar özellikle "aralıklı kavşaklar" gibi şeyleri emer.
Mehrdad Afshari

2
stl ağaçları neredeyse evrensel olarak uygulanan kırmızı-siyah ağaçlar, gelişmiş bir kendini dengeleyen ağaçtır. Gerçekten O (n) 'nin daha kötü durumda bakmasının kabul edilemez olduğu durumlar vardır. Kötü amaçlı bir kullanıcı özel hazırlanmış değerleri depolayarak etkili bir şekilde DoS oluşturabileceğinden, kullanıcı değerlerini saklamak ve arabirim sağlayan bir web hizmeti karma harita kullanmamalıdır. Kritik, zamana duyarlı sistemler aynı zamanda O (n) aramasına, hava trafik kontrolüne vb.
usta_code

14

Aşağıdaki durumlarda set kullanın:

  1. Sıralı verilere ihtiyacımız var (farklı öğeler).
  2. Verileri yazdırmamız / erişmemiz gerekir (sıralı olarak).
  3. Öğelerin selefine / halefine ihtiyacımız var.

Unordered_set komutunu şu durumlarda kullanın:

  1. Bir dizi farklı unsuru tutmamız gerekiyor ve sipariş gerekli değil.
  2. Tek eleman erişimine ihtiyacımız var, yani çapraz geçiş yok.

Örnekler:

Ayarlamak:

Giriş: 1, 8, 2, 5, 3, 9

Çıktı: 1, 2, 3, 5, 8, 9

Unordered_set:

Giriş: 1, 8, 2, 5, 3, 9

Çıktı: 9 3 1 8 2 5 (belki de bu işlev, karma işlevinden etkilenir)

Temelde fark:

resim açıklamasını buraya girin

Not: (bazı durumlarda setdaha uygundur) örneğin vectoranahtar olarak kullanma

set<vector<int>> s;
s.insert({1, 2});
s.insert({1, 3});
s.insert({1, 2});

for(const auto& vec:s)
    cout<<vec<<endl;   // I have override << for vector
// 1 2
// 1 3 

Geçersiz kılma nedeniyle vector<int>anahtar olarak neden olabilir .setvectoroperator<

Ancak kullanırsanız unordered_set<vector<int>>, için bir karma işlevi oluşturmanız gerekir vector<int>, çünkü vektörün bir karma işlevi yoktur, bu nedenle aşağıdakini tanımlamanız gerekir:

struct VectorHash {
    size_t operator()(const std::vector<int>& v) const {
        std::hash<int> hasher;
        size_t seed = 0;
        for (int i : v) {
            seed ^= hasher(i) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        }
        return seed;
    }
};

vector<vector<int>> two(){
    //unordered_set<vector<int>> s; // error vector<int> doesn't  have hash function
    unordered_set<vector<int>, VectorHash> s;
    s.insert({1, 2});
    s.insert({1, 3});
    s.insert({1, 2});

    for(const auto& vec:s)
        cout<<vec<<endl;
    // 1 2
    // 1 3
}

bazı durumlarda unordered_setdaha karmaşık olduğunu görebilirsiniz.

Alıntı: https://www.geeksforgeeks.org/set-vs-unordered_set-c-stl/ https://stackoverflow.com/a/29855973/6329006


6

Çünkü std :: set Standard C ++ 'ın bir parçasıdır ve unordered_set değildir. C ++ 0x bir standart değildir ve ikisi de Boost değildir. Birçoğumuz için taşınabilirlik çok önemlidir ve bu da standarda bağlı kalmak anlamına gelir.


2
Onu doğru anlarsam, o zaman neden insanların hala set kullandığını sormuyor. Kendini C ++ 0x hakkında bilgilendiriyor.
Johannes Schaub - litb

2
Olabilir. Herkesin hash tablolarını bildiğini ve ağaçların farklı sorunları çözdüğünü düşündüm.

21
Evet, bu bir standarttır şimdi (sadece bir kaç yıl sürdü)
Clayton Hughes

6

Süpürme hattı algoritmalarını düşünün. Bu algoritmalar hash tablolarıyla tamamen başarısız olur, ancak dengeli ağaçlarla güzel çalışır. Size bir süpürme hattı algoritması somut bir örnek vermek için servet algoritması düşünün. http://en.wikipedia.org/wiki/Fortune%27s_algorithm


1
Bence bu referans çok karmaşık. (Bakmak zorunda kaldım)
hectorpal

3

Bir şey daha, diğer insanların daha önce bahsettiklerine ek olarak. Bir unordered_set bir öğe eklemek için beklenen iskonto edilmiş karmaşıklık O iken (1), her şimdi ve sonra olacak karma masa ihtiyaçları yeniden yapılandırılacak çünkü (değişikliğine kovalar ihtiyaçları sayısı) O (n) almak - hatta ile bir 'iyi' hash fonksiyonu. Tıpkı bir vektöre eleman eklemek her seferinde O (n) alır, çünkü alttaki dizinin yeniden tahsis edilmesi gerekir.

Bir küme eklemek her zaman en fazla O (log n) alır. Bu, bazı uygulamalarda tercih edilebilir.


3

Affedersiniz, sıralanan mülk hakkında fark edilmeye değer bir şey daha:

İsterseniz veri aralığı örneğin kapta,: Sen alan saat seti ve 2013-01-01 den 2014-01-01 zaman istiyorum.

İçin unordered_set imkânsız.

Tabii ki, bu örnek harita ile unordered_map arasındaki kullanım durumları için daha inandırıcı olacaktır .


3

g++ 6.4 stdlibc ++ sipariş vs unordered set benchmark

Farkı görmek için bu baskın Linux C ++ uygulamasını karşılaştırdım:

resim açıklamasını buraya girin

Tüm kıyaslama ayrıntıları ve analizi şu adreste verilmiştir: C ++ 'da ayarlanan bir STL'nin temel veri yapısı nedir? ve burada tekrar etmeyeceğim.

"BST" ile test edilen std::set"ve" karma harita "ile test edilen" anlamına gelir std::unordered_set. "Heap" std::priority_queueanaliz ettiğim yer: Heap vs Binary Search Tree (BST)

Kısa bir özet olarak:

  • grafik, bu koşullar altında, 100 bin'den fazla öğe olduğunda hashmap eklemenin her zaman çok daha hızlı olduğunu ve öğe sayısı arttıkça farkın arttığını açıkça göstermektedir.

    Bu hız artışının maliyeti, sırayla verimli bir şekilde hareket edememenizdir.

  • eğriler, siparişin std::setBST temelli ve std::unordered_sethashmap temelli olduğunu açıkça göstermektedir . Referans cevap, ben de GDB adım kod hata ayıklama ile doğruladı.

mapVs için benzer soru unordered_map: önemsiz anahtarlar durumunda unordered_map üzerinde harita kullanmanın herhangi bir avantajı var mı?


1

El dışında, farklı bir formata dönüştürmek istiyorsanız, bir ilişkide bir şeylere sahip olmanın uygun olduğunu söyleyebilirim.

Birine erişmek daha hızlı olsa da, dizini oluşturma süresinin veya oluştururken ve / veya erişirken kullanılan belleğin daha büyük olması da mümkündür.


+1, Büyük Oh gösterimi sabit faktörleri gizler ve tipik sorun boyutları için genellikle en önemli olan sabit faktörlerdir.
j_random_hacker

1

Sıralanan şeylerin olmasını istiyorsanız, unordered_set yerine set'i kullanırsınız. unordered_set, saklanan siparişin önemi olmadığında set üzerinden kullanılır.


1

Bu cevap 10 yıl geç olsa std::unordered_setda, güvenlik dezavantajları olduğunu belirtmek gerekir .

Karma işlevi öngörülebilirse (rastgele bir tuz gibi karşı önlemleri uygulamadığı sürece genellikle durum budur), saldırganlar karma çarpışmalar üreten ve tüm eklemelerin ve aramaların O (n) zaman almasına neden olan verileri el ile yapabilirler .

Bu, çok verimli ve zarif hizmet reddi saldırıları için kullanılabilir.

Hash haritalarını dahili olarak kullanan birçok (en çok?) Uygulama şu şekildedir:

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.