Bir sayının regex ile asal olup olmadığı nasıl belirlenir?


128

RosettaCode'da Java için aşağıdaki kod örneğini buldum :

public static boolean prime(int n) {
  return !new String(new char[n]).matches(".?|(..+?)\\1+");
}
  • Özellikle Java bilmiyorum, ancak normal ifadenin kendisi dışında bu pasajın tüm yönlerini anlıyorum
  • Yerleşik PHP işlevlerinde bulabileceğiniz gibi, temelden temelden ileri düzeye kadar Regex bilgisine sahibim

.?|(..+?)\\1+Asal sayılarla nasıl eşleşir?


9
@Amir Rachum: !new String(new char[n]).matches(".?|(..+?)\\1+")eşdeğerdir !((new String(new char[n])).matches(".?|(..+?)\\1+")).
Gumbo

14
Bu sadece hesaplama açısından pahalı değil, aynı zamanda potansiyel olarak yıkıcı bir şekilde bellek pahalıdır. Asal sayıları bulmak için kullanılan algoritma çok basit olduğu için (neden dünyada bunu karmaşıklaştırıp bu kadar savurgan hale getirdiği için) karşı tavsiyem olan bu yaklaşımı herhangi biri kullanmayı seçerse, "yeni karakter [n ] "makul bir eşiğin altında olduğundan emin olmak için. Örneğin "prime (Integer.MAX_VALUE)" çağırın ve OutOfMemoryError attığında bir hata dosyalayın.
nicerobot

28
@nicerobot: Aydınlatmak mı?
Cam

6
@nicerobot: Aslında bunu geri alıyorum. Başlangıçta, bu sorunun akademik doğasının, onun yalnızca öğrenme amacıyla kullanılmasını ima ettiğini ve senin iğrenç bir dangalak olduğunuzu düşündüm. Ancak ikinci düşüncede durum bu değil; soruda regex'in yalnızca öğrenme amaçlı olduğu hiç bahsedilmiyor veya ima edilmiyor. Aslında, benim ilk izlenimim, kod parçacıkları söz konusu olduğunda çok basit göründüğü, bu yüzden yeni başlayanlar gerçekten de pratikte kullanılabileceğini varsayabilir. +1.
Cam

7
@incrediman endişelenme. Bunu nasıl düşündüğünü görebiliyorum. Nasıl çalıştığını öğrenmekten caydırmak değil, bunu kullanmanın sonuçları konusunda uyarmak sadece niyetimdi. Basit bir "Lütfen bunu dağıtmayın." Yorumumun geri kalanından önce, sizin ilk bakış açınızdan daha az küçümseyici görünebilirdi.
robot

Yanıtlar:


120

Bu kısmı anladığınızı söylediniz, ancak sadece vurgulamak için, üretilen String'in verilen sayıya eşit bir uzunluğu var. Yani dizede, ancak ve ancak, üç karakter vardır n == 3.

.?

Normal ifadenin ilk kısmı "herhangi bir karakter, sıfır veya bir kez" der. Yani temelde sıfır mı yoksa bir karakter mi var - veya yukarıda bahsettiğim her şeye göre n == 0 || n == 1. Eğer eşleşmeye sahipsek, bunun olumsuzlamasını geri getirin. Bu, sıfır ve birin asal OLMADIĞI gerçeğine karşılık gelir.

(..+?)\\1+

Normal ifadenin ikinci kısmı, gruplara ve geri referanslara bağlı olarak biraz daha karmaşıktır. Grup, parantez içindeki herhangi bir şeydir ve daha sonra normal ifade motoru tarafından yakalanacak ve daha sonra kullanılmak üzere saklanacaktır. Geri referans, daha sonra aynı normal ifadede kullanılan, eşleşen bir gruptur.

Grup 1 karakter ve ardından herhangi bir karakterden 1 veya daha fazlasını yakalar. (+ Karakteri, bir veya daha fazla, ancak YALNIZCA önceki karakter veya grup anlamına gelir. Yani bu "iki veya dört veya altı vb. Karakter" değil, "iki veya üç vb." Anlamına gelir. +?, + Gibidir, ancak Mümkün olduğu kadar az karakteri eşleştirmeye çalışır. + normalde yapabiliyorsa tüm dizeyi silip süpürmeye çalışır, bu durumda kötüdür çünkü geri referans kısmının çalışmasını engeller.)

Bir sonraki bölüm geri referans: Aynı karakter dizisi (iki veya daha fazla), yeniden görünüyor. Söz konusu geri referans bir veya daha fazla kez görünür.

Yani. Yakalanan grup, yakalanan doğal bir karakter sayısına (2'den itibaren) karşılık gelir. Söz konusu grup daha sonra bazı doğal sayılarda görünür (ayrıca 2'den itibaren). Bir eşleşme varsa, bu, n-uzunluklu dizeyle eşleşen 2'den büyük veya 2'ye eşit iki sayının çarpımını bulmanın mümkün olduğu anlamına gelir ... yani bileşik bir n'ye sahip olursunuz. Yine, başarılı maçın olumsuzlamasını geri getirin: n asal DEĞİLDİR.

Eşleşme bulunamazsa, 2'den büyük veya 2'ye eşit iki doğal sayıdan oluşan bir çarpım elde edemezsiniz ... ve hem eşleşmeyen hem de asal sayıya sahip olursunuz, dolayısıyla yine olumsuzluk geri gelir. maç sonucunun.

Şimdi görüyor musun? İnanılmaz derecede zor (ve hesaplama açısından pahalı!) Ama aynı zamanda bir kez aldığınızda biraz da basit. :-)

Normal ifade ayrıştırmanın gerçekte nasıl çalıştığı gibi başka sorularınız varsa, ayrıntılara girebilirim. Ancak bu cevabı şimdilik basit tutmaya çalışıyorum (veya olabildiğince basit).


10
Bu mantığı chrome dev konsolunda JS ile denedim. web sayfasında. ve kontrol etmek için 5'i geçti. Sayfa çöktü!
Amogh Talpallikar

Aşağıdaki yorum daha iyi bir açıklama sağlar. Lütfen devam etmeden önce okuyun!
Ivan Davidov

"Daha iyi" özneldir - soruna farklı bir açıdan yaklaştığını ve bu cevabın harika bir tamamlayıcısı olduğunu söyleyebilirim. :-)
Platinum Azure

1
Aslında bunu daha ayrıntılı olarak açıklayan bir blog yazısı yazdım: Bir Sayının Asal Olup Olmadığını Kontrol Eden Normal İfadenin Gizemini Çözmek .
Illya Gerasymchuk

73

Asallık testinin dışındaki regex kısmını açıklayacağım: aşağıdaki düzenli ifade verilen String s, yinelemeden oluşan a String t, bulur t.

    System.out.println(
        "MamamiaMamamiaMamamia".replaceAll("^(.*)\\1+$", "$1")
    ); // prints "Mamamia"

Çalışma şekli, normal ifadenin (.*)içine girmesi \1ve ardından \1+onu takip edip etmediğini görmesidir . kullanılması ^ve$ sağlayan bir maç bütün dize olması gerektiğini.

Yani, bir bakıma, String s"çarpanı" olan bize verilir String tve normal ifade bunu bulacaktır t( \1açgözlü olduğundan mümkün olan en uzun olanı ).

Bu normal ifadenin neden çalıştığını anladıktan sonra (şimdilik OP'nin normal ifadesindeki ilk alternatifi görmezden gelerek), asallık testi için nasıl kullanıldığını açıklamak basittir.

  • Asallığını test etmek için n, önce bir Stringuzunluk oluşturun n(aynısıyla doldurulmuş char)
  • Normal Stringifade , kiçine bir miktar (örneğin ) yakalar \1ve diğeriyle eşleştirmeye çalışır \1+.String
    • Bir eşleşme varsa, o zaman ntam katıdır kve bu nedenle nasal değildir.
    • kEşleşme yoksa bölen böyle bir şey yoktur nve nbu nedenle asal

.?|(..+?)\1+Asal sayılarla nasıl eşleşir?

Aslında öyle değil! Bu maçları String uzunluğu DEĞİL asal!

  • .?: Değişimli Stringuzunluk eşleşmelerinin ilk bölümü 0veya 1(tanım gereği asal DEĞİL)
  • (..+?)\1+: Sırayla değişen ikinci kısmı, düzenli ifade bir varyasyonu, yukarıda açıklandığı gibi, stoktaki Stringuzunluğunun niçinde bir "çoklu" olduğu Stringuzunluğunun k >= 2(diğer bir deyişlen bileşik, değil bir asal).
    • İsteksiz değiştiricinin ?aslında doğruluk için gerekli olmadığını, ancak könce daha küçük deneyerek süreci hızlandırmaya yardımcı olabileceğini unutmayın.

Not ! booleaniçinde tamamlayıcı operatörü returnaçıklamada: değilleme matches. Regex zaman var ETMEZ maç, nasal! Bu ikili negatif bir mantık, bu yüzden kafa karıştırıcı olmasına şaşmamalı !!


sadeleştirme

İşte kodu daha okunaklı hale getirmek için basit bir yeniden yazma:

public static boolean isPrime(int n) {
    String lengthN = new String(new char[n]);
    boolean isNotPrimeN = lengthN.matches(".?|(..+?)\\1+");
    return !isNotPrimeN;
}

Yukarıdakiler esasen orijinal Java koduyla aynıdır, ancak mantığın anlaşılmasını kolaylaştırmak için yerel değişkenlere atamalarla birden çok ifadeye bölünmüştür.

Ayrıca, aşağıdaki gibi, sonlu tekrar kullanarak normal ifadeyi basitleştirebiliriz:

boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");

Yine, bir Stringuzunluk verildiğinde n, aynısı ile dolu char,

  • .{0,1}kontrol eder n = 0,1, asal DEĞİL
  • (.{2,})\1+asal DEĞİL, ndoğru bir katı olup olmadığını kontrol ederk >= 2

İsteksiz değiştiricinin ?açık olması dışında \1(açıklık amacıyla atlanmıştır), yukarıdaki normal ifade orijinal ile aynıdır.


Daha eğlenceli normal ifade

Aşağıdaki normal ifade benzer bir teknik kullanır; eğitici olmalı:

System.out.println(
    "OhMyGod=MyMyMyOhGodOhGodOhGod"
        .replaceAll("^(.+)(.+)(.+)=(\\1|\\2|\\3)+$", "$1! $2! $3!")
); // prints "Oh! My! God!"

Ayrıca bakınız


6
+1: Sanırım yaklaşımınız benimkinden daha iyi. Neden bu kadar çok oy aldığım ya da onay işaretim hakkında hiçbir fikrim yok ... bunu daha çok hak ediyorsun, sanırım. :-( Üzgünüm
Platinum Azure

@Platinum: Vay canına, bunu herkesin önünde söyleyeceğini hiç düşünmemiştim! Destek için teşekkürler. Belki bundan bir [Populist]gün alırım .
poligenelubricants

2
Şey, bu sadece gerçek (anladığım kadarıyla) ... gerçekten büyük bir anlaşma değil. Temsil için burada değilim (her zaman bir bonus ve hoş bir sürpriz olsa da) ... Yapabildiğimde soruları cevaplamaya çalışıyorum. Bu nedenle, birisinin bunu belirli bir soruda benden daha iyi yaptığını kabul edebilmem şaşırtıcı olmamalı.
Platinum Azure

25

Güzel normal ifade numarası (çok verimsiz olsa da) ... :)

Normal ifade, asal olmayanları şu şekilde tanımlar:

Sadece ve ancak N <= 1 VEYA N bir K> 1 ile bölünebiliyorsa, N asal değildir.

N'nin basit dijital gösterimini normal ifade motoruna geçirmek yerine, tekrar eden bir karakterden oluşan N uzunluğunda bir dizi ile beslenir . Ayrılmanın ilk bölümü N = 0 veya N = 1 olup olmadığını kontrol eder ve ikincisi geri referansları kullanarak bir bölen K> 1 arar. Düzenli ifade motorunu, diziyi oluşturmak için en az iki kez tekrarlanabilen boş olmayan bazı alt diziler bulmaya zorlar. Böyle bir alt dizi varsa, uzunluğu N'yi böldüğü anlamına gelir, dolayısıyla N asal değildir.


2
İşin garibi, diğer uzun ve daha teknik açıklamaları defalarca okuduktan sonra bile, bu açıklamayı kafamda 'klik' yapan açıklama olarak buldum .
Eight-Bit Guru

2
/^1?$|^(11+?)\1+$/

1 tabanına dönüştürdükten sonra sayılara uygulayın (1 = 1, 2 = 11, 3 = 111, ...). Asal olmayanlar bununla eşleşecek. Eşleşmiyorsa, asaldır.

Açıklama burada .

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.