Bir giriş değerini bastıran şamandıra çiftini bulmak için sıralı şamandıra dizisini aramak için hızlı algoritma


10

En küçükten büyüğe doğru sıralanan bir dizi yüzen var ve en yakın float geçirilen bir giriş değerinden daha büyük veya daha az seçmek gerekir. Bu giriş değeri dizide bir değer olarak bulunmayabilir.

Saf bir yaklaşım dizi boyunca basit bir doğrusal arama yapmak olacaktır. Bu şöyle görünebilir:

void FindClosestFloatsInArray( float input, std::vector<float> array, 
                               float *min_out, float *max_out )
{
    assert( input >= array[0] && input < array[ array.size()-1 ] );
    for( int i = 1; i < array.size(); i++ )
    {
        if ( array[i] >= input )
        {
            *min = array[i-1];
            *max = array[i];
        }
    }
}

Ama tabii ki dizi büyüdükçe, bu yavaşlar ve yavaşlar.

Herkes bu verileri daha iyi bulmama izin verecek bir algoritma hakkında bir fikri var mı? Zaten bazı konularda geliştirilmiş bir ikili aramaya geçtim, ama yine de istediğimden çok daha yavaş ve aslında dizide var olan belirli bir değeri aradığımdan, asla sonlandıramaz erken.

Daha fazla bilgi: Dizideki kayan nokta değerlerinin eşit olarak dağıtılması gerekmez (yani, dizi "1.f, 2.f, 3.f, 4.f, 100.f, 1200.f değerlerinden oluşabilir). , 1203.f, 1400.f ".

Bu işlemi yüz binlerce kez yapıyorum, ancak arama süresini geliştirecekse, kayan dizide herhangi bir miktarda ön işleme yapabilirim. Onları saklamak için vektörden başka bir şey kullanmayı kesinlikle değiştirebilirim, eğer yardımcı olursa.


İkili aramanızın erken sonlanamayacağını düşündüren nedir? Şüphesiz, i ve i + 1'deki öğeleri hedef değerlerini destekleyip desteklemediklerini görmek için test edebilir ve öyleyse sonlandırabilir misiniz?
Paul R

Alternatif olarak, i ve i-1'deki öğeleri hedef değerlerini destekleyip desteklemediklerini test edebilirim. Ben de 'i'> = array.size () - 1 olup olmadığını test etmek gerekir, bu yüzden testi yapmaktan kaçınabilirsiniz, ve <= 0 olup olmadığını test etmek için kaçınmak olabilir ... aslında bir sürü erken çıkışları kontrol etmek için her adımda gerçekleştirilecek ekstra şartlar. Algoritmayı çok yavaşlatacaklarını hayal ediyorum, ancak bunu henüz profillemediğimi itiraf edeceğim.
Trevor Powell

3
Çok karmaşık olması gerekmez - diziniz N boyutundaysa, o zaman sadece N - 1 boyutundaymış gibi davranmanız gerekir. Bu şekilde i + 1'de her zaman geçerli bir öğe vardır. i + 1 öğesi hedef değerden daha büyük olan, i öğesi için hedef değerinizden küçük olan N - 1 öğesi üzerinde ikili arama.
Paul R

Yanıtlar:


11

Sorudaki kod (doğrusal arama), haklı olarak işaret ettiğiniz gibi, büyük şamandıra dizileri için yavaşlayacaktır. Teknik olarak O (n) 'dir, burada n dizinizdeki kayan değerlerin sayısıdır.

Genel olarak, sıralı bir dizide bir değer bulmak için yapabileceğiniz en iyi şey, bir tür özyinelemeli ağaç aramasıdır (örn. İkili arama), bu durumda öğe sayısında O (günlük n) arama süresi elde edebilirsiniz. dizinizde. Büyük n değerleri için O (log n), O (n) 'den çok daha iyidir.

Benim önerilen yaklaşım bu nedenle dizinin basit bir ikili arama olacaktır , yani:

  1. Tüm kayan reklam dizinizi kapsayacak şekilde min / maks tamsayı dizinlerini ayarlama
  2. aralığın ortasındaki değeri ortadaki indeks = (min + maks / 2) arama değerine karşı test edin
  3. x bu değerden düşükse, maks. orta olarak ayarlayın, diğer min. orta olarak ayarlayın
  4. Doğru değeri bulana kadar tekrarlayın (2-4)

Bu, neredeyse tüm durumlar için yeterince hızlı olması gereken bir O (log n) algoritmasıdır. Sezgisel olarak, doğru değeri bulana kadar her adımda aranacak aralığı yarıya indirerek çalışır.

Basit ikili aramayı canlandırmak gerçekten zordur, bu yüzden bunu doğru bir şekilde uyguladıysanız zaten optimale oldukça yakın olabilirsiniz. Bununla birlikte, verilerin dağılımlarını biliyorsanız ve / veya sınırlı bir arama değeri aralığına (x) sahipseniz, deneyebileceğiniz başka daha gelişmiş numaralar da vardır:

  • Bölümleme - bölümler oluşturun (örneğin, iki tamsayı arasındaki her aralık için), her biri iki sınırlayıcı tamsayı arasındaki yüzer değerlerin daha küçük bir sıralanmış listesini ve her aralığın hemen altında ve hemen üzerinde iki değer içerir. Daha sonra aramanızı (trunc (x) +0.5) konumunda başlatabilirsiniz. Uygun boyutta kovalar seçerseniz, bu size iyi bir hız kazandıracaktır (ağacın dallanma faktörünü etkili bir şekilde arttırır .....). Tamsayılar sizin için işe yaramazsa, başka bir sabit nokta hassasiyetine sahip kovaları deneyebilirsiniz (örneğin 1/16'nın katları).
  • Bit eşleme - olası arama değerleri aralığı yeterince küçükse, x'in bitsel değeri ile dizinlenmiş büyük bir arama tablosu oluşturmayı deneyebilirsiniz. Bu O (1) olacaktır, ancak önbelleğinizde çok düşmanca olacak çok fazla belleğe ihtiyacınız olabilir ... bu yüzden dikkatli kullanın. Float değerlerine baktığınız için bu özellikle kötüdür, bu yüzden daha az önemli bitlerin tümünü hesaba katmak için birkaç GB'ye ihtiyacınız olabilir ...
  • Yuvarlama ve hash - karma tabloları muhtemelen bu sorun için en iyi veri yapısı değildir, ancak biraz doğruluk kaybıyla hayatta kalabilirseniz, işe yarayabilirler - sadece arama değerlerinizin en düşük bitlerini yuvarlayın ve doğrudan aramak için bir hashmap kullanın doğru değer. Hashmap boyutu ve hassasiyeti arasında doğru dengeyi denemeniz ve ayrıca mümkün olan tüm hash değerlerinin doldurulmasını sağlamanız gerekir, böylece bu biraz zor olabilir ...
  • Ağaç dengeleme - ideal ağacınızın sola veya sağa gitme şansı% 50 olmalıdır. Dolayısıyla, arama değerlerinin (x) dağılımına dayalı bir ağaç oluşturursanız, minimum test miktarıyla yanıtlar üretmek için ağacı optimize edebilirsiniz. Şamandıra dizinizdeki birçok değer birbirine çok yakınsa, bu iyi bir çözüm olacaktır, çünkü bu dalları çok sık aramanızı önleyecektir.
  • Kritik bit ağaçlar - bunlar hala ağaçlar (bu yüzden hala O (log n) ...) ancak bazı durumlar: ancak karşılaştırmaları yapmak için şamandıralarınızı sabit nokta formatına dönüştürmeniz gerekir

Ancak, çok özel bir durumda değilseniz, basit bir ikili aramaya bağlı kalmanızı tavsiye ederim. Nedenleri:

  • uygulamak çok daha kolay
  • en yaygın durumlar için çok hızlı
  • daha karmaşık yaklaşımların ekstra yükü (örneğin, daha yüksek bellek kullanımı / önbellek baskısı) genellikle küçük teorik kazanımlardan daha ağır basar
  • veri dağıtımlarında gelecekteki değişikliklere karşı daha sağlam olacaktır ....

1

Bu yeterince basit görünüyor:

Sınırlamak istediğiniz şamandıra için ikili bir arama yapın - O (log n) zamanı.

Sonra solundaki eleman alt sınırdır ve sağındaki eleman üst sınırdır.


0

Açık cevap şamandıraları bir ağaçta saklamaktır . 'Önceki' ve 'sonraki' işlemleri desteklemek bir ağaçta önemsizdir. Bu nedenle, değeriniz üzerinde bir 'sonraki' yapın ve ardından ilk adımda bulduğunuz değer üzerinde bir 'önceki' yapın.


1
Bu aslında bir ikili arama ile aynıdır.
kevin cline

-1

Bu makale ("çarpma olmadan sublogaritmik arama") ilgi çekici olabilir; hatta bazı kaynak kodları içerir. Karşılaştırma amacıyla, bir kayan noktalı sayıya, aynı bit modeline sahip bir tamsayı gibi davranabilirsiniz; bu IEEE kayan nokta standardının tasarım hedeflerinden biriydi.

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.