En iyi 10 arama terimini bulmak için algoritma


115

Şu anda bir röportaj için hazırlanıyorum ve bu bana daha önceki bir röportajda sorulan ve bunun gibi bir soruyu hatırlattı:

"Google'da sürekli olarak en iyi 10 arama terimini görüntülemek için bazı yazılımlar tasarlamanız istendi. Şu anda Google'da aranmakta olan sonsuz gerçek zamanlı arama terimleri akışı sağlayan bir beslemeye erişiminiz var. Hangi algoritmayı ve veri yapılarını açıklayın bunu uygulamak için kullanırsınız. İki varyasyon tasarlayacaksınız:

(i) Tüm zamanların en iyi 10 arama terimini görüntüleyin (yani, beslemeyi okumaya başladığınızdan beri).

(ii) Saatlik olarak güncellenen, geçen ay için yalnızca en iyi 10 arama terimini görüntüleyin.

En iyi 10 listeyi elde etmek için bir tahmin kullanabilirsiniz, ancak seçimlerinizi gerekçelendirmelisiniz. "
Bu röportajda bombaladım ve hala bunu nasıl uygulayacağıma dair hiçbir fikrim yok.

İlk bölüm, sonsuz bir listenin sürekli büyüyen bir alt dizisindeki en sık 10 öğeyi soruyor. Seçim algoritmalarına baktım, ancak bu sorunu çözmek için herhangi bir çevrimiçi sürüm bulamadım.

İkinci bölüm sonlu bir liste kullanır, ancak işlenen büyük miktarda veri nedeniyle, tüm arama terimlerini hafızada saklayamaz ve her saat bir histogramı hesaplayamazsınız.

Sorun, ilk 10 listesinin sürekli olarak güncellenmesi gerçeğiyle daha da zorlaşıyor, bu nedenle bir şekilde kayan bir pencere üzerinden ilk 10'unuzu hesaplamanız gerekiyor.

Herhangi bir fikir?


11
@BlueRaja - Aptalca bir röportaj sorusu değil, OP açısından kötü bir yorum. Sonsuz bir listedeki en sık öğeleri istemiyor, sonsuz bir listenin sonlu bir alt dizisinin en sık öğelerini istiyor. Benzetmenize devam etmek what is the most frequent item in the subsequence [2; 2; 3; 3; 3; 4; 4; 4; 4; 5; 5] of your sequence?
gerekirse

3
@BlueRaja - Kesinlikle zor bir soru, ama neden aptalca olduğunu anlamıyorum - büyük veri kümelerine sahip şirketlerin karşılaştığı oldukça tipik bir sorunun temsilcisi gibi görünüyor. @IVlad - Önerinize göre düzeltildi, benim açımdan kötü ifadeler!
del

Yanıtlar:


47

Şey, tüm frekansları depolamanın belki de çok yüksek bir maliyeti olan çok fazla veri gibi görünüyor. Veri miktarı, hepsini saklayamayacak kadar büyük olduğunda, veri akışı algoritmalarının alanına gireriz .

Bu alandaki faydalı kitap: Muthukrishnan - "Veri Akışları: Algoritmalar ve Uygulamalar"

Yukarıdan seçtiğim, eldeki soruna yakından ilişkili referans: Manku, Motwani - "Veri Akışları Üzerindeki Yaklaşık Frekans Sayımları" [pdf]

Bu arada, Stanford'dan Motwani (değiştir) çok önemli "Randomized Algorithms" kitabının yazarıydı . Bu kitabın 11. bölümü bu sorunu ele almaktadır . Düzenleme: Üzgünüz, kötü referans, o bölüm farklı bir problemle ilgili. Kontrol ettikten sonra, bunun yerine Muthukrishnan'ın kitabının çevrimiçi olarak ulaşılabilen 5.1.2 bölümünü öneriyorum .

Heh, güzel röportaj sorusu.


2
+1 Çok ilginç şeyler, sitelerde şeyleri "okumak" için etiketlemenin bir yolu olmalı. Paylaşım için teşekkürler.
Ramadheer Singh

@Gollum: Yer imlerimde okunacak bir klasörüm var; sadece bunu yapabilirsin. Bu bağlantıların benimkine eklendiğini biliyorum :)
Cam

+1. Akış algoritmaları burada tam olarak konu ve Muthu'nun kitabı (şu ana kadar yazılmış tek kitap olan AFAIK) harika.
ShreevatsaR

1
+1. İlgili: en.wikipedia.org/wiki/Online_algorithm . Btw Motwani yüzden belki, yakın zamanda ölen oldu bir yazar daha doğrudur.

Çok ilginç. Onu kitaptan tanıyordum, ancak bundan dolayı kesinlikle daha ünlü olmuş olmalı: "Motwani, PageRank algoritması üzerine etkili bir ilk makalenin ortak yazarlarından biriydi (Larry Page ve Sergey Brin ve Terry Winograd ile birlikte), Google'ın arama tekniklerinin temeli. "( en.wikipedia.org/wiki/Rajeev_Motwani )
Dimitris Andreou

55

Frekans Tahminine Genel Bakış

Sabit miktarda depolama kullanarak böyle bir akış için frekans tahminleri sağlayabilen bazı iyi bilinen algoritmalar vardır. Biri , sık Misra ve Gries (1982). N öğelik bir listeden, k - 1 sayaçlarını kullanarak n / k defadan fazla gerçekleşen tüm öğeleri bulur . Bu Boyer ve Moore'un Çoğunluk algoritmasının (Fischer-Salzberg, 1982) bir genellemesidir , burada k 2'dir. Manku ve Motwani'nin LossyCounting (2002) ve Metwally'nin SpaceSaving (2005) algoritmaları benzer alan gereksinimlerine sahiptir, ancak belirli koşullar altında daha doğru tahminler sağlayabilirler. koşullar.

Unutulmaması gereken önemli şey, bu algoritmaların yalnızca frekans tahminleri sağlayabilmesidir. Spesifik olarak, Misra-Gries tahmini gerçek frekansı (n / k) maddelere göre eksik sayabilir .

Bir öğeyi ancak zamanın% 50'sinden fazlasında meydana gelirse pozitif olarak tanımlayabilen bir algoritmanız olduğunu varsayalım . Bu algoritma, bir akım beslemesi , N başka farklı öğeler, ve daha sonra ilave N - 1 , bir ürünün, bir kopyasını x , toplam 2N - 1 ürün. Algoritma size x'in toplamın% 50'sini aştığını söylerse , ilk akışta olması gerekir; değilse, x ilk akışta değildi. Algoritmanın bu belirlemeyi yapabilmesi için, ilk akışı (veya uzunluğuyla orantılı bir özeti) saklaması gerekir! Böylece, böyle bir "kesin" algoritmanın gerektirdiği alanın Ω ( N ) olacağını kendimize kanıtlayabiliriz .

Bunun yerine, burada açıklanan bu sıklık algoritmaları, eşiği aşan herhangi bir öğeyi ve belirli bir marjla altına düşen bazı öğeleri tanımlayan bir tahmin sağlar. Örneğin , tek bir sayaç kullanan Çoğunluk algoritması her zaman bir sonuç verecektir; herhangi bir öğe akışın% 50'sini aşarsa bulunacaktır. Ancak size yalnızca bir kez oluşan bir öğe de verebilir. Veriler üzerinde ikinci bir geçiş yapmadan bilemezsiniz (yine tek bir sayaç kullanarak, ancak yalnızca o öğeyi ararsanız).

Sık Algoritma

Misra-Gries'in Sık kullanılan algoritmasının basit bir açıklaması . Demaine (2002) ve diğerleri algoritmayı optimize ettiler, ancak bu size ana fikri veriyor.

1 / k eşik oranını belirtin ; n / k defadan fazla ortaya çıkan herhangi bir öğe bulunacaktır. Boş bir harita oluşturun (kırmızı-siyah bir ağaç gibi); anahtarlar arama terimleri olacak ve değerler o terim için bir sayaç olacaktır.

  1. Akıştaki her bir öğeye bakın.
  2. Terim haritada mevcutsa, ilgili sayacı artırın.
  3. Aksi takdirde, harita k - 1 girişinden azsa , bir sayacı ile terimi haritaya ekleyin.
  4. Bununla birlikte, haritada zaten k - 1 girişi varsa, her girişte sayacı azaltın. Bu işlem sırasında herhangi bir sayaç sıfıra ulaşırsa, onu haritadan kaldırın.

Sınırsız miktarda veriyi sabit bir depolama miktarıyla (yalnızca sabit boyutlu harita) işleyebileceğinizi unutmayın. Gereken depolama miktarı yalnızca ilgili eşiğe bağlıdır ve akışın boyutu önemli değildir.

Aramaları Sayma

Bu bağlamda, belki bir saatlik aramaları arabelleğe alıyorsunuz ve bu işlemi o saatlik veriler üzerinde gerçekleştiriyorsunuz. Bu saatin arama günlüğünden ikinci bir geçiş yapabilirseniz, ilk geçişte belirlenen en iyi "adayların" gerçekleşme sayısını tam olarak alabilirsiniz. Ya da belki tek bir geçiş yapmak ve tüm adayları rapor etmek, orada olması gereken herhangi bir öğenin dahil olduğunu ve herhangi bir ekstranın önümüzdeki bir saat içinde kaybolacak gürültülerden ibaret olduğunu bilerek sorun olabilir.

İlgi eşiğini gerçekten aşan adaylar özet olarak saklanır. Her saatin en eskisini atarak bu özetleri bir ay boyunca saklayın ve en yaygın arama terimlerinin iyi bir tahminine sahip olursunuz.


Bu çözümün, ilgilendiğiniz arama terimlerinin sayısını azaltarak bir filtre görevi görebileceğine inanıyorum. Bir terim haritaya girerse, haritadan düşse bile gerçek istatistiklerini izlemeye başlayın. Daha sonra verilerdeki ikinci geçişi atlayabilir ve topladığınız sınırlı istatistiklerden sıralı bir ilk 10 elde edebilirsiniz.
Dolph

Sayaçları azaltarak daha az aranan terimleri ağaçtan budamanın zarif yolunu seviyorum. Ancak harita "dolu" olduğunda, bu, gelen her yeni arama terimi için bir azaltma adımı gerektirmez mi? Ve bu bir kez olmaya başladığında, bu, daha yeni arama terimlerinin, sayaçlarının yeterince artma şansı olmadan önce haritadan hızla kaldırılmasına neden olmaz mı?
del

1
@del - Bu algoritmanın belirli bir eşik sıklığını aşan terimleri bulmak için olduğunu, en yaygın terimleri bulmak için gerekli olmadığını unutmayın. En yaygın terimler belirtilen eşiğin altına düşerse, genellikle bulunmazlar. Yeni terimleri "çok hızlı" kaldırmakla ilgili endişeniz bu vakayla ilişkili olabilir. Buna bakmanın bir yolu, popülaritede gerçek "sinyaller" vardır, bunlar "gürültüden" fark edilir şekilde sıyrılacaklardır. Ancak bazen, bulunacak hiçbir sinyal yoktur, sadece rastgele arama durağan.
erickson

@erickson - Doğru - anladığım şey, bu algoritmadaki varsayımın, ilk 10 kelimenin ölçüm penceresi boyunca eşit olarak dağıtılmasıdır. Ancak ölçüm penceresini yeterince küçük tuttuğunuz sürece (örneğin 1 saat), bu muhtemelen geçerli bir varsayım olacaktır.
del

1
@erickson, tek tip bir dağıtım bir gereklilik olmasa da, bunun daha gerçekçi bir dağıtımda (güç yasası, Zipf) nasıl çalışacağını merak ediyorum. N farklı kelimemiz olduğunu varsayalım ve en sık kullanılan K terimleriyle sonuçlanacağını umarak, K kapasiteli kırmızı-siyah ağacı tutalım. (N - K) kelimelerin kümülatif sıklığı, en sık kullanılan K kelimelerin kümülatif sıklığından daha büyükse, sondaki ağacın çöp içermesi garanti edilir. Katılıyor musun?
Dimitris Andreou

19

Bu, şu anda yaşadığım araştırma projelerinden biri. Gereksinim neredeyse tam olarak sizinkiyle aynı ve sorunu çözmek için güzel algoritmalar geliştirdik.

Girdi

Giriş, sonsuz bir İngilizce kelime veya kelime öbeği akışıdır (bunlara şu şekilde değiniyoruz tokens).

Çıktı

  1. Şimdiye kadar gördüğümüz en iyi N jetonu çıkarın (gördüğümüz tüm jetonlardan!)
  2. Geçmiş bir pencerede, örneğin son gün veya geçen hafta ilk N jetonu çıkarın.

Bu araştırmanın bir uygulaması, Twitter veya Facebook'ta sıcak konu veya konu trendlerini bulmaktır. Web sitesinde gezinen ve sisteme beslenecek bir kelime akışı oluşturan bir tarayıcımız var. Sistem daha sonra genel veya tarihsel olarak en yüksek frekanslı kelimeleri veya cümleleri çıkaracaktır. Son birkaç hafta içinde "Dünya Kupası" ifadesinin Twitter'da birçok kez görüneceğini düşünün. "Ahtapot Paul" da öyle. :)

Tamsayılara Dize

Sistemin her kelime için bir tamsayı kimliği vardır. İnternette neredeyse sonsuz olası kelime olmasına rağmen, çok sayıda kelime biriktirdikten sonra, yeni kelimeler bulma olasılığı gittikçe azalır. Şimdiden 4 milyon farklı kelime bulduk ve her birine benzersiz bir kimlik verdik. Tüm bu veri seti, kabaca 300MB bellek tüketen bir karma tablo olarak belleğe yüklenebilir. (Kendi hash tablomuzu uyguladık. Java'nın uygulaması büyük bellek yükü gerektirir)

Her cümle daha sonra bir tamsayı dizisi olarak tanımlanabilir.

Bu önemlidir, çünkü tam sayılarda sıralama ve karşılaştırmalar çok daha hızlıdır dizelerden .

Verileri Arşivle

Sistem, her belirteç için arşiv verilerini tutar. Temelde çiftleri(Token, Frequency) . Ancak, verileri depolayan tablo o kadar büyük olur ki, tabloyu fiziksel olarak bölümlere ayırmamız gerekir. Bölme şeması, jetonun ngramlarına dayandığında. Jeton tek bir kelimeyse, 1 gramdır. Belirteç iki kelimeli bir ifade ise, 2 gramdır. Ve bu devam ediyor. Kabaca 4 gramda 1 milyar kaydımız var ve tablo boyutu 60 GB civarında.

Gelen Akışları İşleme

Sistem, bellek tamamen kullanılıncaya kadar gelen cümleleri absorbe edecektir (Ya, bir MemoryManager'a ihtiyacımız var). N cümleyi alıp belleğe kaydettikten sonra, sistem duraklar ve her cümleyi sözcüklere ve cümlelere dönüştürmeye başlar. Her simge (kelime veya kelime öbeği) sayılır.

Çok sık kullanılan belirteçler için her zaman bellekte tutulurlar. Daha az sıklıkta belirteçler için, kimliklere göre sıralanırlar (Dizeyi bir tamsayı dizisine çevirdiğimizi unutmayın) ve bir disk dosyasına serileştirilir.

(Bununla birlikte, probleminiz için, sadece kelimeleri saydığınız için, tüm kelime-frekans haritasını sadece hafızaya koyabilirsiniz. Dikkatlice tasarlanmış bir veri yapısı, 4 milyon farklı kelime için sadece 300MB hafıza kullanır. Bazı ipucu: ASCII karakterini kullanarak Dizeleri temsil eder) ve bu çok kabul edilebilir.

Bu arada, sistem tarafından oluşturulan herhangi bir disk dosyasını bulduğunda etkinleştirilen ve ardından onu birleştirmeye başlayan başka bir işlem olacaktır. Disk dosyası sıralandığından, birleştirme, birleştirme sıralaması gibi benzer bir işlem alacaktır. Çok fazla rastgele disk aramasından kaçınmak istediğimiz için burada da bazı tasarımlara dikkat edilmesi gerekiyor. Buradaki fikir, aynı anda okumayı (birleştirme işlemi) / yazmayı (sistem çıktısı) önlemek ve birleştirme işleminin farklı bir diske yazarken bir diskten okumasına izin vermektir. Bu, bir kilitleme uygulamaya benzer.

Günün sonu

Günün sonunda, sistem, bellekte depolanmış frekansı olan birçok sık simgeye ve birkaç disk dosyasında depolanan (ve her dosya sıralanır) diğer daha az sıklıkta belirteçlere sahip olacaktır.

Sistem, bellek içi haritayı bir disk dosyasına temizler (sıralayın). Şimdi, sorun bir dizi sıralı disk dosyasını birleştirmeye dönüşüyor. Benzer işlemi kullanarak, sonunda bir sıralı disk dosyası alırdık.

Ardından, son görev, sıralanan disk dosyasını arşiv veritabanına birleştirmektir. Arşiv veritabanının boyutuna bağlı olarak, algoritma yeterince büyükse aşağıdaki gibi çalışır:

   for each record in sorted disk file
        update archive database by increasing frequency
        if rowcount == 0 then put the record into a list
   end for

   for each record in the list of having rowcount == 0
        insert into archive database
   end for

Önsezi, bir süre sonra ekleme sayısının daha da azalacağıdır. Giderek daha fazla işlem yalnızca güncelleme ile ilgili olacaktır. Ve bu güncelleme endeks tarafından cezalandırılmayacaktır.

Umarım tüm bu açıklama yardımcı olur. :)


Ben anlamadım Kelimelerin tamsayı kimliklerinde ne tür anlamlı sıralama veya karşılaştırmalar yapılabilir? Sayılar keyfi değil mi?
Dimitris Andreou

Ayrıca, kelimelerin sıklıklarını sayma, Google'ın MapReduce belgesinde ( labs.google.com/papers/mapreduce.html ), onu birkaç satırda ölçeklenebilir bir şekilde çözen ilk örnektir . Hatta verilerinizi google app angine'e taşıyabilir ve böyle bir MapReduce ( code.google.com/p/appengine-mapreduce ) yapabilirsiniz
Dimitris Andreou,

@Dimitris Andreou: Tamsayılara göre sıralama dizelerde daha hızlı olur. Bunun nedeni, iki tam sayıyı karşılaştırmanın iki dizeyi karşılaştırmaktan daha hızlı olmasıdır.
SiLent SoNG

@Dimitris Andreou: Google'ın harita indirimi, bu sorunu çözmek için güzel bir dağıtılmış yaklaşım. Ah! Bağlantıları sağladığınız için teşekkürler. Ya, birden fazla makine kullanarak sıralama yapmak bizim için iyi olur. Güzel yaklaşım.
SiLent SoNG

@Dimitris Andreou: Şimdiye kadar sadece tek makineli sıralama yaklaşımını düşünüyordum. Dağıtımda sıralamak ne kadar güzel bir fikir.
SiLent SoNG

4

Kullanabilirsin İkili arama ağacıyla birleştirilmiş hash tablosu . <search term, count>Her arama teriminin kaç kez arandığını söyleyen bir sözlük uygulayın .

Açıkçası, ilk 10'u elde etmek için tüm hash tablosunu her saat yinelemek çok kötü. Ancak bahsettiğimiz bu google, yani ilk on tanesinin hepsinin 10.000'den fazla isabet alacağını varsayabilirsiniz (muhtemelen çok daha büyük bir sayıdır). Dolayısıyla, bir arama teriminin sayısı 10 000'i her aştığında, onu BST'ye ekleyin. Daha sonra her saat, sadece ilk 10'u BST'den almanız gerekir, bu da nispeten az sayıda giriş içermelidir.

Bu, tüm zamanların en iyi 10'u sorununu çözer.


Gerçekten zor olan kısım, bir terimin diğerinin aylık raporda yerini almasıdır (örneğin, "yığın taşması" son iki ayda 50.000 isabet alabilir, ancak geçen ay yalnızca 10.000, "amazon" ise 40 Son iki ay için 000, geçen ay için 30 000. Aylık raporunuzda "amazon" un "yığın taşması" ndan önce gelmesini istiyorsunuz). Bunu yapmak için, tüm ana (10 000'den fazla tüm zamanların araması) arama terimleri için, her gün o terimin kaç kez arandığını size söyleyen 30 günlük bir liste kaydederim. Liste bir FIFO kuyruğu gibi çalışır: ilk günü kaldırırsınız ve her gün (veya her saatte bir yenisini) eklersiniz, ancak daha sonra daha fazla bilgi depolamanız gerekebilir, bu da daha fazla bellek / alan anlamına gelir. Bellek sorun değilse aksi takdirde bu "yaklaşım" için gidin

Bu iyi bir başlangıç ​​gibi görünüyor. Daha sonra, 10 000'den fazla isabete sahip olan ancak uzun süredir çok fazla sonuç alamayan terimleri ve bunun gibi şeyleri kısaltmak konusunda endişelenebilirsiniz.


3

durum i)

Tüm arama terimleri için bir hashtable ve hashtable'dan ayrı olarak sıralanmış bir ilk on liste tutun. Bir arama gerçekleştiğinde, hashtable'daki uygun öğeyi artırın ve bu öğenin şimdi ilk on listedeki 10. öğe ile değiştirilip değiştirilmeyeceğini kontrol edin.

O (1) ilk on listeyi arar ve hashtable'a maksimum O (log (n)) ekleme (kendi kendini dengeleyen ikili ağaç tarafından yönetilen çarpışmalar varsayılarak).

durum ii) Büyük bir hashtable ve küçük bir liste tutmak yerine, bir hashtable ve tüm öğelerin sıralı bir listesini tutuyoruz. Bir arama yapıldığında, bu terim hashtable'da artırılır ve sıralanan listede, terim, kendisinden sonraki terimle geçiş yapıp yapmayacağını görmek için kontrol edilebilir. Kendi kendini dengeleyen bir ikili ağaç bunun için işe yarayabilir, çünkü onu hızlı bir şekilde sorgulayabilmemiz gerekir (bu konu hakkında daha sonra daha fazlası).

Ayrıca, bir FIFO listesi (sıra) biçiminde bir 'saat' listesi de tutuyoruz. Her 'saat' öğesi, o belirli saat içinde yapılan tüm aramaların bir listesini içerir. Örneğin, saat listemiz şöyle görünebilir:

Time: 0 hours
      -Search Terms:
          -free stuff: 56
          -funny pics: 321
          -stackoverflow: 1234
Time: 1 hour
      -Search Terms:
          -ebay: 12
          -funny pics: 1
          -stackoverflow: 522
          -BP sucks: 92

Ardından, her saat: Listede en az 720 saat varsa (bu, 30 gün içindeki saat sayısıdır), listedeki ilk öğeye bakın ve her arama terimi için, hashtable'daki öğeyi uygun miktarda azaltın . Daha sonra bu ilk saat öğesini listeden silin.

Diyelim ki 721 saatindeyiz ve listemizdeki ilk saate bakmaya hazırız (yukarıda). Hashtable'da ücretsiz şeyleri 56, komik resimleri 321 vb. Oranında azaltırız ve sonra 0 saatini listeden tamamen çıkarırız çünkü bir daha bakmamıza gerek kalmaz.

Hızlı sorgulara izin veren tüm terimlerin sıralı bir listesini tutmamızın nedeni, 720 saat önceki arama terimlerini geçerken her saat başı, ilk on listenin sıralı kalmasını sağlamamız gerektiğidir. Örneğin, hashtable'da 'ücretsiz şeyler'i 56 azalttığımızda, listenin şimdi nereye ait olduğunu kontrol ederiz. Kendi kendini dengeleyen ikili bir ağaç olduğu için, bunların tümü O (log (n)) zamanında güzelce gerçekleştirilebilir.


Düzenleme: Yer için doğruluktan ödün vermek ...

İkincisinde olduğu gibi birincisine de büyük bir liste uygulamak faydalı olabilir. Sonra her iki durumda da aşağıdaki alan optimizasyonunu uygulayabiliriz: Listedeki ilk x öğe hariç tümünü kaldırmak için bir cron işi çalıştırın . Bu, alan gereksinimini azaltacaktır (ve sonuç olarak listedeki sorguları daha hızlı hale getirecektir). Tabii ki, yaklaşık bir sonuçla sonuçlanır, ancak buna izin verilir. x , uygulama dağıtılmadan önce kullanılabilir belleğe göre hesaplanabilir ve daha fazla bellek kullanılabilir hale gelirse dinamik olarak ayarlanabilir.


2

Kaba düşünce ...

Tüm zamanların ilk 10'u için

  • Her terim için bir sayımın depolandığı bir karma toplama kullanma (terimleri sterilize etme vb.)
  • Devam eden ilk 10'u içeren sıralanmış bir dizi, bir terimin sayısı dizideki en küçük sayıya eşit veya ondan büyük olduğunda bu diziye eklenen bir terim / sayı

Her ay güncellenen ilk 10 saat için:

  • Modulo 744 başlangıcından bu yana geçen saat sayısına (bir aydaki saat sayısı) göre indekslenmiş bir dizi kullanarak, bu dizi girişleri, bu saat aralığında karşılaşılan her terim için bir sayımın depolandığı karma toplamadan oluşur. Saat dilimi sayacı her değiştiğinde bir giriş sıfırlanır
  • Saat diliminde indekslenen dizideki istatistiklerin, geçerli saat aralığı sayacı her değiştiğinde (en fazla saatte bir), saat dilimlerinde endekslenen bu dizinin içeriğini kopyalayıp düzleştirerek toplanması gerekir.

Errr ... mantıklı mı? Bunu gerçek hayatta yapacağım gibi düşünmedim

Ah evet, söylemeyi unuttum, aylık istatistikler için gereken saatlik "kopyalama / düzleştirme" aslında tüm zamanların ilk 10'u için kullanılan aynı kodu yeniden kullanabilir, bu hoş bir yan etki.


2

Kesin çözüm

İlk olarak, doğru sonuçları garanti eden ancak çok fazla bellek gerektiren bir çözüm (büyük bir harita).

"Tüm zamanlar" varyantı

Anahtar olarak sorgular ve değer olarak sayıları olan bir karma harita oluşturun. Ayrıca, şimdiye kadarki en sık 10 sorguyu ve en sık 10. sayının sayısını (eşik) bir liste tutun.

Sorgu akışı okundukça haritayı sürekli güncelleyin. Bir sayı geçerli eşiği her aştığında şunları yapın: 10. sorguyu "İlk 10" listesinden kaldırın, yeni güncellediğiniz sorguyla değiştirin ve eşiği de güncelleyin.

"Geçen ay" varyantı

Aynı "İlk 10" listesini koruyun ve yukarıdakiyle aynı şekilde güncelleyin. Ayrıca benzer bir harita tutun, ancak bu sefer 30 * 24 = 720 sayılık vektörleri (her saat için bir tane) değer olarak saklayın. Her saat, her anahtar için aşağıdakileri yapın: vektörden en eski sayacı kaldırın, sonuna yeni bir tane ekleyin (0'a başlatılmış). Vektör tamamen sıfırsa anahtarı haritadan kaldırın. Ayrıca her saat başı "İlk 10" listesini sıfırdan hesaplamanız gerekir.

Not: Evet, bu sefer bir yerine 720 tamsayı saklıyoruz, ancak çok daha az anahtar var (tüm zamanların varyantında gerçekten bir uzun bir kuyruğu var).

Yaklaşımlar

Bu yaklaşımlar doğru çözümü garanti etmez ancak daha az bellek tüketir.

  1. Gerisini atlayarak her N'inci sorguyu işleyin.
  2. (Yalnızca tüm zamanların varyantı için) Haritada en fazla M anahtar / değer çifti bulundurun (M, karşılayabileceğiniz kadar büyük olmalıdır). Bu bir tür LRU önbelleği: haritada olmayan bir sorguyu her okuduğunuzda, en az kullanılan sorguyu sayı 1 ile kaldırın ve o anda işlenen sorguyla değiştirin.

Yaklaşım 1'deki olasılıkçı yaklaşımı seviyorum. Ancak yaklaşık 2'yi (LRU önbelleği) kullanarak, çok popüler olmayan terimler başlangıçta daha sonra popüler hale gelirse ne olur? Sayıları çok düşük olacağından, her eklendiklerinde atılmazlar mı?
del

@del Haklısınız, ikinci yaklaşım yalnızca belirli sorgu akışları için işe yarayacaktır. Daha az güvenilirdir, ancak aynı zamanda daha az kaynak gerektirir. Not: Her iki yaklaşımı da birleştirebilirsiniz.
Bolo

2

Geçen ay en iyi 10 arama terimi

Bellek verimli indeksleme / veri yapısı, örneğin, kullanılarak sıkı bir şekilde istiflenmiş çalışır üzerinde Ara girişlerinden ( deneme terim sayısı -) yaklaşık bellek gereksinimleri ve n arasında bir ilişkiyi tanımlamaktadır.

Gerekli belleğin mevcut olması durumunda ( varsayım 1 ), aylık istatistiği tam olarak tutabilir ve her ay tüm zaman istatistiğinde toplayabilirsiniz.

Ayrıca burada 'geçen ayı' sabit pencere olarak yorumlayan bir varsayım var. Ancak aylık pencere kayıyor olsa bile, yukarıdaki prosedür ilkeyi gösterir (kayma, belirli boyuttaki sabit pencerelerle yaklaşık olarak tahmin edilebilir).

Bu bana sıralı bir veritabanını hatırlatıyor , rrd katılaşır süreler, ortalamaya ayrıntıları göz ardı ederek özetliyor veya maks / dk değerleri seçerken; tüm veriler korunur değil bir anlamda (bazı istatistik 'tüm zamanların' üzerinde hesaplandığını hariç belirli bir görevde kaybolan ayrıntı, düşük frekanslı öğeler hakkındaki bilgidir ve bu da hatalara neden olabilir).

Varsayım 1

Tüm ay boyunca mükemmel istatistikleri tutamazsak, o zaman mükemmel istatistiklere sahip olabileceğimiz belirli bir P periyodu bulabiliriz. Örneğin, bir P zaman periyodu için mükemmel istatistiklere sahip olduğumuzu varsayarsak, bu, n kez aya girer.
Mükemmel istatistikler işlevi tanımlar f(search_term) -> search_term_occurance.

Tüm nmükemmel istatistik tablolarını hafızada tutabilirsek , aylık istatistikler şu şekilde hesaplanabilir:

  • en yeni dönem için istatistikler ekleyin
  • en eski döneme ait istatistikleri kaldırın (bu yüzden nmükemmel istatistik tabloları tutmalıyız )

Ancak, toplu düzeyde (aylık) yalnızca ilk 10'u tutarsak, sabit dönemin tüm istatistiklerinden çok sayıda veriyi çıkarabileceğiz. Bu, bellek gereksinimlerini sabitleyen (P periyodu için mükemmel istatistik tablosunda üst sınır varsayılarak) zaten bir çalışma prosedürü verir.

Yukarıdaki prosedürle ilgili sorun, eğer kayan bir pencere için yalnızca en iyi 10 terim hakkında bilgi tutarsak (tüm zamanlar için benzer şekilde), o zaman istatistiklerin bir dönemde zirve yapan arama terimleri için doğru olacağı, ancak Zaman içinde sürekli olarak damlayan arama terimlerinin istatistikleri.

Bu, bilgileri ilk 10'dan fazla terimle, örneğin ilk 100 terimle, ilk 10'un doğru olacağını umarak dengeleyerek dengeleyebilir.

Daha fazla analizin, bir girişin istatistiklerin bir parçası olması için gereken minimum oluşum sayısını ilişkilendirebileceğini düşünüyorum (bu, maksimum hatayla ilgilidir).

(Hangi girdilerin istatistiklerin bir parçası olması gerektiğine karar verirken, eğilimler de izlenebilir ve izlenebilir; örneğin, her dönem için P oluşumlarının doğrusal bir ekstrapolasyonu, terimin bir veya iki ay içinde önemli hale geleceğini söylüyorsa zaten izlemeye başlayabilir. Benzer ilke, arama terimini izlenen havuzdan kaldırmak için de geçerlidir.)

Yukarıdakiler için en kötü durum, neredeyse eşit sıklıkta çok sayıda teriminiz olduğu ve bunların her zaman değiştiği durumdur (örneğin, yalnızca 100 terimi takip ediyorsanız, o zaman ilk 150 terim eşit sıklıkta geçiyorsa, ancak ilk 50, ilk ayda daha sık ve Çoğunlukla bir süre sonra istatistikler doğru bir şekilde tutulmayacaktır).

Ayrıca, bellek boyutuna sabitlenmemiş başka bir yaklaşım da olabilir (kesinlikle yukarıdakiler de değildir), bu yaklaşımlar / periyotlar (gün, ay, yıl, tüm zamanlar) açısından minimum önemi tanımlayabilir. istatistikleri. Bu, toplama sırasında istatistiklerin her birinde maksimum hatayı garanti edebilir (round robin'e tekrar bakın).


2

"Saat sayfası değiştirme algoritmasının" ("ikinci şans" olarak da bilinir) bir uyarlamasına ne dersiniz ? Arama istekleri eşit olarak dağıtılırsa çok iyi çalıştığını hayal edebiliyorum (bu, aranan terimlerin çoğunun arka arkaya 5 milyon kez yerine düzenli olarak göründüğü ve ardından bir daha asla görünmediği anlamına gelir).

İşte algoritmanın görsel bir temsili: saat sayfası değiştirme algoritması


0

Arama terimlerinin sayısını, her yeni aramanın belirli bir öğenin bir artırılmasına neden olduğu dev bir karma tabloda saklayın. İlk 20 veya daha fazla arama terimini takip edin; 11. sıradaki öğe artırıldığında, # 10 * ile konum değiştirmesi gerekip gerekmediğini kontrol edin (ilk 10'u sıralı tutmak gerekli değildir; tüm ilgilendiğiniz, 10. ve 11. arasındaki farkı çizmektir).

* Yeni bir arama teriminin 11. sırada olup olmadığını görmek için benzer kontrollerin yapılması gerekiyor, bu nedenle bu algoritma diğer arama terimlerine de yansıyor - bu yüzden biraz basitleştiriyorum.


Hash tablonuzun boyutunu sınırlamak isteyeceksiniz. Ya bir benzersiz arama akışı alırsanız? Düzenli olarak ancak seyrek olarak aranan bir terimi fark etmekten kendinizi alıkoymadığınızdan emin olmalısınız. Zamanla, özellikle diğer tüm arama terimleri "güncel olaylar" ise, yani şu anda çok arandıysa, ancak gelecek hafta çok fazla arandıysa, bu en iyi arama terimi olabilir. Aslında, bunlar gibi düşünceler yapmak isteyeceğiniz tahminler olabilir. Bunları, bu tür şeyleri yakalayamayacağımızı söyleyerek gerekçelendirin çünkü bunu yapmak algoritmayı çok daha fazla zaman / alan pahalı hale getirir.
cape1232

Google'ın her şeyin bir sayısına sahip olduğundan oldukça eminim - bazı sayılar statik olarak korunmaz, bunun yerine gerektiği gibi hesaplanır.
Ether

0

bazen en iyi cevap "bilmiyorum" olur.

Daha derin bir bıçak alacağım. İlk içgüdülerim, sonuçları bir Soru'ya beslemek olurdu. Bir süreç, Q'ya gelen öğeleri sürekli olarak işler. Süreç,

terim -> sayım

Bir Q öğesi her işlendiğinde, sadece arama terimine bakarsınız ve sayımı artırırsınız.

Aynı zamanda, haritadaki ilk 10 girişe ilişkin bir referans listesi tutardım.

Halihazırda uygulanan giriş için, sayısının ilk 10'daki en küçük girişin sayısından daha büyük olup olmadığına bakın (zaten listede yoksa). Eğer öyleyse, en küçüğü girişle değiştirin.

Bunun işe yarayacağını düşünüyorum. Hiçbir işlem zaman yoğun değildir. Sayım haritasının boyutunu yönetmenin bir yolunu bulmanız gerekir. ama bu bir röportaj cevabı için yeterince iyi olmalı.

Düşünüp düşünemeyeceğinizi görmek isteyen bir çözüm beklemiyorlar. Çözümü o anda ve orada yazmak zorunda değilsin ....


12
Veri yapısı bir denir queue, Q:) bir mektup.
IVlad

3
Eğer röportajı ben yapıyor olsaydım, "<stop> bilmiyorum" kesinlikle en iyi cevap olmazdı. Ayaklarınızın üzerinde düşünün. Bilmiyorsan çöz ya da en azından dene.
Stephen

röportajlarda, 7 sayfalık özgeçmişinde 5 kez hazırda bekletilen birisini gördüğümde ve bana ORM'nin ne olduğunu söyleyemediğinde, görüşmeyi hemen bitiriyorum. Özgeçmişlerine koymamalarını ve sadece "bilmiyorum" demelerini tercih ederim. Kimse her şeyi bilmiyor. @IVIad, bir C geliştiricisi olduğumu iddia ediyordum ve bitleri kurtarmaya çalışıyordum ...;)
hvgotcodes

0

Bunun bir yolu, her aramada o arama terimini ve zaman damgasını kaydetmenizdir. Bu şekilde, herhangi bir dönem için ilk on'u bulmak, yalnızca belirli bir dönem içindeki tüm arama terimlerini karşılaştırmaktan ibarettir.

Algoritma basittir, ancak dezavantajı daha fazla bellek ve zaman tüketimi olacaktır.


0

10 düğümlü bir Yayılma Ağacı kullanmaya ne dersiniz ? Ağaçta bulunmayan bir değere (arama terimi) her erişmeye çalıştığınızda, herhangi bir yaprağı atın, onun yerine değeri girin ve ona erişin.

Bunun arkasındaki fikir diğer cevabımla aynı . Arama terimlerine eşit / düzenli olarak erişildiği varsayımı altında bu çözüm çok iyi performans göstermelidir.

Düzenle

Çok yakında tekrar erişilebilecek bir düğümü silmemek için ağaçta birkaç arama terimi daha saklanabilir (aynısı diğer cevabımda önerdiğim çözüm için de geçerlidir). İçinde ne kadar çok değer depolanırsa sonuçlar o kadar iyi olur.


0

Doğru anlasam da anlamasam da Dunno. Benim çözümüm yığın kullanıyor. İlk 10 arama öğesi nedeniyle, 10 boyutunda bir yığın oluşturuyorum. Ardından bu yığını yeni aramayla güncelleyin. Yeni bir aramanın frekansı öbek (Max Heap) tepesinden büyükse, güncelleyin. En küçük frekanslı olanı terk edin.

Ancak, belirli bir aramanın sıklığının nasıl hesaplanacağı başka bir şeye bağlı olacaktır. Belki de herkesin belirttiği gibi, veri akışı algoritması ...


0

Başladığından beri tüm aramaların sayısını saklamak için cm-taslak kullanın, ilk 10 için onunla en az 10 büyüklüğünde bir yığın tutun. Aylık sonuç için, 30 cm-taslak / karma-tabloyu ve min-yığın bulundurun, her biri başlar son 30, 29 .., 1 günden itibaren sayma ve güncelleme. Gün geçtikçe, sonuncuyu temizleyin ve 1. gün olarak kullanın. Saatlik sonuç için aynı, 60 karma tablo ve min-yığın tutun ve son 60, 59, ... 1 dakika için saymaya başlayın. Bir dakika geçtikçe, sonuncuyu temizleyin ve 1. dakika olarak kullanın.

Aylık sonuç 1 gün aralığında, saatlik sonuç 1 dakika aralığında doğrudur


0

Sabit miktarda belleğiniz ve 'sonsuz' (çok büyük düşünün) token akışınız olduğunda sorun evrensel olarak çözülemez.

Kaba bir açıklama ...

Nedenini görmek için, belirli bir token (yani kelime) giriş akışındaki her N token T içeren bir token akışını düşünün.

Ayrıca, belleğin en fazla M simgeye referansları (kelime kimliği ve sayıları) tutabileceğini varsayalım.

Bu koşullar ile, N'nin yeterince büyük olması ve böylece akış, T'ler arasında farklı M simgelerini içermesi halinde token T'nin asla algılanmayacağı bir giriş akışı oluşturmak mümkündür.

Bu, ilk N algoritma ayrıntılarından bağımsızdır. Sadece M limitine bağlıdır.

Bunun neden doğru olduğunu görmek için, aynı iki simgeden oluşan gruplardan oluşan gelen akışı düşünün:

T a1 a2 a3 ... a-M T b1 b2 b3 ... b-M ...

a'lar ve b'lerin tümü T'ye eşit olmayan geçerli simgelerdir.

Bu akışta, T'nin her ai ve bi için iki kez göründüğüne dikkat edin. Yine de nadiren sistemden yıkanacak kadar görünür.

Boş bir bellekten başlayarak, ilk simge (T) bellekte (M ile sınırlanmış) bir yuva kaplayacaktır. O zaman a1, M bittiğinde a- (M-1) 'e kadar bir yuva tüketecektir.

AM geldiğinde, algoritma bir sembol bırakmalıdır, bu yüzden T olsun. Bir sonraki sembol, a-1'in boşaltılmasına neden olacak b-1 olacaktır, vb.

Dolayısıyla, T, gerçek bir sayım oluşturmak için yeterince uzun süre bellekte kalmayacaktır. Kısacası, herhangi bir algoritma yeterince düşük yerel frekansta ancak yüksek global frekansta (akışın uzunluğu boyunca) bir belirteci kaçıracaktır.

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.