最近在學習正則表達式,偶然間看到利用正則表達式判斷一個數是不是素數的帖子。當時就震驚了,覺得好神奇。那個判斷素數的函數是這樣子的:
public static bool IsPrime(int i) { return !Regex.IsMatch(new String('*', i), "^.?$|^(..+?)\\1+$"); }
有沒有覺得很神奇?我當時就覺得相當的有想象力的一種實現。那讓我們看一下這個正則表達式是如何做判斷素數的。
-
第一步,創建了一個長度為i,並以'*'填充的字符串。
-
第二步,讓這個后面的正則表達式去匹配這個字符串,如果匹配則不是質數。
過程很簡單。讓我們看一下這個正則表達式。
能被這個正則表達式匹配出則這個數不是素數,這個正則表達式可以分成兩部分看,
- 第一部分:
^.?$
。^
匹配字符串起始位置,$
匹配字符串結束位置,.
表示匹配單個任意字符,?
表示可選。合起來的意思就是匹配只有一個字符或者沒有字符的字符串,也就是0或者1。0或1不是質數,這里特殊匹配這兩個數字。 - 第二部分:
^(..+?)\1+$
。這部分是關鍵。先拆開來解釋一下意思,^
和$
就不解釋了。(..+?)
表示匹配兩個及以上的任意字符,這里的+?
表示它是忽略優先(lazy)的,對結果來說沒有影響,出於性能考慮。注意到這個表達式有一個括號,這個是一個捕獲型括號。\1
是反向引用,引用的內容就是前面第一個括號選中的內容。那么后面\1+
的意思就是匹配前面括號匹配中的內容一次或多次。
這個表達式基本內容解釋完了,那么為什么能夠匹配非素數呢?
匹配的過程大致是這樣子的,(..+?)
首先匹配2個字符,然后\1+
匹配2的倍數個字符,如果能夠匹配是不是就說明了這個數是2的倍數啦?當匹配失敗的時候,匹配引擎將(..+?)
匹配三個字符,然后\1+
匹配3的倍數個字符,如果能夠匹配則是3的倍數。依此類推。。。。。。
如果匹配了則說明這個數必定能夠被某一個數整除,如果匹配完都沒匹配到則說明從2到n-1的數都不能整除n,那就證明這個數是素數!
題外話
(..+?)
中?
的作用,我認為這個是為了提高性能的。如果沒有?
的話(..+?)
會試圖把所有的字符都匹配完,然后發現后面還有表達式,於是從最后一個位置開始回溯。而加了?
則是從第二個就開始往后去匹配。
打個比方:我們匹配12。^.?$|^(..+)\1+$
會做的事情是先判斷12是不是11的倍數、then 12是不是10的倍數........then 12是不是6的倍數。^.?$|^(..+?)\1+$
會做的是先判斷12是不是2的倍數,then 就沒then了。對於素數的效率是一樣的,因為每個位置都需要匹配過來,但是對於非素數從小開始匹配則能夠讓這個表達式早點結束匹配。
如果你覺得我解釋的不清楚沒明白,下面有其他人的解釋: