1 milyarlık sayı dizisinden en büyük 100 sayısını bulmak için bir program yazın


300

Kısa bir süre önce "1 milyarlık bir sayı dizisinden en büyük 100 sayıyı bulmak için bir program yaz" diye bir röportaja katıldım.

Ben sadece O (nlogn) zaman karmaşıklığı dizi sıralamak ve son 100 sayı almak için bir kaba kuvvet çözümü vermeyi başardı.

Arrays.sort(array);

Görüşmeci daha iyi bir zaman karmaşıklığı arıyordu, birkaç başka çözüm denedim ama cevap veremedim. Daha iyi bir zaman karmaşıklığı çözümü var mı?


70
Belki de problem bu değildi yani sıralama soru, ama bir arayan biri.
geomagas

11
Teknik bir not olarak, sıralama sorunu çözmenin en iyi yolu olmayabilir, ancak bunun kaba kuvvet olduğunu düşünmüyorum - bunu yapmanın çok daha kötü yollarını düşünebilirim.
Bernhard Barker

88
Sadece daha aptalca bir kaba kuvvet yöntemi düşündüm ... 1 milyar element dizisinden 100 elementin tüm olası kombinasyonlarını bulun ve bu kombinasyonlardan hangisinin en büyük miktara sahip olduğunu görün.
Shashank

10
Boyut artışı olmadığı için tüm deterministik (ve doğru) algoritmaların O(1)bu durumda olduğunu unutmayın. Görüşmeci "n >> m'li bir n dizisinden en büyük m öğelerini nasıl bulurum?"
Bakuriu

Yanıtlar:


328

Sıradaki en küçük sayıdan (kuyruğun başı) daha büyük bir sayı ile karşılaştığınızda, sıranın başını kaldırabilir ve yeni sayıyı ekleyebilirsiniz. kuyruğa.

DÜZENLEME: Dev'in belirttiği gibi, bir yığınla uygulanan bir öncelik kuyruğu ile kuyruğa ekleme karmaşıklığıO(logN)

En kötü durumda , daha iyi olanbillionlog2(100)billionlog2(billion)

Genel olarak, bir N sayısı kümesinden en büyük K sayılarına ihtiyacınız varsa, karmaşıklık O(NlogK)daha doğrudur O(NlogN), K, N'ye kıyasla çok küçük olduğunda bu çok önemli olabilir.

EDIT2:

Bu algoritmanın beklenen süresi oldukça ilginçtir, çünkü her yinelemede bir ekleme olabilir veya olmayabilir. Sıraya eklenecek olan i'üncü sayının olasılığı, rastgele bir değişkenin i-Kaynı dağılımdan en azından rastgele değişkenlerden daha büyük olma olasılığıdır (ilk k sayıları otomatik olarak kuyruğa eklenir). Bu olasılığı hesaplamak için sipariş istatistiklerini kullanabiliriz ( bağlantıya bakın ). Örneğin, sayıların rasgele olarak eşit olarak seçildiğini {0, 1}, (iK) th sayısının (i sayılarının dışında) beklenen değerinin (i-k)/ive rastgele bir değişkenin bu değerden daha büyük olma olasılığının olduğunu varsayalım 1-[(i-k)/i] = k/i.

Böylece, beklenen ekleme sayısı:

resim açıklamasını buraya girin

Ve beklenen çalışma süresi şu şekilde ifade edilebilir:

resim açıklamasını buraya girin

( ksırayı ilk köğelerle oluşturma zamanı , ardından n-kkarşılaştırmalar ve yukarıda açıklandığı gibi beklenen ekleme sayısı her biri ortalama bir log(k)/2zaman alır )

Ne zaman geldiğini hatırlatırız Nkarşılaştırarak çok büyük K, bu ifade çok daha yakın olmaktır nziyade NlogK. Bu, soru söz konusu olduğu gibi, 10000 yinelemeden sonra bile (bir milyar ile karşılaştırıldığında çok küçük) bile, bir sayının kuyruğa girme şansı çok azdır.


6
Aslında her bir kesici uç için sadece O (100) 'dir .
MrSmith42

8
@RonTeller Bağlantılı bir listeyi etkili bir şekilde ikili olarak arayamazsınız, bu nedenle öncelik sırası genellikle bir yığınla uygulanır. Açıklandığı gibi yerleştirme süreniz O (logn) değil O (n) 'dir. Skizz sizi ikinci kez tahmin edene kadar ilk seferinde (sıralı veya öncelikli sıra) doğru yapmıştınız.
Dev

17
@ThomasJungblut milyar da sabittir, bu durumda O (1): P
Ron Teller

9
@RonTeller: normalde bu tür sorular, milyarlarca Google arama sonucundan en iyi 10 sayfayı veya bir kelime bulutu için en sık kullanılan 50 kelimeyi veya MTV'deki en popüler 10 şarkıyı bulmak gibi şeyleri düşünüyor. Bu nedenle, normal koşullarda inanıyorum. kıyasla k sabit ve küçük düşünmek güvenlidir n. Yine de, bu "normal koşulları" daima akılda tutmak gerekir.
arkadaş

5
1G öğeleriniz olduğundan, rastgele 1000 öğeyi örnekleyin ve en büyük 100'ü seçin. Bu, dejenere vakalardan (sıralı, ters sıralı, çoğunlukla sıralı) kaçınmalı ve kesici uçların sayısını önemli ölçüde azaltmalıdır.
ChuckCottrill

136

Bu bir röportajda sorulursa, görüşmeci muhtemelen sadece algoritma bilginizi değil, problem çözme sürecinizi görmek istiyor.

Açıklama oldukça geneldir, bu yüzden problemi açıklığa kavuşturmak için ona bu sayıların aralığını veya anlamını sorabilirsiniz. Bunu yapmak görüşmeyi etkileyebilir. Örneğin, bu rakamlar insanların bir ülke (örneğin Çin) içindeki yaşını temsil ediyorsa, o zaman çok daha kolay bir sorundur. Hayatta kimsenin 200'den büyük olmadığı makul bir varsayımla, aynı yaştaki insanların sayısını tek bir yinelemede saymak için 200 boyutunda bir int dizisi kullanabilirsiniz (belki 201). Burada indeks yaş demektir. Bundan sonra en büyük 100 numarayı bulmak çok kolay. Bu arada bu algoya sayma sıralaması denir .

Her neyse, soruyu daha spesifik ve açık hale getirmek bir röportajda sizin için iyidir.


26
Çok iyi noktalar. Kimse bu sayıların dağılımı hakkında herhangi bir şey sormadı veya göstermedi - soruna nasıl yaklaşılacağı konusunda tüm farkı yaratabilir.
NealB

13
Bu cevabı uzatmak için yeterince istiyorum. Dağılım yapabilmeniz için min / maks değerlerini almak üzere sayıları bir kez okuyun. Ardından, iki seçenekten birini kullanın. Aralık yeterince küçükse, sayıları oluştukça kontrol edebileceğiniz bir dizi oluşturun. Aralık çok büyükse, yukarıda tartışılan sıralı yığın algoritmasını kullanın .... Sadece bir düşünce.
Richard_G

2
Katılıyorum, görüşmeciye soru sormak gerçekten çok fark yaratıyor. Aslında, hesaplama gücü ile sınırlı olup olmadığınız gibi bir soru, birden fazla hesaplama düğümü kullanarak çözümü paralel hale getirmenize de yardımcı olabilir.
Nigam Sumit

1
@R_G Tüm listeyi gözden geçirmeye gerek yok. Yararlı istatistikler elde etmek için listenin küçük bir kısmını rastgele (örneğin, bir milyon) örneklemek için yeterlidir.
Itamar

Bu çözümü düşünmemiş olanlar için en.wikipedia.org/wiki/Counting_sort sayım sıralaması hakkında bilgi almanızı tavsiye ederim . Bu oldukça yaygın bir röportaj sorusu: Bir diziyi O (nlogn) 'dan daha iyi sıralayabilir misiniz? Bu soru sadece bir süredir.
Maxime Chéramy

69

O (n) alan sayıları yineleyebilirsiniz

Geçerli minimum değerden daha büyük bir değer bulduğunuzda, yeni değeri 100 boyutlu dairesel bir kuyruğa ekleyin.

Bu dairesel kuyruğun min değeri, yeni karşılaştırma değerinizdir. Bu kuyruğa eklemeye devam edin. Doluysa, minimum değeri kuyruktan çıkarın.


3
Bu işe yaramıyor. örneğin, {1, 100, 2, 99} 'un ilk 2'sini {100,1}' e ilk 2 olarak verir.
Skizz

7
Sırayı sıralanmış olarak tutamazsınız. (bir sonraki en küçük eleman için her seferinde delik kuyruğunu aramak istemiyorsanız)
MrSmith42

3
@ MrSmith42 Bir yığın gibi kısmi sıralama yeterlidir. Ron Teller'in cevabına bakınız.
Christopher Creutzig

1
Evet, sessizce bir özü-min-kuyruğunun yığın olarak uygulandığını varsaydım.
Regenschein

Dairesel sıra yerine 100 büyüklüğünde minimum yığın kullanın, üstte en az yüz sayı olacaktır. Bu, kuyruk durumunda yalnızca o (n) ile karşılaştırıldığında ekleme için O (log n) alacaktır
techExplorer

33

Bunun 'algoritma' ile etiketlendiğini fark ettim, ancak muhtemelen 'röportaj' olarak da etiketlenmesi gerektiğinden, diğer bazı seçenekleri de atacağım.

1 milyar rakamın kaynağı nedir? Eğer bir veritabanı ise, 'tablo sırasından değeri desc limit 100 değerine göre seç' işi oldukça iyi yapar - lehçe farklılıklar olabilir.

Bu bir defalık mı yoksa tekrarlanacak bir şey mi? Tekrarlanırsa, ne sıklıkta? Bir defalıksa ve veriler bir dosyadaysa, 'cat srcfile | sıralama (isteğe bağlı seçenekler) | head -100 ', bilgisayar bu önemsiz işi hallederken size ödenen verimli işleri hızlı bir şekilde yapacaktır.

Tekrarlanırsa, ilk cevabı almak ve sonuçları saklamak / saklamak için iyi bir yaklaşım seçmenizi tavsiye edersiniz, böylece ilk 100'ü sürekli olarak raporlayabilirsiniz.

Son olarak, bu düşünce var. Giriş seviyesi bir iş mi arıyorsunuz ve bir geeky yöneticisi veya gelecekteki bir meslektaşınızla mülakat yapıyor musunuz? Eğer öyleyse, ilgili teknik artıları ve eksileri tanımlayan her türlü yaklaşımı fırlatabilirsiniz. Daha yönetimsel bir iş arıyorsanız, çözümün geliştirme ve bakım maliyetleri ile ilgilenen bir yönetici gibi yaklaşın ve "çok teşekkür ederim" deyin ve görüşmeci CS trivia'ya odaklanmak istiyorsa bırakın . O ve sen orada çok fazla ilerleme potansiyeline sahip olmayacaksınız.

Bir sonraki röportajda daha iyi şanslar.


2
Olağanüstü cevap. Diğer herkes sorunun teknik yönüne odaklanırken, bu yanıt işin sosyal yönüyle ilgilenir.
vbocan

2
Teşekkür edebilirim, röportaj bırakıp bitmesini beklemeyeceğinizi hiç düşünmemiştim. Fikrimi açtığın için teşekkürler.
UrsulRosu

1
Neden bir yığın milyar element oluşturamıyoruz ve en büyük 100 elementi çıkartamıyoruz. Bu şekilde maliyet = O (milyar) + 100 * O (log (milyar)) ??
Mohit Shah

17

Bunun için anında tepkim bir yığın kullanmak olacaktır, ancak herhangi bir zamanda tüm giriş değerlerini el altında tutmadan QuickSelect'i kullanmanın bir yolu vardır.

200 büyüklüğünde bir dizi oluşturun ve ilk 200 giriş değeriyle doldurun. QuickSelect'i çalıştırın ve size 100 ücretsiz yer bırakarak düşük 100 değerini atın. Sonraki 100 giriş değerini okuyun ve QuickSelect'i tekrar çalıştırın. Tüm girdiyi 100'lük gruplar halinde çalışana kadar devam edin.

Sonunda en iyi 100 değere sahipsiniz. N değerleri için QuickSelect'i yaklaşık olarak N / 100 kez çalıştırdınız. Her bir Quickselect'in maliyeti sabitin yaklaşık 200 katıdır, bu nedenle toplam maliyet sabitin 2 katıdır. Bu açıklamada 100 olarak sabit olduğum parametre boyutuna bakılmaksızın, bana girdi boyutunda doğrusal görünüyor.


10
Küçük ama muhtemelen önemli bir optimizasyon ekleyebilirsiniz: 200 dizisini bölümlemek için QuickSelect'i çalıştırdıktan sonra, ilk 100 öğenin minimum değeri bilinir. Daha sonra, tüm veri kümesini yinelediğinde, yalnızca geçerli değer geçerli minimumdan büyükse düşük 100 değerini doldurun. C ++ 'da bu algoritmanın basit bir uygulaması, libstdc ++' ın partial_sortdoğrudan 200 milyon 32 bitlik bir veri kümesi intüzerinde (eşit olarak dağıtılmış bir MT19937 ile oluşturulmuş) çalıştırılmasıyla eşittir.
dyp

1
İyi fikir - en kötü durum analizini etkilemez, ama yapmaya değer.
mcdowella

@mcdowella Denemeye değer ve yapacağım, teşekkürler!
userx

8
Guava'nın yaptığı tam olarak budur Ordering.greatestOf(Iterable, int). Kesinlikle doğrusal zaman ve tek geçişli ve süper sevimli bir algoritma. FWIW, bazı gerçek kriterlerimiz de var: sabit faktörleri ortalama durumda geleneksel öncelik kuyruğundan daha yavaş bir saçtır, ancak bu uygulama "en kötü durum" girdisine (örn. Kesinlikle artan giriş) çok daha dayanıklıdır.
Louis Wasserman

15

[Milyar-101] dizinindeki sayıyı bulmak için Hızlı seçim algoritmasını kullanabilir ve ardından sayılar üzerinde yineleyebilir ve bu sayıdan daha büyük sayıları bulabilirsiniz.

array={...the billion numbers...} 
result[100];

pivot=QuickSelect(array,billion-101);//O(N)

for(i=0;i<billion;i++)//O(N)
   if(array[i]>=pivot)
      result.add(array[i]);

Bu algoritma Zamanı: 2 XO (N) = O (N) (Ortalama vaka performansı)

Thomas Jungblut gibi ikinci seçenek :

Yığın kullanın MAX yığın O (N) alacak, daha sonra ilk 100 maksimum sayı Yığın üstünde olacak, ihtiyacınız olan tek şey onları yığıntan çıkarmak (100 XO (Log (N)).

Bu algoritma Zamanı: O (N) + 100 XO (Log (N)) = O (N)


8
Tüm liste boyunca üç kez çalışıyorsunuz. 1 biyo. tamsayılar kabaca 4gb, bunları belleğe sığmazsanız ne yapardınız? quickselect bu durumda mümkün olan en kötü seçimdir. Bir kez yineleme ve en iyi 100 öğenin yığınını tutma IMHO, O (n) 'de en iyi performans gösteren çözümdür (yığındaki n'nin 100 (sabit = çok küçük olduğu için yığın eklerinin O (günlük n)' sini kesebileceğinizi unutmayın) ).
Thomas Jungblut

3
Yine de O(N), iki QuickSelect ve başka bir doğrusal tarama yapmak gerekenden çok daha fazla yük.
Kevin

Bu PSEUDO kodu buradaki tüm çözümlerin daha fazla zaman alacağı (O (NLOG (N) veya 100 * O (N))
One Man Crew

1
100*O(N)(bu geçerli bir sözdizimi ise) = O(100*N)= O(N)(100 kabul edilebilir, eğer öyleyse, bu kesinlikle doğru değildir). Oh, ve Quickselect'in en kötü durum performansı O (N ^ 2) (ah). Ve belleğe sığmazsa, verileri diskten iki kez yeniden yükleyeceksiniz, bu da bir kereden çok daha kötü (bu darboğaz).
Bernhard Barker

Bunun beklenen çalışma süresi ve en kötü durum değil, ancak iyi bir pivot seçim stratejisi kullanarak (örn. 21 öğeyi rastgele seçin ve bu 21'in medyanını pivot olarak seçin), sonra karşılaştırma sayısı olabilir. keyfi olarak küçük bir sabit için yüksek olasılıkla (2 + c) n olması garantili c.
One Man Crew

10

Diğer quickselect çözümü indirilmemesine rağmen, quickselect'in çözümü 100 büyüklüğünde bir kuyruk kullanmaktan daha hızlı bulacağı gerçeği devam ediyor. Quickselect'in karşılaştırmalar açısından beklenen 2n + o (n) çalışma süresi var. Çok basit bir uygulama

array = input array of length n
r = Quickselect(array,n-100)
result = array of length 100
for(i = 1 to n)
  if(array[i]>r)
     add array[i] to result

Bu ortalama 3n + o (n) karşılaştırmaları alacaktır. Ayrıca, quickselect'in dizideki en büyük 100 öğeyi en sağdaki 100 konumda bırakması daha verimli hale getirilebilir. Böylece, çalışma süresi 2n + o (n) 'ye kadar geliştirilebilir.

Bunun beklenen çalışma süresi ve en kötü durum değil, ancak iyi bir pivot seçim stratejisi kullanarak (örn. 21 öğeyi rastgele seçin ve bu 21'in medyanını pivot olarak seçin), sonra karşılaştırma sayısı olabilir. keyfi olarak küçük bir sabit için yüksek olasılıkla (2 + c) n olması garantili c.

Aslında, optimize edilmiş bir örnekleme stratejisi (örn. Rastgele örnek sqrt (n) öğeleri kullanarak ve 99. yüzdelik dilimi seçerek), çalışma süresi keyfi olarak küçük c için (1 + c) n + o (n) değerine kadar indirilebilir (K olduğu varsayılarak, seçilecek eleman sayısı o (n) 'dir).

Öte yandan, 100 büyüklüğünde bir kuyruk kullanmak O (log (100) n) karşılaştırmaları gerektirir ve 100 log tabanı 2 yaklaşık olarak 6.6'ya eşittir.

Bu sorunu, N boyutu dizisinden en büyük K öğelerini seçmenin daha soyut anlamıyla düşünürsek, burada K = o (N) ama hem K hem de N sonsuza gider, o zaman hızlı seçim versiyonunun çalışma süresi O (N) ve kuyruk versiyonu O (N log K) olacaktır, bu yüzden bu bağlamda hızlı seçim asimptotik olarak daha üstündür.

Yorumlarda, kuyruk çözümünün rastgele bir girdi üzerinde N + K log N beklenen zamanda çalışacağı belirtildi. Elbette, soru açıkça belirtmedikçe, rastgele girdi varsayımı asla geçerli değildir. Kuyruk çözümü, diziyi rastgele bir sırayla geçirmek için yapılabilir, ancak bu, rastgele bir sayı üretecine N çağrılarının ek maliyetine neden olacak ve tüm giriş dizisine izin verecek veya başka bir rastgele indeksler.

Sorun, orijinal dizideki öğelerin etrafında gezinmenize izin vermiyorsa ve bellek ayırma maliyeti yüksekse diziyi çoğaltmak bir seçenek değildir, bu farklı bir konudur. Ama kesinlikle çalışma süresi açısından, bu en iyi çözümdür.


4
Son paragrafınız kilit noktadır: Bir milyar sayı ile tüm verileri hafızada tutmak veya öğeleri takas etmek mümkün değildir. (En azından bir röportaj sorusu olduğu için sorunu bu şekilde yorumlayacağım.)
Ted Hopp

14
Herhangi bir algoritmik soruda, verilerin okunması bir sorunsa, soruda belirtilmelidir. Soru, diskte belleğe sığmayan ve algoritma analizinde standart olan von neuman modeline göre değiştirilemeyen bir dizi verildiğinde "değil bir dizi verildi" ifadesini belirtir. Bu günlerde 8g koçlu bir dizüstü bilgisayar alabilirsiniz. Bir milyar rakamı hafızada tutma fikrinin mümkün olmadığından emin değilim. Şu anda iş istasyonumda birkaç milyar belleğim var.
13'te

Bilginize QuickSelect arasında kötü durum çalışma zamanı O (n ^ 2) (bakınız en.wikipedia.org/wiki/Quickselect ) ve aynı zamanda giriş dizideki elementlerin sırasını değiştirir. Çok büyük bir sabitle ( en.wikipedia.org/wiki/Median_of_medians ) en kötü O (n) çözümüne sahip olmak mümkündür .
puan

En hızlı hızlı seçim vakasının katlanarak gerçekleşmesi olası değildir, bu da pratik amaçlar için bunun önemsiz olduğu anlamına gelir. Hızlı seçimi değiştirmek kolaydır, böylece yüksek olasılıkla karşılaştırma sayısı keyfi olarak küçük c için (2 + c) n + o (n) olur.
mrip

"Gerçek şu ki, quickselect 100 büyüklüğünde bir kuyruk kullanmaktan daha hızlı çözüm bulacaktır" - Hayır. Yığın çözümü, N = Klog (N) karşılaştırmaları ile hızlı seçim için 2N ortalaması ve Median of Medians için 2.95 karşılaştırması yapar. Verilen K. için açıkça daha hızlı
Neil G

5

milyarın ilk 100 sayısını al ve sırala. şimdi sadece milyardan tekrarlayın, eğer kaynak sayı 100'den küçükse, sıralama düzenini ekleyin. Sonunda, setin büyüklüğü üzerinde O (n) 'ye çok daha yakın bir şey var.


3
ayy, benimkinden daha ayrıntılı bir cevap görmedi.
Samuel Thurston

İlk 500 kadar sayıyı alın ve liste dolduğunda sıralamayı bırakın (ve düşük 400'ü atın). (Ve o zaman listeye sadece yeni sayı seçilen 100'ün en düşükse eklediğinizi söylemeye gerek yok.)
Hot Licks

4

İki seçenek:

(1) Yığın (öncelik Sırası)

100 boyutunda bir min-yığın tutun. Diziyi çaprazlayın. Öğe, öbekteki ilk öğeden küçük olduğunda, değiştirin.

InSERT ELEMENT INTO HEAP: O(log100)
compare the first element: O(1)
There are n elements in the array, so the total would be O(nlog100), which is O(n)

(2) Harita azaltma modeli.

Bu hadoop'taki kelime sayısı örneğine çok benzer. Harita işi: görüntülenen her öğenin sıklığını veya saatini sayın. Azalt: Üst K öğesini al.

Genellikle işe alan kişiye iki cevap veririm. Onlara ne isterse verin. Tabii ki, harita azaltma kodlaması emek-bazı olurdu çünkü her kesin parametreyi bilmek zorundasınız. Uygulama zararı yok. İyi şanslar.


MapReduce için +1, bir milyar sayı için Hadoop'tan bahseden tek kişi olduğuna inanamıyorum. Görüşmeci 1 milyar milyar rakam isterse ne olur? Bence daha fazla oy hak ediyorsun.
Silviu Burcea

@Silviu Burcea Çok teşekkürler. Ben de MapReduce değer. :)
Chris Su

Bu örnekte 100 boyutu sabit olmasına rağmen, bunu gerçekten ayrı bir değişkene genellemelisiniz. k. 100, 1 milyar kadar sabit olduğu için, neden büyük sayı kümesinin boyutuna, daha küçük sayı kümesi için değil, n boyut değişkeni veriyorsunuz? Gerçekten karmaşıklığınız O (n) olmayan O (nlogk) olmalıdır.
Tom Heard

1
Ama benim açımdan, eğer soruyu sadece cevaplıyorsanız, soruda 1 milyar sabittir, bu yüzden neden 100'den k'a değil 1 milyardan n'ye genelleştirin. Mantıklarınızın ardından karmaşıklık aslında O (1) olmalıdır, çünkü bu soruda hem 1 milyar hem de 100 sabittir.
Tom Heard

1
@TomHeard Tamam. O (nlogk) Sonuçları etkileyecek tek bir faktör vardır. Bu, eğer n gittikçe büyüyorsa, "sonuç seviyesi" doğrusal olarak artacaktır. Ya da diyebiliriz ki, trilyon sayı olsa bile, hala en büyük 100 rakamı alabilirim. Ancak şunu söyleyemezsiniz: n'nin artmasıyla k artar, böylece k sonucu etkiler. Bu yüzden O (nlogk) kullanıyorum ama O (nlogn) kullanmıyorum
Chris Su

4

Çok kolay bir çözüm diziyi 100 kez tekrarlamak olacaktır. Hangi O(n).

En büyük sayıyı her çıkardığınızda (ve değerini minimum değere değiştirdiğinizde, bir sonraki yinelemede görmediğiniz veya önceki yanıtların dizinlerini takip ettiğinizde (orijinal dizinin sahip olabileceği dizinleri izleyerek) aynı sayının katları)). 100 yinelemeden sonra en büyük 100 sayıya sahipsiniz.


1
İki dezavantaj - (1) Süreçteki girdiyi yok ediyorsunuz - bu tercihen önlenir. (2) Diziyi birden çok kez geçirirsiniz - dizi diskte depolanır ve belleğe sığmazsa, bu kabul edilen yanıttan neredeyse 100 kat daha yavaş olabilir. (Evet, ikisi de O (n), ama yine de)
Bernhard Barker

İyi arama @Dukeling, önceki cevap indekslerini takip ederek orijinal girdinin değiştirilmesinin nasıl önleneceğine dair ek ifadeler ekledim. Bu hala kodlamak oldukça kolay olurdu.
James Oravec

O (n log n) 'den çok daha yavaş olan O (n) çözeltisinin mükemmel bir örneği. log2 (1 milyar) sadece 30 ...
gnasher729

@ gnasher729 O (n log n) sabitinin büyüklüğü nedir?
miracle173

1

@Ron teller'ın cevabından esinlenerek, istediğinizi yapmak için bir barebone C programı.

#include <stdlib.h>
#include <stdio.h>

#define TOTAL_NUMBERS 1000000000
#define N_TOP_NUMBERS 100

int 
compare_function(const void *first, const void *second)
{
    int a = *((int *) first);
    int b = *((int *) second);
    if (a > b){
        return 1;
    }
    if (a < b){
        return -1;
    }
    return 0;
}

int 
main(int argc, char ** argv)
{
    if(argc != 2){
        printf("please supply a path to a binary file containing 1000000000"
               "integers of this machine's wordlength and endianness\n");
        exit(1);
    }
    FILE * f = fopen(argv[1], "r");
    if(!f){
        exit(1);
    }
    int top100[N_TOP_NUMBERS] = {0};
    int sorts = 0;
    for (int i = 0; i < TOTAL_NUMBERS; i++){
        int number;
        int ok;
        ok = fread(&number, sizeof(int), 1, f);
        if(!ok){
            printf("not enough numbers!\n");
            break;
        }
        if(number > top100[0]){
            sorts++;
            top100[0] = number;
            qsort(top100, N_TOP_NUMBERS, sizeof(int), compare_function);
        }

    }
    printf("%d sorts made\n"
    "the top 100 integers in %s are:\n",
    sorts, argv[1] );
    for (int i = 0; i < N_TOP_NUMBERS; i++){
        printf("%d\n", top100[i]);
    }
    fclose(f);
    exit(0);
}

Makinemde (hızlı SSD'li core i3) 25 saniye ve 1724 çeşit alıyor. dd if=/dev/urandom/ count=1000000000 bs=1Bu çalışma için bir ikili dosya oluşturdum .

Açıkçası, diskten bir seferde sadece 4 bayt okuma ile ilgili performans sorunları var, ancak bu örneğin aşkına. Artı tarafta, çok az bellek gerekir.


1

En basit çözüm, milyar sayı büyük dizisini taramak ve şimdiye kadar herhangi bir sıralama olmadan küçük bir dizi tamponunda bulunan en büyük 100 değeri tutmak ve bu tamponun en küçük değerini hatırlamaktır. İlk önce bu yöntemin fordprefect tarafından önerildiğini düşündüm, ancak bir yorumda 100 numara veri yapısının yığın olarak uygulandığını varsaydığını söyledi. Daha büyük olan yeni bir sayı bulunduğunda, arabellekteki minimum değerin üzerine bulunan yeni değer yazılır ve arabellek yeniden geçerli minimum değeri arar. Milyar sayı dizisindeki sayılar çoğu zaman rasgele dağıtılırsa, büyük dizideki değer küçük dizinin minimum değeriyle karşılaştırılır ve atılır. Yalnızca çok küçük bir sayı kesiri için değer küçük diziye eklenmelidir. Bu nedenle, küçük sayıları tutan veri yapısını manipüle etme farkı ihmal edilebilir. Az sayıda öğe için, bir öncelik kuyruğu kullanımının saf yaklaşımımı kullanmaktan daha hızlı olup olmadığını belirlemek zordur.

10 ^ 9 öğe dizisi tarandığında küçük 100 öğe dizisi arabelleğindeki ekleme sayısını tahmin etmek istiyorum. Program bu büyük dizinin ilk 1000 elemanını tarar ve ara belleğe en fazla 1000 eleman eklemelidir. Tampon, taranan 1000 elemanın 100 elemanını, yani taranan elemanın 0.1 elemanını içerir. Bu nedenle, büyük dizideki bir değerin, tamponun geçerli minimum değerinden daha büyük olma olasılığının yaklaşık 0.1 olduğunu varsayıyoruz. Böyle bir eleman, tampona eklenmelidir. Şimdi program büyük diziden sonraki 10 ^ 4 elemanını tarar. Çünkü her yeni eleman eklendiğinde tamponun asgari miktarı artacaktır. Mevcut minimum değerimizden daha büyük öğelerin oranının yaklaşık 0,1 olduğunu ve eklenecek 0,1 * 10 ^ 4 = 1000 öğenin olduğunu tahmin ettik. Aslında, ara belleğe eklenen beklenen öğe sayısı daha az olacaktır. Bu 10 ^ 4 elementin taranmasından sonra tampondaki sayıların fraksiyonu şimdiye kadar taranan elementlerin yaklaşık 0.01'i olacaktır. Bu nedenle, sonraki 10 ^ 5 sayılarını tararken, ara belleğe 0.01 * 10 ^ 5 = 1000'den fazla eklenmeyeceğini varsayıyoruz. Bu tartışmaya devam ederek, büyük dizinin 1000 + 10 ^ 4 + 10 ^ 5 + ... + 10 ^ 9 ~ 10 ^ 9 elemanlarını taradıktan sonra yaklaşık 7000 değer ekledik. Bu nedenle, rastgele boyutta 10 ^ 9 öğe içeren bir dizi tararken, arabellekte en fazla 10 ^ 4 (= 7000 yuvarlatılmış) ekleme olmasını bekleriz. Arabelleğe her sokulduktan sonra yeni minimum bulunmalıdır. Arabellek basit bir diziyse, yeni minimum değeri bulmak için 100 karşılaştırmaya ihtiyacımız vardır. Tampon başka bir veri yapısı ise (yığın gibi) minimum değeri bulmak için en az 1 karşılaştırmaya ihtiyacımız var. Büyük dizinin elemanlarını karşılaştırmak için 10 ^ 9 karşılaştırmaya ihtiyacımız var. Sonuç olarak, bir diziyi tampon olarak kullanırken yaklaşık 10 ^ 9 + 100 * 10 ^ 4 = 1.001 * 10 ^ 9 karşılaştırmasına ve başka bir veri yapısı türü (yığın gibi) kullanırken en az 1.000 * 10 ^ 9 karşılaştırmaya ihtiyacımız var. . Dolayısıyla, performans karşılaştırma sayısına göre belirlenirse, bir yığın kullanmak yalnızca% 0,1'lik bir kazanç sağlar. Ancak, 100 element yığınına bir eleman eklemek ile 100 element dizisindeki bir elemanın değiştirilmesi ile yeni minimum değerini bulmak arasındaki yürütme süresindeki fark nedir? Başka bir veri yapısı türü (yığın gibi) kullanırken 000 * 10 ^ 9 karşılaştırmaları. Dolayısıyla, performans karşılaştırma sayısına göre belirlenirse, bir yığın kullanmak yalnızca% 0,1'lik bir kazanç sağlar. Ancak, 100 element yığınına bir eleman eklemek ile 100 element dizisindeki bir elemanın değiştirilmesi ile yeni minimum değerini bulmak arasındaki yürütme süresindeki fark nedir? Başka bir veri yapısı türü (yığın gibi) kullanırken 000 * 10 ^ 9 karşılaştırmaları. Dolayısıyla, performans karşılaştırma sayısına göre belirlenirse, bir yığın kullanmak yalnızca% 0,1'lik bir kazanç sağlar. Ancak, 100 element yığınına bir eleman eklemek ile 100 element dizisindeki bir elemanın değiştirilmesi ile yeni minimum değerini bulmak arasındaki yürütme süresindeki fark nedir?

  • Teorik düzeyde: Bir öbeğe eklemek için kaç karşılaştırmaya ihtiyaç vardır. O (log (n)) olduğunu biliyorum ama sabit faktör ne kadar büyük? ben

  • Makine düzeyinde: Önbellek ve dal tahmininin, bir yığın ekinin yürütme süresi ve bir dizideki doğrusal aramanın etkisi nedir?

  • Uygulama düzeyinde: Bir kütüphane veya derleyici tarafından sağlanan yığın veri yapısında hangi ek maliyetler gizlenir?

Ben bir 100 element yığın performansı veya 100 element dizi performansı arasındaki gerçek farkı tahmin etmeye çalışabilirsiniz önce cevaplanması gereken sorular bazıları olduğunu düşünüyorum. Bu yüzden bir deney yapmak ve gerçek performansı ölçmek mantıklı olacaktır.


1
Bir yığın bunu yapar.
Neil G

@Neil G: Ne "o"?
miracle173

1
Yığının üstü, yığındaki minimum öğedir ve yeni öğeler bir karşılaştırma ile reddedilir.
Neil G

1
Ne dediğini anlıyorum, ancak asimptotik karşılaştırma sayısı yerine mutlak sayıda karşılaştırma yapsanız bile, dizi hala çok daha yavaş çünkü "yeni eleman ekleme, eski minimumları atma ve yeni minimumları bulma" zamanı 100, 7 yerine 7
Neil G

1
Tamam, ama tahmininiz çok dolambaçlı. Beklenen ek sayısını doğrudan k (digamma (n) - digamma (k)) olarak hesaplayabilirsiniz, bu da klog (n) 'den daha azdır. Her durumda, hem yığın hem de dizi çözümü bir öğeyi atmak için yalnızca bir karşılaştırma harcar. Tek fark, eklenen bir eleman için karşılaştırma sayısı çözümünüz için 100, yığın için 14'e kadar (ortalama durum muhtemelen daha az olmasına rağmen)
Neil G

1
 Although in this question we should search for top 100 numbers, I will 
 generalize things and write x. Still, I will treat x as constant value.

Algoritma n'den en büyük x elemanı:

LIST dönüş değerini arayacağım . Bu x elemanları kümesidir (bence bağlantılı liste olmalı)

  • İlk x elemanları "geldikçe" havuzundan alınır ve LIST'e göre sıralanır (x sabit - O (x log (x)) zamanı olarak değerlendirildiğinden bu sabit sürede yapılır)
  • Sonraki her öğe için, LIST içindeki en küçük öğeden daha büyük olup olmadığını kontrol ederiz ve en küçük öğeyi çıkarır ve geçerli öğeyi LIST'e ekler miyiz. Bu sıralı liste olduğundan, her eleman logaritmik zamandaki yerini bulmalıdır (ikili arama) ve sıralandığı için listenin eklenmesi bir sorun değildir. Her adım sabit zamanda (O (log (x)) zamanda) yapılır.

Peki, en kötü durum senaryosu nedir?

x log (x) + (nx) (log (x) +1) = nlog (x) + n - x

Yani en kötü durum için O (n) zamanı. +1, sayının LIST'deki en küçük sayıdan büyük olup olmadığını kontrol eder. Ortalama vaka için beklenen süre, bu n öğenin matematiksel dağılımına bağlı olacaktır.

Olası iyileştirmeler

Bu algoritma en kötü durum senaryosu için biraz geliştirilebilir, ancak ortalama davranışı bozacak IMHO (bu iddiayı kanıtlayamıyorum). Asimptotik davranış aynı olacaktır.

Bu algoritmadaki gelişme, elemanın en küçükten büyük olup olmadığını kontrol etmeyeceğimiz olacaktır. Her eleman için eklemeye çalışacağız ve en küçükten küçükse onu göz ardı edeceğiz. Her ne kadar sadece en kötü durum senaryosuna bakarsak, bu mantıksız gelse de

x günlüğü (x) + (nx) günlüğü (x) = nlog (x)

operasyonlar.

Bu kullanım durumu için başka gelişme görmüyorum. Yine de kendinize şunu sormalısınız - bunu log (n) kereden fazla ve farklı x-es için yapmak zorunda kalırsam? Açıkçası bu diziyi O (n log (n)) şeklinde sıralayacağız ve x elemanımızı ihtiyacımız olduğunda alacağız.


1

Bu soru sadece bir satır C ++ kodu ile N log (100) karmaşıklığı (N log N yerine) ile cevaplandırılacaktır.

 std::vector<int> myvector = ...; // Define your 1 billion numbers. 
                                 // Assumed integer just for concreteness 
 std::partial_sort (myvector.begin(), myvector.begin()+100, myvector.end());

Son cevap, ilk 100 elemanın dizinizin en büyük 100 sayısı olduğu garanti edilen bir vektör olacaktır.

C ++ STL (standart kütüphane) bu tür problemler için oldukça kullanışlıdır.

Not: Bunun en uygun çözüm olduğunu söylemiyorum ama görüşmenizi kurtaracaktı.


1

Basit çözüm, sıraya ilk 100 sayıyı ekleyerek ve sıradaki en küçük sayıyı takip ederek, daha sonra diğer milyar sayılarını yineleyerek ve her seferinde en büyük sayıdan daha büyük olanı bulduğumuz bir öncelik sırası kullanmak olacaktır. öncelik sırasına göre, en küçük sayıyı kaldırır, yeni sayıyı ekleriz ve sıradaki en küçük sayıyı tekrar izleriz.

Sayılar rasgele sırada olsaydı, bu güzel olurdu çünkü bir milyar rasgele sayı boyunca yinelediğimizde, bir sonraki sayının şu ana kadarki en büyük 100 arasında olması çok nadir olurdu. Ancak sayılar rastgele olmayabilir. Dizi zaten artan sırada sıralandıysa, her zaman öncelik sırasına bir öğe eklerdik.

Bu yüzden önce diziden 100.000 rastgele sayı seçiyoruz . Yavaş olabilecek rastgele erişimleri önlemek için 250 ardışık sayıdan oluşan 400 rastgele grup ekliyoruz. Bu rasgele seçim ile, kalan sayıların çok azının ilk yüzde olduğundan emin olabiliriz, bu yüzden yürütme süresi bir milyar sayıyı bir miktar maksimum değerle karşılaştıran basit bir döngüye çok yakın olacaktır.


1

Bir milyar sayının ilk 100'ünü bulmak en iyi 100 elementin yığınını kullanarak yapılır .

Önce min yığınını karşılaşılan ilk 100 sayı ile doldurun. min-heap ilk 100 sayının en küçüğünü kökte (üstte) saklar.

Şimdi sayıların geri kalanı boyunca gittikçe onları sadece kök ile karşılaştırın (100'ün en küçüğü).

Karşılaşılan yeni sayı min-yığın kökünden büyükse, kök ile bu sayıyı değiştirin, aksi takdirde yoksayın.

Yeni sayının min-yığın içine eklenmesinin bir parçası olarak, yığın içindeki en küçük sayı üste (kök) gelecektir.

Tüm sayıları inceledikten sonra, min yığınında en büyük 100 sayıya sahip olacağız.


0

Herkesin ilgilenmesi durumunda Python'da basit bir çözüm yazdım. bisectModülü ve düzenli olarak sakladığı geçici bir dönüş listesini kullanır . Bu, öncelik kuyruğu uygulamasına benzer.

import bisect

def kLargest(A, k):
    '''returns list of k largest integers in A'''
    ret = []
    for i, a in enumerate(A):
        # For first k elements, simply construct sorted temp list
        # It is treated similarly to a priority queue
        if i < k:
            bisect.insort(ret, a) # properly inserts a into sorted list ret
        # Iterate over rest of array
        # Replace and update return array when more optimal element is found
        else:
            if a > ret[0]:
                del ret[0] # pop min element off queue
                bisect.insort(ret, a) # properly inserts a into sorted list ret
    return ret

Sıralı bir liste olan 100.000.000 eleman ve en kötü girişle kullanım:

>>> from so import kLargest
>>> kLargest(range(100000000), 100)
[99999900, 99999901, 99999902, 99999903, 99999904, 99999905, 99999906, 99999907,
 99999908, 99999909, 99999910, 99999911, 99999912, 99999913, 99999914, 99999915,
 99999916, 99999917, 99999918, 99999919, 99999920, 99999921, 99999922, 99999923,
 99999924, 99999925, 99999926, 99999927, 99999928, 99999929, 99999930, 99999931,
 99999932, 99999933, 99999934, 99999935, 99999936, 99999937, 99999938, 99999939,
 99999940, 99999941, 99999942, 99999943, 99999944, 99999945, 99999946, 99999947,
 99999948, 99999949, 99999950, 99999951, 99999952, 99999953, 99999954, 99999955,
 99999956, 99999957, 99999958, 99999959, 99999960, 99999961, 99999962, 99999963,
 99999964, 99999965, 99999966, 99999967, 99999968, 99999969, 99999970, 99999971,
 99999972, 99999973, 99999974, 99999975, 99999976, 99999977, 99999978, 99999979,
 99999980, 99999981, 99999982, 99999983, 99999984, 99999985, 99999986, 99999987,
 99999988, 99999989, 99999990, 99999991, 99999992, 99999993, 99999994, 99999995,
 99999996, 99999997, 99999998, 99999999]

Bunu 100.000.000 element için hesaplamak yaklaşık 40 saniye sürdü, bu yüzden 1 milyar için yapmaktan korkuyorum. Gerçi dürüst olmak gerekirse, ben en kötü durum giriş (ironik bir şekilde zaten sıralanmış bir dizi) besleme.


0

Çok fazla O (N) tartışma görüyorum, bu yüzden sadece düşünce alıştırması için farklı bir şey öneriyorum.

Bu sayıların doğası hakkında bilinen herhangi bir bilgi var mı? Doğada rastgele ise, daha fazla gitmeyin ve diğer cevaplara bakın. Onlardan daha iyi sonuç alamazsınız.

Ancak! Liste listeleme mekanizmasının bu listeyi belirli bir sırada doldurup doldurmadığına bakın. En büyük sayıların listenin belirli bir bölgesinde veya belirli bir aralıkta bulunacağını kesin olarak bildiğiniz iyi tanımlanmış bir kalıpta mı? Bir desen olabilir. Öyleyse, örneğin, ortadaki karakteristik kamburla bir çeşit normal dağılımda olmaları garanti edilirse, her zaman tanımlanmış alt kümeler arasında yukarı doğru eğilimleri tekrarlayın, verilerin ortasında bir süre T'de uzun süreli bir artış yapın belki de içeriden öğrenenlerin ticareti veya ekipman arızası insidansı gibi, ya da bir felaketten sonraki kuvvetlerin analizinde olduğu gibi her Nth numarasının bir "başak" olması, önemli ölçüde kontrol etmeniz gereken kayıt sayısını azaltabilirsiniz.

Zaten düşünce için yiyecek var. Belki bu, gelecekteki görüşmecilere düşünceli bir cevap vermenize yardımcı olacaktır. Birisi bana böyle bir soruna cevap olarak böyle bir soru sorsaydı etkilenirim biliyorum - bana optimizasyonu düşündüklerini söylerdi. Her zaman optimize etme olasılığının olmayabileceğini unutmayın.


0
Time ~ O(100 * N)
Space ~ O(100 + N)
  1. 100 boş alandan oluşan boş bir liste oluşturun

  2. Giriş listesindeki her numara için:

    • Sayı birinciden küçükse, atla

    • Aksi takdirde bu numarayla değiştirin

    • Ardından, numarayı bitişik takastan geçirin; bir sonrakinden daha küçük olana kadar

  3. Listeyi iade et


Not: Eğer log(input-list.size) + c < 100, o zaman en uygun yol giriş listesini sıralamaksa, ilk 100 öğeyi bölün.


0

Karmaşıklık O (N)

İlk önce 100 ints'lık bir dizi oluşturun, bu dizinin ilk öğesini N değerlerinin ilk öğesi olarak oluşturun, geçerli öğenin dizinini başka bir değişkenle takip edin, CurrentBig olarak adlandırın

N değerleri üzerinden yineleme

if N[i] > M[CurrentBig] {

M[CurrentBig]=N[i]; ( overwrite the current value with the newly found larger number)

CurrentBig++;      ( go to the next position in the M array)

CurrentBig %= 100; ( modulo arithmetic saves you from using lists/hashes etc.)

M[CurrentBig]=N[i];    ( pick up the current value again to use it for the next Iteration of the N array)

} 

bittiğinde, M dizisini CurrentBig'den 100 kere modulo 100 :-) yazdırın :-) Öğrenci için: kodun son satırının kod çıkmadan hemen önce geçerli verileri kesmediğinden emin olun


0

Başka bir O (n) algoritması -

Algoritma ortadan kaldırılarak en büyük 100'ü bulur

ikili gösterimlerindeki tüm milyon sayılarını göz önünde bulundurun. En önemli parçadan başlayın. MSB'nin 1 olup olmadığını bulmak, uygun bir sayı ile bir boole işlemi çarpımı ile yapılabilir. Bu milyonda 100'den fazla 1 varsa, diğer sayıları sıfırlarla ortadan kaldırın. Şimdi kalan sayılar bir sonraki en önemli bit ile devam ediyor. eleme işleminden sonra kalan sayıların sayısını tutun ve bu sayı 100'den büyük olduğu sürece devam edin.

Büyük boole işlemi GPU'larda paralel olarak yapılabilir


0

Bir diziye bir milyar numara koymak ve onu kovmak için kimin zamanı olduğunu öğrenirdim. Hükümet için çalışmalı. En azından bağlantılı bir listeniz olsaydı, yer açmak için yarım milyar hareket etmeden ortaya bir sayı ekleyebilirsiniz. Daha da iyisi bir Btree ikili aramaya izin verir. Her karşılaştırma toplamınızın yarısını ortadan kaldırır. Bir karma algoritma veri yapısını bir dama tahtası gibi doldurmanıza izin verir, ancak seyrek veriler için o kadar iyi değildir. En iyi bahis 100 tamsayıdan oluşan bir çözüm dizisine sahip olmak ve çözüm dizinizdeki en düşük sayıyı takip etmek, böylece orijinal dizide daha yüksek bir sayı ile karşılaştığınızda değiştirebilirsiniz. Başlangıçta sıralanmadığı varsayılarak orijinal dizideki her öğeye bakmanız gerekir.


0

Sen bunu yapabilir O(n)zaman. Sadece listeyi tekrarlayın ve herhangi bir noktada gördüğünüz en büyük 100 sayıyı ve bu gruptaki minimum değeri takip edin. On numaranızın en küçüğünden daha büyük yeni bir sayı bulduğunuzda, onu değiştirin ve 100'ün yeni min değerini güncelleyin (bunu her yaptığınızda bunu belirlemek için 100'lük sabit bir süre gerekebilir, ancak bu genel analizi etkilemez ).


1
Bu yaklaşım, bu sorunun hem en çok hem de en çok dile getirilen cevaplarıyla hemen hemen aynıdır.
Bernhard Barker

0

Ayrı bir listeyi yönetmek fazladan bir iştir ve her yeni değişiklik bulduğunuzda bir şeyleri tüm listede taşımanız gerekir. Sadece qsort ve ilk 100 al.


-1 quicksort, OP'nin tam olarak yaptığı ve geliştirmesini istediği O (n log n) 'dir. Ayrı bir listeyi yönetmeniz gerekmez, sadece 100 numaradan oluşan bir liste. Öneriniz ayrıca, orijinal listeyi değiştirme veya kopyalamanın istenmeyen yan etkisine de sahiptir. Bu 4GiB ya da daha fazla hafıza, gitti.

0
  1. 100. elemanı O (n) almak için n. Elemanı kullanın
  2. İkinci kez sadece bir kez yineleyin ve bu özel öğeden daha büyük olan her öğenin çıktısını alın.

Lütfen esp. ikinci adım paralel olarak hesaplamak kolay olabilir! Ayrıca, bir milyon en büyük elemana ihtiyaç duyduğunuzda da verimli olacaktır.


0

Bu, Google'dan veya başka bir endüstri devinden gelen bir soru. Belki de aşağıdaki kod görüşmeci tarafından beklenen doğru cevaptır. Zaman maliyeti ve alan maliyeti giriş dizisindeki maksimum sayıya bağlıdır. 32 Bit int dizi girişi için, maksimum alan maliyeti 4 * 125M Bayt, Zaman maliyeti 5 * Milyar.

public class TopNumber {
    public static void main(String[] args) {
        final int input[] = {2389,8922,3382,6982,5231,8934
                            ,4322,7922,6892,5224,4829,3829
                            ,6892,6872,4682,6723,8923,3492};
        //One int(4 bytes) hold 32 = 2^5 value,
        //About 4 * 125M Bytes
        //int sort[] = new int[1 << (32 - 5)];
        //Allocate small array for local test
        int sort[] = new int[1000];
        //Set all bit to 0
        for(int index = 0; index < sort.length; index++){
            sort[index] = 0;
        }
        for(int number : input){
            sort[number >>> 5] |= (1 << (number % 32));
        }
        int topNum = 0;
        outer:
        for(int index = sort.length - 1; index >= 0; index--){
            if(0 != sort[index]){
                for(int bit = 31; bit >= 0; bit--){
                    if(0 != (sort[index] & (1 << bit))){
                        System.out.println((index << 5) + bit);
                        topNum++;
                        if(topNum >= 3){
                            break outer;
                        }
                    }
                }
            }
        }
    }
}

0

kendi kodumu yaptım, emin değilim ne "görüşmeci" arıyor

private static final int MAX=100;
 PriorityQueue<Integer> queue = new PriorityQueue<>(MAX);
        queue.add(array[0]);
        for (int i=1;i<array.length;i++)
        {

            if(queue.peek()<array[i])
            {
                if(queue.size() >=MAX)
                {
                    queue.poll();
                }
                queue.add(array[i]);

            }

        }

0

Olası iyileştirmeler.

Dosya 1 milyar numara içeriyorsa, okumak gerçekten uzun olabilir ...

Bu çalışmayı geliştirmek için şunları yapabilirsiniz:

  • Dosyayı n parçaya ayırın, n iş parçacığı oluşturun, n iş parçacığının her birinin dosyalarındaki en büyük 100 numaraya bakmasını sağlayın (öncelik sırasını kullanarak) ve son olarak tüm iş parçacıklarının en büyük 100 sayısını alın.
  • Hadoop gibi bir çözümle böyle bir görev yapmak için bir küme kullanın. Burada dosyayı daha da bölebilir ve çıktıyı 1 milyar (veya 10 ^ 12) sayı dosyası için daha hızlı yapabilirsiniz.

0

İlk olarak 1000 element alın ve bunları maksimum yığın halinde ekleyin. Şimdi ilk maksimum 100 elemanı çıkarın ve bir yerde saklayın. Şimdi dosyadan sonraki 900 öğeyi seçin ve bunları en son 100 öğeyle birlikte öbeğe ekleyin.

Yığından 100 öğe toplama ve dosyadan 900 öğe ekleme işlemini tekrarlamaya devam edin.

100 elementin son seçimi bize bir milyar rakamdan maksimum 100 element verecek.


-1

Sorun: n öğesinde m >> öğesinin en büyük öğelerini bulun

Herkes için açık olması gereken en basit çözüm, kabarcık sıralama algoritmasının m geçişlerini yapmaktır.

sonra dizinin son n öğesini yazdırın.

Bu herhangi bir dış veri yapısı gerektirmez ve herkesin bildiği bir algoritma kullanır.

Çalışma süresi tahmini O (m * n) 'dir. Şimdiye kadar en iyi cevaplar O (n log (m)) 'dir, bu nedenle bu çözüm küçük m için önemli ölçüde daha pahalı değildir.

Bunun geliştirilemeyeceğini söylemiyorum, ama bu şimdiye kadarki en basit çözüm.


1
Harici veri yapısı yok mu? Sıralanacak milyar sayı dizisi ne olacak? Bu boyuttaki bir dizi, hem doldurmak hem de depolamak için büyük bir yüktür. Tüm "büyük" sayılar dizinin yanlış ucundaysa ne olur? Onları pozisyona " patlatmak " için 100 milyar swap emrinde ihtiyacınız olacak - başka bir büyük ek yük ... Son olarak, M N = 100 milyar vs M Log2 (N) = 6.64 milyar, ki bu neredeyse iki büyüklük farkı. Belki bunu yeniden düşün. En büyük sayıların veri yapısını korurken tek geçişli tarama bu yaklaşımı önemli ölçüde gerçekleştirecektir.
NealB
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.