Java'da Küfür Filtresi Performansı


9

Java tabanlı bir web uygulamasında kullanıcıların gönderilerinden küfür filtrelemek için bir gereksinim var. Müşteri hem Scunthorpe Probleminin hem de Clbuttic Probleminin farkındadır ve sonuçlarını kabul etmiştir. Lütfen, sansür eksikliğinin esası hakkında bir tartışma istemiyorum.

İki veri biti vardır:

  1. Kullanıcının gönderimi, potansiyel olarak 500 kelime içerebilir;
  2. İzin verilmeyen sözcükleri içeren tek sütunlu bir veritabanı tablosu. Bu tabloda binlerce kayıt olabilir.

Mevcut çözüm benim için yanlış görünüyor:

  1. Tüm tablo, başlangıçta bir Singleton'a statik bir String [] olarak yüklenir (böylece bellekte bulunur).
  2. Her kullanıcı gönderimi için dizi boyunca döngü yaparız ve dizede [] içindeki herhangi bir kelimenin gönderimde görünüp görünmediğini görmek için bir .indexOf () yaparız.
  3. Görünüyorsa, yerine% $ # @% stilinde karakterler yerleştiririz. Bu, kullanıcı gönderimini belirterek, tüm kullanıcı gönderiminde belirteçler (tekrar) olarak döngü yaparak ve bulunan kelimenin her bir örneğini değiştirerek yapılır.

Bu çözümde parlaklık olabilir, ama şüpheliyim. Ve bir süreliğine ona baktıktan sonra yolumu bulamıyorum.

Sorular, iyi performans verecek ve umarım daha önce hiç duymadığım bazı belirsiz kelimeleri filtrelemediğim için kovulduktan sonra gelecekteki geliştiricilerin sürdürmesi için makul bir aklı başında olacak bir çözüm nedir?


Bize bunun neden yanlış olduğunu düşündüğünüzü söylemeden size yanlış geldiğini söylüyorsunuz. O zaman bize söylemeden, mevcut çözümün yeterli olmadığı bir performans çözümü istersiniz. Saniyede kaç metin alıyorsunuz, kaç tanesini işleyebiliyorsunuz?
kullanıcı bilinmiyor

Çözümün yanlış olduğunu düşündüm, çünkü öncelikle çalıştığım kod tabanı yetersiz ve özensiz. Önyargım göz önüne alındığında, kendi güvensizliğime güvenmedim. Başkalarının görüşünün faydalı olacağını hissettim. Benim için alarm veren şeyler, kullanıcının gönderdiği çok daha küçük veri kümesi yerine String [] döngüsünün içine yerleştirilen String [] (bu 1999 nedir?) jetonlu kullanıcı gönderme ile vb. Beklenen kullanım belirtilmemiş, ideal olarak makul performansa sahip zarif bir çözüm güzel olurdu.
blueishgoldfish

2
'Makul performans' her şey anlamına gelebilir. Somut bir hedefiniz yoksa, hedefe ulaşıp ulaşmadığınızı bilemezsiniz. Bir işlemi 100 kat daha hızlı olacak şekilde hızlandırırsanız - bu bir hedef midir? Kullanıcı 1ms veya 1 / 10s bekliyorsa? Kullanıcı işinizden faydalanmayacaktır.
kullanıcı bilinmiyor

Yanıtlar:


18

Bir kelime filtresini akıllıca yapmanın tek yolu fonik eşleme sistemi kullanmaktır. Birkaç yıl önce Java'da onlarca ve gençler için çok popüler bir kitlesel çok oyunculu çevrimiçi oyun için çok etkili bir küfür filtresi yazdım.

Mümkün olduğunca çok şeyle eşleşecek olan varsayılan yerine daha doğru olacak şekilde ayarlanmış , oldukça değiştirilmiş bir Çift MetaPhone algoritmasına dayanıyordu . Gerçek sözlerle aynı yanlış yazımları ve fonetik yazımları seçtiği için son derece etkiliydi. Eklediğim konuşmasını ve daha bir Üç / Dört Metafon algoritmasının yapma, hem de metaphone algoritmasına konuşmasına.l33ttxt

Çalışan harfleri w o r d ssıkıştıran ve harfleri akıllıca sıkıştırarak ve çalışan kopyaları ortadan kaldırarak gibi şeyler koyan çocuklar gibi şeyleri algılayan bir ön işlemciye sahipti wwoorrddss, sadece İngilizce için çok özeldi.

8 yıl önce, tek çekirdekli bir CPU sisteminde on binlerce kullanıcıyla fark edilir bir gecikme olmadan gerçek zamanlı sohbet sistemi akışında kullanılmak için yeterince hızlıydı.

Veritabanındaki bir tabloda Metaphone olarak kodlanmış kelimelerin bir listesi vardı ve şaşırtıcı derecede küçük olan statik bir Haritaya yüklendi ve yasaklanan kelimelerin listesine erişmek için özel bir şey yapmamız gerekmedi, ekleyebildim neredeyse ücretsiz için aynı teknikleri kullanarak ifade algılama.

Tabii ki sistemi gerçek zamanlı olarak kırmaya çalışan binlerce çocuğun tüm sohbetleri için bir çalışma günlüğü vardı, bu yüzden çalışmak için oldukça kapsamlı bir veri kümesi vardı. Ben günlüğü yaptığı gibi birisi bir pozitifliği söz filtresi tetiklendiğinde, önümüzdeki birkaç sohbet mesajı günlüğe oldu etmedi Olabilirim, onlar belirli bir kelimenin veya kelime etrafında bir yol bulmak yaptıysam, bu şekilde onlardan filtreyi tetiklemek sistemimi uyarla ve yakala. Birkaç hafta sonra oldukça kurşun geçirmezdim.


3
Bu çözüm en iyisidir. Sorun bir öğleden sonra çözmem gerekti (ya da bu noktada). Yeterli zaman varsa, Double MetaPhone yaklaşımını alacağım ya da bunu yapmak için işe alacağım. :-)
blueishgoldfish

Yani, sanırım insanların yarısı artık oyunu oynamayı bırakacak: D
Davor Ždralo

2

Eşleştirmeyi verimli bir şekilde yapmak istiyorsanız, Aho Corasick algoritması oldukça iyi bir seçenektir (eminim, etrafında yüzen bir Java uygulaması bulabilirsiniz).

Elbette, yazım düzensizliklerini ('$' -> 's', '@' -> 'a', '| <' -> 'k' vb.) Değiştirmek için gönderimi önceden işlemek isteyeceksiniz.


Tam olarak aradığım şey, teşekkürler! İşte bir Java uygulaması: hkn.eecs.berkeley.edu/~dyoo/java
Remi Mélisson

0

Statik bir String [] içine yüklemek yerine, HashMap [] veya başka bir ikili ağaç türü kullanın (aramayı iyileştirmek istiyorsanız) dizeyi karma içindeki anahtarınız haline getirin. Dizenizi boşluklara ayırın ve noktalama işaretlerini kaldırın. Daha sonra dize bölmenizdeki her sözcük için HashMap'i sorgulayabilirsiniz; eğer hashmap null olmayan ile geri gelirse, kötü bir kelimeniz olduğunu bilirsiniz.

Burada başarısız olan şey, birinin kötü sözcüğün etrafına rastgele karakterler eklediği Clbuttic problemidir. bhassda


Bence son uyarı, bu çözümü neredeyse işe yaramaz kılan şeydir - tam kelime eşleşmeleri dışında herhangi bir şeye genişletmenin yolu yoktur.

Bu adil bir ifadedir; ancak insan zihninin bir küfür filtresinden kaçmak için ortaya koyabileceği her şeyi yakalamak zorlaşır. Tüm seçenekleri birleştirmek için OR ifadeleriyle her zaman büyük bir normal ifade oluşturabilir ve ardından normal ifadeyi girişle eşleştirebilirsiniz. VEYA veritabanına "kötü kelime alanı" ile veritabanından bir RLIKE girişine karşı bir seçim yapabilirsiniz. Dönüş kötü sözcüğü belirtir ve kötü sözcüğü de döndürür.

@Suroot, sorumun konuştuğu gibi fonetik eşleme ile hemen hemen herhangi bir kelime veya ifadeyi yakalamak zor değil. Mutlak eşleşmeler hiçbir zaman çalışmaz veya ölçeklenmez, ancak fonetik eşleme, ayarlayabildiğiniz gibi ayarladıktan sonra% 100'e yakın çalışır.

-1

Bir fonik sistem kullanmak hiçbir şekilde tek çözüm değildir, ancak en basit olabilir, çünkü bu tür şeyleri yapan çok sayıda açık kaynak kütüphanesi vardır.

Zor kısım her zaman herhangi bir algoritmanın eşleşen kısmı olacak ve maçınız oldukça yavaş ve saf gibi görünüyor. İndexOf'un bir tür yardımcı kontrol olmadan doğru eşleşeceğini varsayamazsınız.

Buna ek olarak, N dizesinin tamamında döngü oluşturacaksınız; burada N, kara listenizdeki kelime sayısıdır. Set veya HashMap kullanma önerileri kesinlikle işleri biraz geliştirecektir.

Çoğu durumda, doğrusal durum tabanlı bir algoritma en iyi ve en hızlıdır. Clean Speak için çözüm yazdım ve bu tür algoritmayı bir işlem öncesi fonik eşleme sistemi ile kullanıyor. Bu, küfür gömüldüğünde (foo küfürse, gömme foosucker ise) karmaşık olmadığında ve yüksek bir performans seviyesini koruyabilen tek çözümdü. Ayrıca, yeni kodeksleri uygulamadan diğer diller için de güzelce ölçeklenir.

Son olarak, herhangi bir formun ön-işlenmesi genellikle kaçınılması gereken bir şeydir. Çoğu durumda, dizedeki karakterlerin her birini işlerken aynı şeyi doğrusal bir şekilde yapabilirsiniz.

Tabii ki, uzun vadede diğer çözümlere bakmanızı öneririm çünkü kullanıcı tarafından oluşturulan içeriğin işlenmesi çoğu uygulamada küfür filtrelemeden daha karmaşıktır. Genellikle e-postalar ve sosyal güvenlik numaraları gibi kişisel bilgileri ve bazen URL'ler gibi şeyleri de filtrelemek istersiniz. Ayrıca, çoğu uygulamanın bir çeşit denetleme sistemi ve içerik aramaya ihtiyacı olduğunu bulduk. Bunlar karmaşıklığı önemli ölçüde artırır.


-2

Böyle bir durumda yapmak istediğiniz şey, iki kelime listesinden hangisinin daha küçük olduğunu belirlemektir. Diyelim ki "verboten" listeniz 2000 kelime içeriyor ve maksimum kullanıcı gönderimi 500 kelime. Bu durumda, kullanıcı gönderisindeki kelimeler listesini tekrarlar ve yasak kelimeler listesinde teker teker ararsınız, bunun tersi de geçerlidir.

Yapacağım diğer değişiklik, bir String [] içindeki yasak sözcüklerin listesini tutmamanızdır - dizide arama yaparsanız, kullanıcı gönderiminde her kelime için O (n) aramanız olur. Bu oldukça kötü. Baktığınız veri yapısını, daha iyi bir arama performansına sahip bir tür ilişkisel kap veya ağaç yapısına yerleştirmeye çalışacağım (n yerine log n). Buradaki zorluk, kullanıcı gönderimini bu kapsayıcıya koyarsanız, kelime konumunu izlemeniz gerekir, böylece girişi yeniden yapılandırabilir veya bir arama isabetiniz varsa giriş dizesini güncelleyebilirsiniz.

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.