En hızlı alt dize arama algoritması nedir?


166

Tamam, bu yüzden bir aptal gibi gelmiyor, problemi / gereksinimleri daha açık bir şekilde ifade edeceğim:

  • İğne (desen) ve samanlık (aranacak metin) C tarzı boş sonlandırılmış dizelerdir. Uzunluk bilgisi verilmez; gerekirse hesaplanmalıdır.
  • İşlev ilk eşleşmeye veya NULLeşleşme bulunmazsa bir işaretçi döndürmelidir .
  • Arıza durumlarına izin verilmez. Bu, sabit olmayan (veya büyük sabit) depolama gereksinimlerine sahip herhangi bir algoritmanın, ayırma hatası için bir geri dönüş durumuna sahip olması gerektiği anlamına gelir (ve geri dönüş bakımındaki performans, en kötü durum performansına katkıda bulunur).
  • Kod olmadan algoritmanın iyi bir açıklaması (veya buna bağlantı) iyi olsa da uygulama C'de olmalıdır.

... yanı sıra "en hızlı" ile kastettiğim:

  • Deterministik O(n)nerede n= samanlık uzunluğu. (Ancak O(nm), deterministik O(n)sonuçlar vermek için daha sağlam bir algoritma ile birleştirilirlerse , normal olan algoritmalardan (örneğin, yuvarlama karma) fikirleri kullanmak mümkün olabilir ).
  • Asla if (!needle[1])naif kaba kuvvet algoritmasından, özellikle de en sık görülen durum olan çok kısa iğnelerde asla (ölçülebilir bir şekilde, birkaç saat, vb. İçin uygundur) iyi performans göstermez. (Koşulsuz ağır önişleme yükü, olası iğneler pahasına patolojik iğneler için doğrusal katsayıyı iyileştirmeye çalıştığı için kötüdür.)
  • Yaygın olarak uygulanan diğer algoritmalara göre rastgele bir iğne ve samanlık göz önüne alındığında, karşılaştırılabilir veya daha iyi performans (% 50 daha uzun arama süresinden daha kötü değil).
  • Bu koşulların yanı sıra, "en hızlı" açık uçlu tanımını bırakıyorum. İyi bir yanıt, önerdiğiniz yaklaşımı neden "en hızlı" olarak değerlendirdiğinizi açıklamalıdır.

Mevcut uygulamam, glibc'nin İki Yönlü uygulamasından yaklaşık% 10 daha yavaş ve 8 kat daha hızlı (girdiye bağlı olarak) çalışıyor.

Güncelleme: Mevcut optimal algoritmam aşağıdaki gibidir:

  • 1 uzunluktaki iğneler için kullanın strchr.
  • Uzunluğu 2-4 olan iğneler için, bir kerede 2-4 baytı karşılaştırmak için makine kelimelerini kullanın: İğneyi bit kaydırmalarla 16 veya 32 bit tamsayıya önceden yükleyin ve her bir yinelemede samanlıktan eski bayt çıkışını / yeni baytları döndürün . Samanlıktaki her bayt tam olarak bir kez okunur ve 0 (dizginin sonu) ve bir 16- veya 32-bit karşılaştırması için bir kontrol yapar.
  • Uzunluk> 4 olan iğneler için, yalnızca pencerenin son baytına uygulanan kötü bir kaydırma tablosu (Boyer-Moore gibi) olan İki Yönlü algoritma kullanın. Birçok orta uzunlukta iğne için net bir kayıp olacak bir 1kb tablo başlatmanın yükünü önlemek için, shift tablosundaki hangi girişlerin başlatıldığını işaretleyen bir bit dizisi (32 bayt) tutarım. Ayarlanmamış bitler, iğne içinde hiç görünmeyen, tam iğne uzunluğu kaydırmanın mümkün olduğu bayt değerlerine karşılık gelir.

Aklımda kalan büyük sorular:

  • Kötü vardiya tablosundan daha iyi yararlanmanın bir yolu var mı? Boyer-Moore en iyi şekilde geriye doğru (sağdan sola) tarayarak kullanır, ancak İki Yönlü bir soldan sağa tarama gerektirir.
  • Genel durum için bulduğum tek iki uygulanabilir aday algoritması (bellek yetersiz veya karesel performans koşulları yok) Sıralı Alfabelerde İki Yönlü ve Dizeli Eşleştirme . Ancak, farklı algoritmaların en uygun olacağı, kolayca tespit edilebilen durumlar var mı? Uzay algoritmalarındaki O(m)( miğne uzunluğunun nerede olduğu) kesinlikle bir kısmı için kullanılabilir m<100. Ayrıca, sadece doğrusal zaman gerektiren iğneler için kolay bir test varsa, en kötü durumda ikinci dereceden algoritmalar kullanmak da mümkün olacaktır.

Bonus puanları:

  • Hem iğne hem de samanlığın iyi oluşturulmuş UTF-8 olduğunu varsayarak performansı artırabilir misiniz? (Değişen bayt uzunluklarındaki karakterlerde, iyi biçimlendirilmiş iğne, iğne ve samanlık arasında bazı dize hizalama gereksinimleri uygular ve uyuşmayan bir kafa baytıyla karşılaşıldığında otomatik 2-4 bayt kaydırmaya izin verir. Ancak bu kısıtlamalar size, maksimum ek hesaplamaları, iyi son ek kaymaları vb. size çeşitli algoritmalar veriyor mu?)

Not: Orada algoritmaların çoğunun iyi farkındayım, pratikte ne kadar iyi performans gösterdiklerini değil. İşte iyi bir referans, böylece insanlar bana algoritmalar hakkında yorum / cevap olarak referans vermiyorlar: http://www-igm.univ-mlv.fr/~lecroq/string/index.html


Dizelerdeki Algoritmalar'da listelenen çok sayıda dize arama algoritması vardır . Bu listeden hangi algoritmaları düşündüğünüzü tanımlamak isteyebilirsiniz.
Greg Hewgill

62
Sonunda bu bağlantı altın!
Carlos

4
Hala bir cevabı kabul etmediğine inanamıyorum.
user541686

1
@Mehrdad: Soruyu gerçekten sorulduğu gibi ele alan herhangi bir cevap olmadığını söylemek üzereydim, ama sizinkine benziyor. Cevap verdiğinizde, ilerledim ve daha strstrsonra için bir şey olarak daha fazla gelişme bıraktım , bu yüzden bağladığınız kağıdı düzgün bir şekilde okumak için etrafta dolaşmadım, ancak kulağa çok umut verici geliyor. Sana geri dönmediğin için teşekkürler ve üzgünüm.
R .. GitHub BUZE YARDIMCI OLMAK

Yanıtlar:


37

Muhtemel iğneler ve samanlıklardan oluşan bir test kütüphanesi oluşturun. Kaba kuvvet de dahil olmak üzere çeşitli arama algoritmalarındaki testleri profilleyin. Verilerinizle en iyi performansı göstereni seçin.

Boyer-Moore , iyi bir sonek tablosuyla birlikte kötü bir karakter tablosu kullanır.

Boyer-Moore-Horspool kötü karakter tablosu kullanıyor.

Knuth-Morris-Pratt kısmi bir eşleşme tablosu kullanır.

Rabin-Karp koşu karmalarını kullanır.

Hepsi farklı bir dereceye indirgenmiş karşılaştırmalar için genel işlem yaparlar, bu nedenle gerçek dünya performansı hem iğnenin hem de samanlığın ortalama uzunluklarına bağlı olacaktır. İlk yük ne kadar fazlaysa, daha uzun girişlerde o kadar iyidir. Çok kısa iğneler ile kaba kuvvet kazanabilir.

Düzenle:

Temel çiftleri, ingilizce ifadeleri veya tek kelimeleri bulmak için farklı bir algoritma en iyisi olabilir. Tüm girdiler için en iyi algoritma olsaydı, bu halka duyurulurdu.

Aşağıdaki küçük tabloyu düşünün. Her soru işaretinin farklı bir en iyi arama algoritması olabilir.

                 short needle     long needle
short haystack         ?               ?
long haystack          ?               ?

Bu gerçekten her grafikte daha kısa ve daha uzun giriş aralığına sahip bir grafik olmalıdır. Her algoritmayı böyle bir grafiğe çizdiyseniz, her birinin farklı bir imzası olacaktır. Bazı algoritmalar, gen arama gibi kullanımları etkileyebilecek kalıpta çok fazla tekrarlanır. Genel performansı etkileyen diğer bazı faktörler aynı kalıbı bir kereden fazla aramak ve aynı anda farklı kalıpları araştırmaktır.

Örnek bir sete ihtiyacım olursa, google veya wikipedia gibi bir siteyi kazımak, sonra tüm sonuç sayfalarından html'yi şeritlemek istiyorum. Bir arama sitesi için, bir sözcük yazın ve önerilen arama ifadelerinden birini kullanın. Varsa, birkaç farklı dil seçin. Web sayfalarını kullanarak, tüm metinler kısa ve orta olacaktır, bu nedenle daha uzun metinler elde etmek için yeterli sayfayı birleştirin. Ayrıca kamuya açık kitaplar, yasal kayıtlar ve diğer büyük metin organlarını da bulabilirsiniz. Ya da sözlükten kelime seçerek rastgele içerik oluşturun. Ancak profil oluşturma noktası, arayacağınız içerik türüne karşı test etmektir, bu nedenle mümkünse gerçek dünya örneklerini kullanın.

Kısa ve uzun belirsiz bıraktım. İğne için 8 karakterin altında kısa, 64 karakterin altında orta ve 1k altında uzun düşünüyorum. Samanlık için, 2 ^ 10 altında kısa, 2 ^ 20 altında orta ve 2 ^ 30 karaktere kadar uzun düşünüyorum.


1
Test kütüphanesi için iyi önerileriniz var mı? SO'ya sorduğum bir önceki soru bununla ilgiliydi ve hiçbir zaman gerçek cevaplar almadım. (kendim hariç ...) Kapsamlı olmalı. Strstr başvurusu benim fikrim İngilizce metin arama olsa bile, başkasının ... baz çifti dizileri genlerin arıyor olabilirler
R .. GitHub DUR ICE YARDIMCI

3
Kısa / uzuntan biraz daha karmaşık. İğne için, çoğu algoritmanın performansıyla ilgili büyük sorular şunlardır: Uzunluk? Herhangi bir periyodiklik var mı? İğne tüm benzersiz karakterleri içeriyor mu (tekrar yok)? Yoksa aynı karakter mi? Samanlıkta asla iğnede görünmeyen çok sayıda karakter var mı? Sisteminizi sakatlamak için en kötü performanstan yararlanmak isteyen bir saldırgan tarafından sağlanan iğnelerle uğraşma şansı var mı? Vb ..
R .. GitHub DURDURMA BUZA YARDIM ET

31

2011 yılında yayınlanan, ben çok iyi Dany Breslauer, Roberto Grossi ve Filippo Mignosi tarafından "Basit Gerçek Zamanlı Sabit Uzay Dize Eşleştirme" algoritması olabilir inanıyorum.

Güncelleme:

2014 yılında Yazarlar, bu gelişme yayınlanan: Optimum dize eşleştirme dolu Doğru .


1
Vay canına teşekkürler. Gazeteyi okuyorum. Eğer sahip olduğumdan daha iyi olduğu ortaya çıkarsa, cevabınızı kesinlikle kabul edeceğim.
R .. GitHub BUZA YARDIMCI DURDUR

1
@R ..: Elbette! :) Bunlardan bahsetmişken, algoritmayı uygulamayı başarırsanız, lütfen herkesin yararlanabilmesi için StackOverflow'da yayınlamayı düşünün! Herhangi bir yerde herhangi bir uygulama bulamadım ve araştırma makalelerinde bulduğum algoritmaları uygulamada iyi değilim haha.
user541686

2
Zaten kullandığım "iki yönlü" algoritmanın bir varyantı, bu yüzden kodumu kullanmak için uyarlamak aslında kolay olabilir. Ancak emin olmak için makaleyi daha ayrıntılı olarak okumam gerekecek ve yapılan değişikliklerin genel durumu büyük ölçüde hızlandıran bir "kötü karakter tablosu" kullanımımla uyumlu olup olmadığını değerlendirmem gerekiyor.
R .. GitHub BUZA YARDIMCI DURDUR

11
Ve @ Mehrdad'ın cevabını hala kabul etmedin! :-)
lifebalance

3
@DavidWallace: Ne? Makale başlıkları ve yazarlar var. Bağlantı kopsa bile kağıtları bulabilirsiniz. Benden ne yapmamı bekliyorsun, algoritma için sahte kod yaz? Algoritmayı anladığımı düşündüren nedir?
user541686

23

Http://www-igm.univ-mlv.fr/~lecroq/string/index.html size en iyi bilinen ve araştırılan dize eşleştirme algoritmalarının bazı mükemmel bir kaynak ve özetidir işaret bağlayın.

Arama sorunlarının çoğuna yönelik çözümler, ön işleme genel giderleri, zaman ve alan gereklilikleriyle ilgili değiş tokuşları içerir. Hiçbir algoritma hiçbir durumda optimal veya pratik olmayacaktır.

Amaç dize arama için belirli bir algoritma tasarlamak, o zaman söylemek zorunda ne geri kalanı yoksaymak, Eğer genelleştirilmiş bir dize arama hizmeti rutin geliştirmek istiyorsanız o zaman aşağıdakileri deneyin:

Önceden başvurduğunuz algoritmaların güçlü ve zayıf yanlarını gözden geçirmek için biraz zaman ayırın. İncelemeyi, ilgilendiğiniz dize aramaları aralığını ve kapsamını kapsayan bir dizi algoritma bulmak amacıyla gerçekleştirin. Ardından, verilen girdiler için en iyi algoritmayı hedeflemek üzere bir sınıflandırıcı işlevine dayalı bir ön uç arama seçici oluşturun. Bu şekilde işi yapmak için en verimli algoritmayı kullanabilirsiniz. Bu, özellikle bir algoritma belirli aramalar için çok iyi olduğunda ancak zayıf bir şekilde bozulduğunda etkilidir. Örneğin, kaba kuvvet muhtemelen uzunluk 1'in iğneleri için en iyisidir, ancak iğne uzunluğu arttıkça hızla bozulur , bunun üzerine sustik-moore algoritmasıdaha küçük iğneler ve daha büyük harfler için KMP veya Boyer-Moore algoritmaları daha iyi olabilir. Bunlar sadece olası bir stratejiyi gösteren örneklerdir.

Çoklu algoritma yaklaşımı yeni bir fikir değil. Birkaç ticari Sıralama / Arama paketi tarafından kullanıldığına inanıyorum (örneğin ana çerçevelerde yaygın olarak kullanılan SYNCSORT, çeşitli sıralama algoritmaları uygular ve verilen girdiler için "en iyi" olanı seçmek için sezgisel tarama kullanır)

Her arama algoritması, örneğin, bu makalede gösterildiği gibi, performansında önemli farklılıklar yaratabilen çeşitli varyasyonlarda bulunur .

Ek arama stratejilerinin gerektiği alanları kategorilere ayırmak veya seçici işlevinizi daha etkili bir şekilde ayarlamak için hizmetinizi kıyaslayın. Bu yaklaşım hızlı veya kolay değildir, ancak iyi yapılırsa çok iyi sonuçlar verebilir.


1
Yanıt için teşekkürler, özellikle daha önce görmediğim Sustik-Moore bağlantısı. Çoklu algoritma yaklaşımı kesinlikle yaygın kullanımdadır. Glibc temel olarak iğne_len'in 1, <32 veya> 32 olmasına bağlı olarak strchr, Kötü karakter kaydırma tablosu olmayan İki Yönlü veya kötü karakter kaydırma tablosu olan İki Yönlü yapar. Şu anki yaklaşımım aynı, ancak vardiya tablosunu her zaman kullanmam dışında; Bunu yapmak için gerekli 1kb memset'i, tablonun hangi öğelerinin başlatıldığını işaretlemek için kullanılan bir bit setinde 32 baytlık bir memset ile değiştirdim ve küçük iğneler için bile fayda elde ettim (ancak ek yük değil).
R .. GitHub BUZA YARDIMCI DURDUR

1
Bunu düşündükten sonra, Sustik-Moore için amaçlanan uygulamanın ne olduğunu gerçekten merak ediyorum. Küçük alfabe ile hiçbir zaman önemli değişiklikler yapamazsınız (alfabenin tüm karakterleri neredeyse iğnenin sonuna yakın görünür) ve sonlu otomata yaklaşımları çok verimlidir (küçük durum geçiş tablosu). Bu yüzden Sustik-Moore'un optimal olabileceği herhangi bir senaryo öngöremiyorum ...
R. .. GitHub DURDURMAK ICE

büyük tepki - eğer bu özel cevaba yıldız koyabilirdim.
Jason S

1
Sustik-moore algoritmasının arkasındaki teori, iğne nispeten büyük ve alfabe nispeten küçük olduğunda (örneğin DNA dizilerini aramak) size daha büyük ortalama kaydırma miktarları vermesidir. Bu durumda daha büyük, aynı girdiler göz önüne alındığında temel Boyer-Moore algoritmasının üreteceğinden daha büyük anlamına gelir. Bunun sonlu bir otomata yaklaşımına veya (birçoğu da dahil olmak üzere) bazı Boyer-Moore varyasyonuna göre ne kadar verimli olduğunu söylemek zordur. Bu yüzden aday algoritmalarınızın güçlü ve zayıf yanlarını araştırmak için biraz zaman harcadığımı vurguladım.
NealB

1
Hm, sanırım Boyer-Moore'un kötü karakter değişimleri anlamında kaymaları düşünmeye başlamıştım. BM iyi son ek kaymalarındaki iyileşmeyle birlikte, Sustik-Moore DNA araştırmasında DFA yaklaşımlarından daha iyi performans gösterebilir. Düzgün şeyler.
R .. GitHub BUZA YARDIMCI DURDUR

21

Bu tartışmada belirtilen teknik raporumuzu gördüğüme şaşırdım; Yukarıda Sustik-Moore adlı algoritmanın yazarlarından biriyim. (Makalemizde bu terimi kullanmadık.)

Burada algoritmanın benim için en ilginç özelliğinin, her harfin en fazla bir kez incelendiğini kanıtlamanın oldukça basit olduğunu vurgulamak istedim. Daha önceki Boyer-Moore versiyonları için, her bir mektubun en fazla 3 ve daha sonra en fazla 2 kez incelendiğini ve bu kanıtların daha fazla dahil olduğunu kanıtladılar (makaledeki alıntılara bakın). Bu nedenle, bu varyantı sunma / incelemede didaktik bir değer de görüyorum.

Makalede ayrıca teorik garantileri gevşetirken verimliliğe yönelik diğer varyasyonları da açıklıyoruz. Kısa bir yazıdır ve materyal bence ortalama bir lise mezunu tarafından anlaşılabilir olmalıdır.

Ana hedefimiz bu versiyonu daha da geliştirebilecek olan başkalarının dikkatine sunmaktı. Dize aramanın çok fazla varyasyonu vardır ve bu fikrin fayda sağlayabileceği her şeyi düşünemeyiz. (Sabit metin ve değişen desen, sabit desen farklı metin, ön işleme mümkün / mümkün değil, paralel yürütme, büyük metinlerde eşleşen alt kümeleri bulma, hatalara izin verme, eşleşmelere yakın vb.)


1
Mevcut bir C veya C ++ uygulaması biliyor musunuz? Bunu bazı dna motif arama (tam motif eşleşmeleri) için kullanmayı düşünüyorum. Değilse, belki kendim bir uygulama geliştirmeyi ve algoritmayı artırmak için göndermeyi
deneyeceğim

4
Bilinen mevcut bir uygulama olmadan, Sustik-Moore / 2BLOCK algoritmasının uygulamada kullanılması olası görünmüyor ve "Tam Dize Eşleştirme Sorunu: Kapsamlı Bir Deneysel Değerlendirme"
JDiMatteo

18

En hızlı alt dize arama algoritması içeriğe bağlı olacaktır:

  1. alfabe boyutu (örneğin, DNA'ya karşı İngilizce)
  2. iğne uzunluğu

2010 tarihli "Tam Dize Eşleştirme Sorunu: Kapsamlı Bir Deneysel Değerlendirme" 51 algoritma (farklı alfabe boyutları ve iğne uzunlukları ile) için çalışma zamanları içeren tablolar verir, böylece bağlamınız için en iyi algoritmayı seçebilirsiniz.

Tüm bu algoritmaların burada C testlerinin yanı sıra bir test takımı vardır:

http://www.dmi.unict.it/~faro/smart/algorithms.php


4

Gerçekten iyi bir soru. Sadece küçük bitler ekleyin ...

  1. Birisi DNA dizisi eşleşmesinden bahsediyordu. Ancak DNA dizisi için genellikle yaptığımız şey samanlık için bir veri yapısı (örn. Sonek dizisi, sonek ağacı veya FM-endeksi) oluşturmak ve birçok iğneyi buna uygun hale getirmektir. Bu farklı bir soru.

  2. Birisi çeşitli algoritmaları karşılaştırmak isterse gerçekten harika olurdu. Sıkıştırma ve sonek dizilerinin oluşturulması konusunda çok iyi ölçütler var, ancak dize eşleşmesinde bir ölçüt görmedim. Potansiyel samanlık adayları SACA kriterlerinden olabilir .

  3. Birkaç gün önce Boyer-Moore uygulamasını tavsiye ettiğiniz sayfadan test ediyordum (EDIT: memmem () gibi bir işlev çağrısına ihtiyacım var, ancak standart bir işlev değil, bu yüzden uygulamaya karar verdim). Kıyaslama programım rastgele samanlık kullanıyor. Görünüşe göre bu sayfadaki Boyer-Moore uygulaması, glibc'nin memmem () ve Mac'in strnstr () yönteminden daha hızlı. İlgilenmeniz durumunda uygulama burada ve kıyaslama kodu burada . Bu kesinlikle gerçekçi bir kriter değil, ama bir başlangıç.


SACA kıyaslamasındaki samanlık adayları ile birlikte test etmek için iyi iğneleriniz varsa, bunları diğer sorumun cevabı olarak gönderin ve daha iyi bir cevap almanın kısa sürede kabul edildiğini işaretleyeceğim.
R .. GitHub DURDURMA BUZA YARDIMCI GEÇ

3
Memmem ve Boyer-Moore hakkında, Boyer-Moore'un (ya da daha ziyade Boyer-Moore'daki geliştirmelerden birinin) rastgele verilerde en iyi performansı göstermesi muhtemeldir. Rasgele veriler son derece düşük bir periyodiklik olasılığı ve kuadratik en kötü duruma yol açan uzun kısmi eşleşmelere sahiptir. Boyer-Moore ve Two-Way'i birleştirmenin veya Boyer-Moore'un "kullanımı güvenli" olduğunda ne zaman etkili bir şekilde tespit etmenin bir yolunu arıyorum ama şimdiye kadar hiçbir başarı elde etmedim. BTW Ben karşılaştırma olarak glibc'nin memmem kullanmak olmaz. Temelde glibc ile aynı algoritmayı uyguladığım uygulama birkaç kat daha hızlı.
R .. GitHub DURDURMA BUZA YARDIMCI GEÇ

Dediğim gibi, bu benim uygulamam değil. Christian Charras ve Thierry Lecroq'a teşekkür ederiz. Rastgele girdinin kıyaslama için neden kötü olduğunu hayal edebiliyorum ve eminim glibc algoritmaları nedenlerle seçti. Ayrıca memmem () etkin bir şekilde uygulanmıyor sanırım. Yapmaya çalışacağım. Teşekkürler.
user172818

4

Bunun eski bir soru olduğunu biliyorum, ama çoğu kötü vardiya tablosu tek karakter. Veri kümeniz için mantıklıysa (örneğin, özellikle yazılı sözcükler varsa) ve kullanılabilir alanınız varsa, tek karakter yerine n-gramdan oluşan kötü bir kaydırma tablosu kullanarak dramatik bir hızlanma elde edebilirsiniz.


3

Stdlib kullanın strstr:

char *foundit = strstr(haystack, needle);

Çok hızlıydı, yazmam sadece 5 saniye sürdü.


26
Ve sorumu okursanız, daha iyi bir zaman geçirdim. Ben alayını beğendim ama -1'i atlayacağım.
R .. GitHub BUZA YARDIMCI DURDUR

3

İşte Python'un çekirdek boyunca kullanılan arama uygulaması . Yorumlar, sıkıştırılmış bir boyer-moore delta 1 tablosu kullandığını gösteriyor .

Kendimi arama dize ile bazı oldukça deneyler yaptık, ama birden fazla arama dizeleri için oldu. Horspool ve Bitap'ın montaj uygulamaları, genellikle düşük kalıp sayıları için Aho-Corasick gibi algoritmalara karşı kendi başlarına olabilir .


3

Daha hızlı "Tek bir eşleşen karakter arayın" (ala strchr) algoritması.

Önemli notlar:

  • Bu işlevler bir "sayı / sayım (baştaki | son) sıfırlar" gccderleyici intrinsic- kullanır __builtin_ctz. Bu işlevlerin yalnızca bu işlemi gerçekleştiren bir talimat (lar) ı olan makinelerde hızlı olması muhtemeldir (örn. X86, ppc, arm).

  • Bu işlevler, hedef mimarinin 32 ve 64 bit hizalanmamış yükler gerçekleştirebileceğini varsayar. Hedef mimariniz bunu desteklemiyorsa, okumaları düzgün bir şekilde hizalamak için bir başlangıç ​​mantığı eklemeniz gerekir.

  • Bu işlevler işlemci nötrdür. Hedef CPU'nun vektör talimatları varsa, (daha iyi) daha iyi yapabilirsiniz. Örneğin, strlenaşağıdaki işlev SSE3 kullanır ve başka bir bayt aramak için taranan baytları XOR olarak değiştirebilir 0. Mac OS X 10.6 (x86_64) çalıştıran 2.66GHz Core 2 dizüstü bilgisayarda gerçekleştirilen karşılaştırmalar:

    • 843.433 MB / s için strchr
    • İçin 2656.742 MB / s findFirstByte64
    • 13094.479 MB / s için strlen

... 32 bit sürümü:

#ifdef __BIG_ENDIAN__
#define findFirstZeroByte32(x) ({ uint32_t _x = (x); _x = ~(((_x & 0x7F7F7F7Fu) + 0x7F7F7F7Fu) | _x | 0x7F7F7F7Fu); (_x == 0u)   ? 0 : (__builtin_clz(_x) >> 3) + 1; })
#else
#define findFirstZeroByte32(x) ({ uint32_t _x = (x); _x = ~(((_x & 0x7F7F7F7Fu) + 0x7F7F7F7Fu) | _x | 0x7F7F7F7Fu);                    (__builtin_ctz(_x) + 1) >> 3; })
#endif

unsigned char *findFirstByte32(unsigned char *ptr, unsigned char byte) {
  uint32_t *ptr32 = (uint32_t *)ptr, firstByte32 = 0u, byteMask32 = (byte) | (byte << 8);
  byteMask32 |= byteMask32 << 16;
  while((firstByte32 = findFirstZeroByte32((*ptr32) ^ byteMask32)) == 0) { ptr32++; }
  return(ptr + ((((unsigned char *)ptr32) - ptr) + firstByte32 - 1));
}

... ve 64 bit sürümü:

#ifdef __BIG_ENDIAN__
#define findFirstZeroByte64(x) ({ uint64_t _x = (x); _x = ~(((_x & 0x7F7F7F7F7f7f7f7full) + 0x7F7F7F7F7f7f7f7full) | _x | 0x7F7F7F7F7f7f7f7full); (_x == 0ull) ? 0 : (__builtin_clzll(_x) >> 3) + 1; })
#else
#define findFirstZeroByte64(x) ({ uint64_t _x = (x); _x = ~(((_x & 0x7F7F7F7F7f7f7f7full) + 0x7F7F7F7F7f7f7f7full) | _x | 0x7F7F7F7F7f7f7f7full);                    (__builtin_ctzll(_x) + 1) >> 3; })
#endif

unsigned char *findFirstByte64(unsigned char *ptr, unsigned char byte) {
  uint64_t *ptr64 = (uint64_t *)ptr, firstByte64 = 0u, byteMask64 = (byte) | (byte << 8);
  byteMask64 |= byteMask64 << 16;
  byteMask64 |= byteMask64 << 32;
  while((firstByte64 = findFirstZeroByte64((*ptr64) ^ byteMask64)) == 0) { ptr64++; }
  return(ptr + ((((unsigned char *)ptr64) - ptr) + firstByte64 - 1));
}

Edit 2011/06/04 OP yorumlarda bu çözümün "aşılmaz bir hataya" sahip olduğuna dikkat çekiyor:

okunmamış bir sayfaya veya okuma izni olmadan sayfaya erişebilen aranan bayt veya boş sonlandırıcıyı okuyabilir. Hizalanmadığı sürece dize işlevlerinde büyük okumaları kullanamazsınız.

Bu teknik olarak doğrudur, ancak yorumlarda OP tarafından önerilen yöntem de dahil olmak üzere tek bir bayttan daha büyük parçalar üzerinde çalışan hemen hemen tüm algoritmalar için geçerlidir :

Tipik bir strchruygulama saf değil, verdiğinizden biraz daha verimlidir. En yaygın kullanılan algoritma için bunun sonuna bakın: http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord

Ayrıca, kendiliğinden hizalama ile hiçbir ilgisi yoktur . Doğru, bu potansiyel olarak kullanılan yaygın mimarilerin çoğunda tartışılan davranışa neden olabilir, ancak bunun mikro mimari uygulama detayları ile daha fazla ilgisi vardır - hizalanmamış okuma bir 4K sınırına (tekrar, tipik) çarparsa, bu okuma bir programa neden olur sonraki 4K sayfa sınırı eşleştirilmezse sonlandırma hatası.

Ancak bu, yanıtta verilen algoritmada bir "hata" değildir - bu davranış, aramanın boyutunu sınırlamak için bir argümanı beğenir strchrve strlenkabul etmez length. Arama char bytes[1] = {0x55};tartışmamızın amaçları için sadece bu yüzden bir 4K VM sayfa sınırında ve sonraki sayfanın en sonunda yerleştirilecek olur ki, birlikte, eşleştirilmemişse strchr(bytes, 0xAA)(burada strchrbir bayt-at-a-time uygulaması olan) tam olarak kilitlenmesine aynı şekilde. strchrİlgili kuzen için aynen strlen.

lengthBağımsız değişken olmadan , yüksek hızlı algoritmadan ne zaman çıkacağını ve bir bayt bayt algoritmasına ne zaman dönmeniz gerektiğini anlamanın bir yolu yoktur. Çok daha muhtemel bir "hata", teknik undefined behaviorolarak çeşitli C dil standartlarına göre sonuçlanan ve benzer bir şey tarafından bir hata olarak işaretlenecek olan "tahsisatın boyutunu geçmiş" okumak olacaktır valgrind.

Özetle, bu yanıt kodu ve OP tarafından işaret edilen, ancak bayt-doğru okuma anlambilimine sahip olması gereken bayt parçalarından daha büyük işler üzerinde daha hızlı gitmek için herhangi bir lengthargüman yoksa "buggy" olması muhtemeldir . "son okunan değer" in köşe durumlarını kontrol eder.

Bu cevap kod hedef CPU bir hızlıca varsa hızlı bir şekilde doğal işlemci kelime boyutu yığın ilk bayt bulmak mümkün olduğu için bir çekirdek olduğunu ctztalimatı gibi. Sadece doğru hizalanmış doğal sınırlarda veya lengthyüksek hızlı çekirdekten çıkmanıza ve daha yavaş bir bayt-byte bayt kontrolüne geçmenize izin verecek bir tür sınırda çalıştığından emin olmak gibi şeyler eklemek önemsizdir .

OP ayrıca yorumlarda şunları belirtmektedir:

Ctz optimizasyonunuza gelince, sadece O (1) kuyruk çalışması için bir fark yaratır. Küçük dizelerle performansı artırabilir (örneğin strchr("abc", 'a');, büyük boyuttaki dizelerle kesinlikle değil).

Bu ifadenin doğru olup olmadığı büyük ölçüde söz konusu mikro mimariye bağlıdır. Kanonik 4 aşamalı RISC boru hattı modelini kullanarak, neredeyse kesinlikle doğrudur. Ancak, çekirdek hızının bellek akış hızını tamamen cüce edebileceği çağdaş bir süper skaler CPU için doğru olup olmadığını söylemek son derece zordur. Bu durumda, sadece akla yatkın olmakla kalmaz, aynı zamanda oldukça yaygındır, çünkü "akıtılabilecek bayt sayısına" göre "emekli olabilen talimatların sayısı" nda büyük bir boşluk olması, msgstr "Aktarabilen her bayt için kullanımdan kaldırılabilecek komut sayısı". Bu yeterince büyükse, ctz+ shift komutu "ücretsiz" olarak yapılabilir.


"Uzunluk 1 iğneler için, kullanın strchr." - En hızlı alt dize arama algoritmalarını istediniz. 1 uzunluğunda bir alt dize bulmak, sadece optimize edilebilen özel bir durumdur. 1 ( strchr) uzunluğundaki alt dizeler için geçerli özel durum kodunuzu yukarıdaki gibi bir şeyle değiştirirseniz, işler (muhtemelen, nasıl strchruygulandığına bağlı olarak ) daha hızlı gider. Yukarıdaki algoritma tipik bir saf strchruygulamadan yaklaşık 3 kat daha hızlıdır .
johne

2
OP dize düzgün bir şekilde sonlandırıldı dedi, bu yüzden hakkında tartışmanız char bytes[1] = {0x55};ilgisiz. Öncelikle uzunluğu bilmeyen herhangi bir kelime okuma algoritması için bunun doğru olduğu hakkındaki yorumunuz çok alakalı.
Seth Robertson

1
Sorun, yalnızca hizalanmış işaretçiler üzerinde kullandığınız için bahsettiğim sürüm için geçerli değil - en azından doğru uygulamaların yaptığı budur.
R '.. GitHub DUR YARDIMCI ICE

2
@R, "hizalanmış işaretçiler" ile ilgisi yoktur. Varsayımsal olarak, bayt düzeyinde tanecikli VM korumasını destekleyen bir mimariye sahipseniz ve her bir mallocayırma her iki tarafta da "yeterince doluydu" ve VM sistemi bu ayırma için bayt taneli korumayı zorunlu kıldı .... işaretçi hizalanmış olsun veya olmasın ( önemsiz 32-bit intdoğal hizalama varsa), bu hizalanmış okumanın ayırma boyutunun ötesinde okunması hala mümkündür. HERHANGİ BİR okuma boyutunu tahsis boyutudur undefined behavior.
johne

5
@johne: Yorum yapmak için +1. Kavramsal olarak haklısınız, ancak gerçek şu ki, bayt-taneciklik korumaları hem depolamak hem de zorlamak için çok pahalılar ve var olmayacaklar ve asla olmayacaklar. Temel alınan depolama biriminin eşdeğerinden elde edilen sayfa ayrıntı düzeyi eşleşmeleri olduğunu biliyorsanız, mmaphizalama yeterlidir.
R .. GitHub BUZA YARDIMCI DURDUR

3

Sadece "en hızlı strstr" için arama yapın ve eğer ilgi çekici bir şey görürseniz bana sorun.

Bence kendinize çok fazla kısıtlama getiriyorsunuz (evet hepimiz maksimum arayıcıda alt doğrusal doğrusal istiyoruz), ancak o zamana kadar karma yaklaşımın sadece şık bir çözüm ( daha kısa 2..16 desenler için BNDM tarafından iyi takviye edilmiştir).

Kısa bir örnek:

As-tek-line String (206908949bytes) içine Desen (32bytes) için Arama yapmak ... atla Performansı (büyük-iyi): 3041% 6801754 sekme / yineleme Railgun_Quadruplet_7Hasherezade_hits / Railgun_Quadruplet_7Hasherezade_clocks: 0/58 Railgun_Quadruplet_7Hasherezade performans: 3483KB / saat

1554% 13307181 atlama / yineleme Boyer_Moore_Flensburg_hits / Boyer_Moore_Flensburg_clocks: 0/83 as-tek-line ... atla Performansı (daha büyük-daha iyi) Dize (206908949bytes) içine Desen (32bytes) için Arama yapmak Boyer_Moore_Flensburg performansı: 2434KB / saat

Deseni (32bayt) Dizeye (206908949bayt) tek satırlık olarak Arama Yapma ... Atlama Performansı (daha büyük olanı daha iyi):% 129, 160239051 atlama / yineleme İki Yönlü_hits / İki Yönlü_Kilit: 0/816 İki -Yol performansı: 247KB / saat

Sanmayce,
Saygılarımızla


3

Sorunuzda bahsettiğiniz İki Yönlü Algoritma (bu arada inanılmaz!) Son zamanlarda bir seferde çok baytlı kelimeler üzerinde verimli çalışmak için iyileştirildi: Optimal Paketlenmiş Dize Eşleştirme .

Tüm makaleyi okumadım, ancak zaman karmaşıklığı iddiası için birkaç yeni, özel CPU talimatına (örneğin SSE 4.2'ye dahil) güveniyor gibi görünüyorlar, ancak mevcut değilse, çok kötü gelmeyen w-bit kelimeler için bunları O (log log w) zamanında simüle edin.


3

Diyelim ki 4 farklı algoritma uygulayabilirsiniz. Her M dakikada bir (ampirik olarak belirlenecek) mevcut verilerin tümü üzerinde 4'ü çalıştırın. N koşusu (TBD) üzerinde istatistik birikimi Ardından sonraki M dakika boyunca yalnızca kazananı kullanın.

Hiç kazanamayan algoritmaları yenileriyle değiştirebilmeniz için Kazançlardaki istatistikleri günlüğe kaydedin. Optimizasyon çalışmalarını en başarılı rutine yoğunlaştırın. Donanım, veritabanı veya veri kaynağındaki herhangi bir değişiklikten sonra istatistiklere özellikle dikkat edin. Mümkünse bu bilgileri istatistik günlüğüne ekleyin, böylece günlük tarih / zaman damgasından anlamanız gerekmez.


3

Kısa bir süre önce mevcut çeşitli algosların performansını ölçmek için güzel bir araç keşfettim: http://www.dmi.unict.it/~faro/smart/index.php

Yararlı bulabilirsiniz. Ayrıca, alt dize arama algoritması hakkında hızlı bir çağrı almak zorunda kalırsam, Knuth-Morris-Pratt ile giderdim.


Bağlantı için teşekkürler. Testler tipik vaka zamanlaması için ilginç görünür ancak en kötü durum zamanlarını yakalamak için değil.
R .. GitHub DUR YARDIMCI ICE

2

Ayrıca, performans üzerinde büyük bir etkisi olabileceğinden, çeşitli dize türleriyle çeşitli ölçütlere sahip olmak isteyebilirsiniz. Algos, doğal dili araştırmaya (ve hatta burada farklı morfologlar nedeniyle hala ince taneli ayrımlar olabilir), DNA dizelerine veya rastgele dizelere vb.

Alfabe boyutu, iğne boyutu gibi birçok algosta da rol oynayacaktır. Örneğin Horspool, İngilizce metinde iyi fakat farklı alfabe boyutu nedeniyle DNA'da kötü, kötü karakter kuralı için hayatı zorlaştırıyor. Son ekin tanıtılması bunu büyük ölçüde ortadan kaldırır.


0

Bunun en iyisi olup olmadığını bilmiyorum, ama Boyer-Moore ile iyi bir deneyim yaşadım .


Boyer-Moore'un kötü vardiya tablosunu İki Yönlü ile birleştirmenin bir yolunu biliyor musunuz? Glibc uzun iğneler (> 32 bayt) için bunun bir varyantını yapar, ancak sadece son baytı kontrol eder. Sorun, iki yönlü iğnenin sağ tarafını soldan sağa araması gerektiğinde, Boyer-Moore'un kötü kayması sağdan sola doğru arama yaparken en etkili olanıdır. İki yönlü (sağa sola tablo veya normal iki yönlü sağ yarı uyuşmazlığı ile ilerleyin, hangisi daha uzunsa) soldan sağa kullanmayı denedim, ancak çoğu durumda normal İki Yönlü'ye göre% 5-10 yavaşlama ve performansı geliştirdiği durumlar bulamadı.
R .. GitHub BUZA YARDIMCI DURDUR

0

Bu doğrudan soruyu cevaplamaz, ancak metin çok büyükse, üst üste binen bölümlere bölünmesine (bir desen uzunluğuyla örtüşmeye), sonra aynı anda bölümleri iplik kullanarak arayın. En hızlı algoritma ile ilgili olarak, Boyer-Moore-Horspool Bence Boyer-Moore varyantları arasında en hızlı olmasa da en hızlılarından biri. Bu konuda Algoritma BMH (Boyer – Moore – Horspool) Arama'dan daha hızlı bir çift Boyer-Moore varyantı (isimlerini bilmiyorum) yayınladım .


0

En hızlısı S. Faro ve OM Kulekci tarafından EPSM. Bkz. Http://www.dmi.unict.it/~faro/smart/algorithms.php?algorithm=EPSM&code=epsm

SIMD SSE4.2 (x86_64 ve aarch64) için optimize edilmiş "Tam Paketlenmiş Dize Eşleme". Tüm boyutlarda istikrarlı ve en iyi performansı gösterir.

Bağlandığım site, 199 hızlı dize arama algoritmasını karşılaştırıyor ve olağan olanları (BM, KMP, BMH) oldukça yavaş. EPSM, bu platformlarda burada belirtilen diğerlerinden daha iyi performans gösterir. Aynı zamanda en sonuncusu.

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.