一行正則表達式判斷質數的代碼
背景
昨天無意中看到一篇大佬的文章Primality regex(正則表達式判斷質數),驚為天人,正則表達式也能用來判斷質數瞭?立馬來研究下
示例
perl -wle 'print "Prime" if (1 x shift) !~ /^1?$|^(11+?)\1+$/' [number]
翻譯成JS代碼如下
function isPrime(n) { return !/^1?$|^(11+?)\1+$/.test("1".repeat(n)) }
代碼邏輯非常簡單,生成"1" * n
長度的字符串,通過/^1?$|^(11+?)\1+$/
正則表達式進行判斷,再將結果取反
正則分析
/^1?$|^(11+?)\1+$/
上面正則表達式有2個分支,分別是
/^1?$
^(11+?)\1+$
分支1 邏輯很簡單,就是匹配0或者1個 "1"
,因為要排除數字1
(非質數)
分支2 就有意思瞭,可以拆成2塊來看
^(11+?)
\1+$
表達式1,非貪婪模式下匹配 "11" "111" "1111"....
,作為一個分組
表達式2,\1
代表將表達式1匹配的結果賦值給\1
,判斷是否結尾,否的話會觸發回溯(因為表達式1可能匹配多種情況)
舉個例子就更清晰瞭,比如傳入n = 9,分支1不滿足可以直接忽略^(11+?)\1+$
步驟 | 匹配結果 | 說明 |
---|---|---|
step 1 | 1 1 1 1 1 1 1 1 1 | (11+?) 匹配到"11" |
step 2 | 1 1 1 1 1 1 1 1 1 | 分組結果賦值給\1 ,那麼正則就變成 "11"+$,繼續匹配剩餘的字符(7個"1") |
step 3 | 1 1 1 1 1 1 1 1 1 | 再重復3輪的匹配,發現剩餘一個"1",不滿足$,進行回溯 |
step 4 | 1 1 1 1 1 1 1 1 1 | 還是不滿足$,繼續回溯 |
step 5 | 1 1 1 1 1 1 1 1 1 | 一直回溯到step 1 ,(11+?) 匹配到"111" |
step 6 | 1 1 1 1 1 1 1 1 1 | 分組結果賦值給\1 ,那麼正則就變成 "111"+$,繼續匹配剩餘的字符(6個"1") |
step 7 | 1 1 1 1 1 1 1 1 1 | 再重復2輪的匹配,滿足$,匹配成功 |
原理
經過上述的分析,不難發現,其實回溯就是將數字不斷除於2、3、4….,最後檢查是否有餘數,沒有的話就匹配成功(非質數),非常簡單粗暴的窮舉法
優化空間
仔細看正則匹配的過程分析,其實step 3 ~ step 4
的回溯完全沒有必要,那麼正則可以改寫成這樣/^1?$|^(11+?)\1+?$/
,將\1+
改成非貪婪模式\1+?
,那麼就放棄step 4回溯
性能測試
console.time('優化前') console.log(!/^1?$|^(11+?)\1+$/.test("1".repeat(33331))); console.timeEnd('優化前') console.time('優化後') console.log(!/^1?$|^(11+?)\1+?$/.test("1".repeat(33331))); console.timeEnd('優化後') // true // 優化前: 227.9189453125 ms // true // 優化後: 155.797119140625 ms
耗時上減少瞭接近一半
總結
其實這個正則性能非常差(窮舉法),實用性不高,但是思路很讓人驚艷
到此這篇關於一行正則表達式判斷質數的文章就介紹到這瞭,更多相關正則表達式判斷質數內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!