Я поясню частину регулярних виразів поза тестом на первинність: наступний регулярний вираз, поданий а, String s
який складається з повторення String t
, знахідки t
.
System.out.println(
"MamamiaMamamiaMamamia".replaceAll("^(.*)\\1+$", "$1")
); // prints "Mamamia"
Як це працює в тому , що регулярні вирази захоплює (.*)
в\1
, а потім бачить , якщо там \1+
після нього. Використання ^
та $
гарантує, що відповідність має бути з усієї рядка.
Таким чином, ми певним чином даємо String s
, що є "кратним" String t
, і регулярний вираз знайде таке t
(найдовше можливо, оскільки \1
жадібний).
Після того, як ви зрозумієте, чому працює цей регулярний вираз, тоді (ігноруючи першу чергу в регексе OP), пояснюючи, як він використовується для тестування первинності, це просто.
- Щоб перевірити первинність
n
, спочатку створіть String
довжину n
(заповнену однаковоюchar
)
- Режекс фіксує
String
деяку довжину (скажімо k
) \1
, і намагається відповідати \1+
рештіString
- Якщо є відповідність, то
n
це правильна кратна величина k
, і тому n
не є простим.
- Якщо немає відповідності, то не
k
існує такого , що розділяє n
, і n
тому є простим
Як .?|(..+?)\1+
співпадає просте число?
Насправді це не так! Це збігається String
, довжина якого НЕ є простим!
.?
: Перша частина чергування збігів String
довжини 0
або 1
(НЕ простим за визначенням)
(..+?)\1+
: Друга частина чергування, зміна регулярного вираження, пояснена вище, збігається String
по довжині n
, "кратній" String
довжини k >= 2
(тобто n
є складовою, а не простою).
- Зауважте, що модифікатор неохоче
?
насправді не потрібен для коректності, але це може допомогти прискорити процес, спробувавши k
спочатку менші розміри
Зауважте !
boolean
оператор доповнення у return
виписці: він заперечує значення matches
. Це коли регекс НЕ співпадає, n
це прем'єр! Це подвійна негативна логіка, тому не дивно, що це заплутано !!
Спрощення
Ось просте переписування коду, щоб зробити його більш читабельним:
public static boolean isPrime(int n) {
String lengthN = new String(new char[n]);
boolean isNotPrimeN = lengthN.matches(".?|(..+?)\\1+");
return !isNotPrimeN;
}
Вищезазначене по суті те саме, що і вихідний код Java, але розбито на декілька операторів з присвоєнням локальним змінним, щоб полегшити розуміння логіки.
Ми також можемо спростити регулярне вираження, використовуючи скінченне повторення, наступним чином:
boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");
Знову ж , з огляду на String
довжину n
, наповнений такою ж char
,
.{0,1}
перевіряє, чи n = 0,1
НЕ є простим
(.{2,})\1+
перевіряє, чи n
є правильним кратним k >= 2
, а не простим
За винятком модифікатора неохоче, який ?
увімкнено \1
(опущено для ясності), вищевказаний регулярний вираз є ідентичним оригіналу.
Веселіший вираз
Наступний регекс використовує аналогічну техніку; це має бути навчальним:
System.out.println(
"OhMyGod=MyMyMyOhGodOhGodOhGod"
.replaceAll("^(.+)(.+)(.+)=(\\1|\\2|\\3)+$", "$1! $2! $3!")
); // prints "Oh! My! God!"
Дивитися також
!new String(new char[n]).matches(".?|(..+?)\\1+")
еквівалентно!((new String(new char[n])).matches(".?|(..+?)\\1+"))
.