Hangi dize arama algoritması aslında en hızlısı?


27

En hızlı dize arama algoritması olan bir süre sıkışıp kaldım, birçok fikir duydum, ama sonunda emin değilim.

Bazılarının en hızlı algoritmanın Boyer-Moore olduğunu ve bazılarının Knuth-Morris-Pratt'ın aslında daha hızlı olduğunu söylediğini duydum.

Her ikisinde de karmaşıklığı aradım ama çoğunlukla aynı görünüyorlar O(n+m). En kötü senaryoda Boyer-Moore'un O(nm)O (m + 2 * n) 'ye sahip Knuth-Morris-Pratt ile karşılaştırıldığında bir karmaşıklığa sahip olduğunu buldum . Burada n = metin uzunluğu ve m = desen uzunluğu.

Galil Kuralı'nı kullanırsam, Boyer-Moore'un bildiğim kadarıyla en kötü durumdayım.

Benim sorum olan her şeyden öte, aslında en hızlı String arama algoritması (Bu soru sadece Boyer-Moore ve Knuth-Morris-Pratt'ı değil tüm olası sting algoritmalarını içerir).

Düzenleme: Bu cevap nedeniyle

Tam olarak aradığım şey:

Bir metin Tve desen göz önüne alındığında P, Piçindeki tüm görünüşleri bulmak zorundayım T.

Ayrıca, P ve T'nin uzunluğu kaynaklıdır [1,2 000 000]ve programın 0.15 sn'nin altında çalışması gerekir.

KMP ve Rabin-Karp'ın problemden% 100 puan alması için yeterli olduğunu biliyorum ama bir tanesi için Boyer-Moore'u denemek ve uygulamak istedim. Bu tür kalıp arama için hangisi en iyi olur?


6
Bunları seçtiğiniz dilde test ederken ne buldunuz?
Walter

4
Bazı testlerde Boyer-Moore diğer KMP’de daha iyiydi, ancak bunların “en iyi” uygulamasına sahip olduğumdan emin değilim. Seçtiğiniz dile gelince, etiketlerde: C ++ ("seçim dili" yazdığından beri bunu gördüğünüzden emin değilim). PS En iyi testlerde test edip etmediğimden de emin değilim.
vandamon taigi,


O (m + 2 * n) olan Knuth-Morris-Pratt ... O (m + n) demek istiyorsun.
Jules

İyi bir algoritmik karmaşıklığa sahip birini seçin ve ardından elindeki bir profilleyiciyle saçma sapan mikro ayarları yapın - her zaman benim için çalıştı. :-D

Yanıtlar:


38

Yapmak istediğiniz arama türüne bağlıdır. Algoritmaların her biri belirli arama türleri için özellikle iyi performans gösterir, ancak aramalarınızın içeriğini belirtmediniz.

Arama türleriyle ilgili bazı tipik düşünceler:

  • Boyer-Moore: Deseni önceden analiz ederek ve sağdan sola karşılaştırarak çalışır. Bir uyumsuzluk meydana gelirse, ilk analiz, modelin aranan metne göre ne kadar kaydırılabileceğini belirlemek için kullanılır. Bu, özellikle uzun arama kalıpları için iyi çalışır. Özellikle, metninizin her bir karakterini okumanız gerekmediğinden alt-doğrusal olabilir.

  • Knuth-Morris-Pratt: aynı zamanda modeli önceden analiz eder, ancak tekrarlamak zorunda kalmamak için, modelin ilk kısmında zaten eşleştirileni yeniden kullanmaya çalışır. Alfabeniz küçükse (örneğin DNA bazları), arama kalıplarınızın tekrar kullanılabilir alt kalıplar içerme olasılığı daha yüksek olduğundan, bu oldukça iyi çalışabilir.

  • Aho-Corasick: Çok fazla ön işleme ihtiyacı var, ancak bunu bir takım desenler için yapıyor. Aynı arama modellerini tekrar tekrar arayacağınızı biliyorsanız, o zaman bu diğerinden çok daha iyidir, çünkü aramaları bir kez değil, yalnızca bir kez analiz etmeniz gerekir.

Bu nedenle, CS'de her zamanki gibi, genel olarak en iyisine kesin bir cevap yoktur . Bu, eldeki iş için doğru aracı seçme meselesidir.

En kötü durumunuzla ilgili bir başka not: En kötü durumu oluşturmak için gereken arama türlerini göz önünde bulundurun ve bunların sizin durumunuzla gerçekten alakalı olup olmadığını iyice düşünün. Örneğin O(mn), Boyer-Moore algoritmasının en kötü durum karmaşıklığı, bir arama modelinden ve her birinin yalnızca bir karakter kullandığı bir metinden kaynaklanır (bulmak aaagibi aaaaaaaaaaaaaaaaaaaaa) - böyle aramalar için gerçekten hızlı olmanız gerekir mi?


Ben tüm ingilizce alfabe ya da öylesine kullanmak ve soruyu güncellediğim için yalvarmaya başladığım için üzgünüm.
Vandamon taigi

Ve evet, böyle aramalar için bile hızlı
olmam gerekiyor

1

Bu soruyu cevaplamak için biraz geç kaldım, ancak sanırım Z-Algorithmherhangi bir emsalinden çok daha hızlı. En kötü durum karmaşıklığı, O (m + n) 'dir ve desenin / metnin önceden işlenmesini gerektirmez. Diğer algoritmalara göre kodlamak da çok kolaydır.

Aşağıdaki şekilde çalışır.

Örneğin, bir dize var S ='abaaba'. İçin z(i)değer bulmalıyız i=0 to len(S)-1. Açıklamaya girmeden önce, önce bazı tanımları ortaya koyalım.

z(i)= hayır. öneki karakterlerin Ssayısı s(i).

s(i)= itheki S.

Aşağıdakiler için s(i)değerlerdir s = 'abaaba'.

s(0) = 'abaaba' = S
s(1) = 'baaba'
s(2) = 'aaba'
s(3) = 'aba'
s(4) = 'ba'
s(5) = 'a'

Z değerleri sırasıyla

z(0) = 6 = length(S)
z(1) = 0
z(2) = 1
z(3) = 3
z(4) = 0
z(5) = 1

Algoritmanın detaylı anlaşılması için aşağıdaki bağlantılara bakınız.

http://codeforces.com/blog/entry/3107

https://www.youtube.com/watch?v=MFK0WYeVEag

Şimdi tüm zdeğerleri hiçbir ön işleme ek yükü olmadan bulmak O (N) alır . Biri şimdi merak ediyorum, bu mantığı verilen bir dizgede deseni eşleştirmek için nasıl kullanabiliyorsunuz?

Bir örnek ile görelim. Desen (P) aba, Metin (T) aacbabcabaad.

Bunu P $ T biçiminde koyun. ( $- desende veya metinde görünmeyen herhangi bir karakter. Birazdan önemine geleceğim $.)

P$T = aba$aacbabcabaad

Biz biliyoruz len(P)= 3.

Her Z değerleri P$Tare

z(0) = 16 = len(P$T)
z(1) = 0
z(2) = 1
z(3) = 0
z(4) = 1
z(5) = 1
z(6) = 0
z(7) = 0
z(8) = 2
z(9) = 0
z(10) = 0
z(11) = 3
z(12) = 0
z(13) = 1
Z(14) = 1
Z(15) = 0

Şimdi hangisi z(i)= len(P). Ans = 11.Demek ki bizim modelimiz Ans-len(P)-1= konumunda 7. -1içindir $karakteri.

Şimdi neden $böyle bir özel karakter önemlidir. Düşünün P = 'aaa've T = 'aaaaaaa'. Özel karakter olmadan, herkes z(i)artan değerlere sahip olacaktır. Biri modelin metindeki konumunu aşağıdaki formüllerle hala bulabilir:

Durum: z(i)> = len(P)ve pozisyonu: Ans-len(P). Ancak bu durumda durum biraz zor ve kafa karıştırıcı hale gelir. Şahsen özel karakter tekniğini kullanmayı tercih ederim.


1
Burada kendin açıklayabilir misin? Dış sitelerle bağlantıların olması ayrıntılı bir şekilde kullanılabilir, ancak cevabın çekirdeği başka bir siteye bir link takip etmek yerine cevabın kendisinde olmalıdır.

Z algoritması temelde kmp ile aynıdır. Çok daha hızlı olduğundan şüpheliyim.
Thomas Ahle

2
@ThomasAhle ile aynı fikirdeyim. Bilgi z işlem ön işleme tabidir. Yine de iyi bir açıklama. O(n)Bu yanıt nedeniyle KMP ön işlemeden Z ön işlemeye dönüştürmenin bir yolunu buldum . İşte
leewz

-1

Sanal adresleme biçiminde (harfleri işaretleyerek) yazılımda uygulanan içerik adreslenebilir belleği kullanın .

Ortalama bir dize eşleştirme algoritması için gereksiz.

CAM, yaklaşık 128 harfe kadar desene kadar çok sayıda modeli eşzamanlı olarak eşleştirebilir (eğer ASCII ise; yalnızca Unicode ise 64). Ve eşleştirmek istediğiniz dizedeki harf uzunluğu başına bir çağrı ve maksimum desen uzunluğunun uzunluğu başına hafızadan rastgele bir okuma. Yani, aynı anda 90.000.000 desene sahip 100.000 harfli bir dizgiyi analiz ediyorsanız (ki bu, bu kadar geniş bir desen sayısını saklamak için yaklaşık 128 GiB alacaktır), RAM'den 12.800.000 rastgele okuma alacaktır, bu yüzden 1ms'de gerçekleşirdi.

İşte sanal adresleme nasıl çalışıyor?

İlk harfi temsil eden 256 başlangıç ​​adresiyle başlarsam, bu harfler sonraki harflerin 256'sını gösterir. Bir model mevcut değilse, saklamayın.

Öyleyse, harfleri harflerle ilişkilendirmeye devam edersem, sanal adreslemeye işaret eden 128 dilim sanal adreslemenin olması gibi olur.

Bu işe yarayacak - ama aynı anda eşzamanlı olarak 900.000.000 paterni elde etmek için, buna eklemek için son bir numara var - ve bu mektup arabelleklerini yeniden kullanmaya başlamanızdan faydalanıyor, ancak daha sonra dağıtıyor. İçeriği listelerseniz, 256 karakterin tümünü yerine koymak yerine, o zaman çok yavaşlar ve 100 kat kapasite artışı alırsınız, çünkü sonuçta her harf işaretçisi arabelleğinde (1 harfini kullandım) kullanılan sadece 1 harf alırsınız. kaçış').

En yakın komşu bir dize eşleşmesini elde etmek istiyorsanız, o zaman bunların çoğunu paralel olarak çalıştırırsınız ve bir hiyerarşide toplanırsınız, böylece hatanızı tarafsız bir şekilde dağıtırsınız. Eğer sadece bir tane ile en yakın komşuya gitmeye çalışırsan, o zaman ağacın başlangıcına doğru önyargılı olursun.


4
@MagnusRobertCarlWoot, roucer81 ile aynı gavatar'a sahip olduğunuza göre, ya karma kod çarpışmasının astronomik bir tesadüfüdür ya da aynı e-posta adresiniz var. Her iki hesabın da aynı kişisiyseniz, bunları birleştirmek için "bize ulaşın" formunu kullanmalısınız, böylece bu cevabın yükseltilmesiyle kazanılan itibar için uygun krediyi almalısınız.
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.