簡體   English   中英

可被 3 整除的二進制數的正則表達式

[英]Regular Expression for Binary Numbers Divisible by 3

我在自學正則表達式,在網上發現了一個有趣的練習題,就是寫一個正則表達式來識別所有可以被 3 整除的二進制數(並且只有這樣的數)。 老實說,問題要求為這種情況構建 DFA,但我認為使用正則表達式應該等效。

我知道有一個小規則來確定一個二進制數是否可以被 3 整除:取數字中偶數位的 1 數,然后減去數字中奇數位的 1 數 - 如果這等於零,該數字可被 3 整除(例如:110 - 1 在偶數 2 插槽中,1 在奇數 1 插槽中)。 但是,我在將其調整為正則表達式時遇到了一些麻煩。

我最接近的是意識到數字可以是 0,所以這將是第一個狀態。 我還看到所有可被 3 整除的二進制數都以 1 開頭,因此這將是第二種狀態,但我被困在那里。 有人可以幫忙嗎?

按照 Oli Charlesworth 所說的,您可以構建 DFA 以將基數b數除以某個除數d ,其中 DFA 中的狀態代表除法的余數。

對於您的情況(基數 2 - 二進制數,除數d = 3 10 ):

初始 DFA

請注意,上面的 DFA 接受空字符串作為可被 3 整除的“數字”。這可以通過在前面再添加一個中間狀態來輕松解決:

固定 DFA

轉換為理論正則表達式可以用正常的過程完成

獲得 DFA 后,可以輕松轉換為支持遞歸正則表達式的風格的實用正則表達式。 這在 CodeGolf.SE 的這個問題中的 (base b = 10, d = 7 10 ) 的情況下顯示。

讓我引用Lowjacker 的答案中的正則表達式,用 Ruby 正則表達式編寫:

(?!$)(?>(|(?<B>4\g<A>|5\g<B>|6\g<C>|[07]\g<D>|[18]\g<E>|[29]\g<F>|3\g<G>))(|(?<C>[18]\g<A>|[29]\g<B>|3\g<C>|4\g<D>|5\g<E>|6\g<F>|[07]\g<G>))(|(?<D>5\g<A>|6\g<B>|[07]\g<C>|[18]\g<D>|[29]\g<E>|3\g<F>|4\g<G>))(|(?<E>[29]\g<A>|3\g<B>|4\g<C>|5\g<D>|6\g<E>|[07]\g<F>|[18]\g<G>))(|(?<F>6\g<A>|[07]\g<B>|[18]\g<C>|[29]\g<D>|3\g<E>|4\g<F>|5\g<G>))(|(?<G>3\g<A>|4\g<B>|5\g<C>|6\g<D>|[07]\g<E>|[18]\g<F>|[29]\g<G>)))(?<A>$|[07]\g<A>|[18]\g<B>|[29]\g<C>|3\g<D>|4\g<E>|5\g<F>|6\g<G>)

分解它,您可以看到它是如何構建的。 原子分組(或非回溯組,或具有所有格行為的組)用於確保僅匹配空字符串替代項。 這是在 Perl 中模擬(?DEFINE)的技巧。 那么當數字除以7時,組AG對應於0到6的余數。

(?!$)
(?>
  (|(?<B>4   \g<A>|5   \g<B>|6   \g<C>|[07]\g<D>|[18]\g<E>|[29]\g<F>|3   \g<G>))
  (|(?<C>[18]\g<A>|[29]\g<B>|3   \g<C>|4   \g<D>|5   \g<E>|6   \g<F>|[07]\g<G>))
  (|(?<D>5   \g<A>|6   \g<B>|[07]\g<C>|[18]\g<D>|[29]\g<E>|3   \g<F>|4   \g<G>))
  (|(?<E>[29]\g<A>|3   \g<B>|4   \g<C>|5   \g<D>|6   \g<E>|[07]\g<F>|[18]\g<G>))
  (|(?<F>6   \g<A>|[07]\g<B>|[18]\g<C>|[29]\g<D>|3   \g<E>|4   \g<F>|5   \g<G>))
  (|(?<G>3   \g<A>|4   \g<B>|5   \g<C>|6   \g<D>|[07]\g<E>|[18]\g<F>|[29]\g<G>))
)
(?<A>$|  [07]\g<A>|[18]\g<B>|[29]\g<C>|3   \g<D>|4   \g<E>|5   \g<F>|6   \g<G>)

我有另一種方法來解決這個問題,我認為這更容易理解。 當我們將一個數除以 3 時,我們可以得到三個余數:0、1、2。我們可以使用表達式3tt是自然數)來描述一個可以被 3 整除的數。


當我們在余數為 0 的二進制數后加 0 時,實際的十進制數將翻倍。 因為每個數字都在向更高的位置移動。 3t * 2 = 6t ,這也可以被 3 整除。

當我們在余數為 0 的二進制數后加 1 時,實際的十進制數會加倍加 1。 3t * 2 + 1 = 6t + 1 ,余數為1。


當我們在余數為1的二進制數后加1時,實際的十進制數會加一加一,余數為0; (3t + 1)*2 + 1 = 6t + 3 = 3(2t + 1) ,這可以被 3 整除。

當我們在余數為 1 的二進制數后加 0 時,實際的十進制數將被加倍。 余數將是 2. (3t + 1)*2 = 6t + 2


當我們在余數為 2 的二進制數后加 0 時,余數為 1。 (3t + 2)*2 = 6t + 4 = 3(2t + 1) + 1

當我們在余數為 2 的二進制數后加 1 時,余數仍為 2。 (3t + 2)*2 + 1 = 6t + 5 = 3(2t + 1) + 2.

無論余數為 2 的二進制數加多少 1,余數永遠為 2。 (3(2t + 1) + 2)*2 + 1 = 3(4t + 2) + 5 = 3(4t + 3) + 2


所以我們可以用 DFA 來描述二進制數: DFA 描述可被 3 整除的二進制數

注意:q2 -> q1應標記為 0。

可被 3 整除的二進制數分為 3 類:

  1. 有兩個連續的 1 或兩個 1 被偶數個 0 分隔的數字。 實際上,每一對都“取消”了自己。

(例如 11, 110, 1100,1001,10010, 1111)

(十進制:3、6、12、9、18、15)

  1. 三個 1 由奇數個 0 分隔的數字。 這些三胞胎也“取消”了自己。

(例如 10101、101010、1010001、1000101)

(十進制:21、42、81、69)

  1. 前兩條規則的某種組合(包括彼此內部)

(例如 1010111、1110101、1011100110001)

(十進制:87、117、5937)

因此,考慮到這三個規則的正則表達式很簡單:

0*(1(00)*10*|10(00)*1(00)*(11)*0(00)*10*)*0*

如何閱讀:

() 封裝

* 表示前一個號碼/組是可選的

| 表示括號內任一側的選項選擇

您遇到的問題是,雖然您的技巧(可能)是有效的,但它並沒有映射到實際的 DFA(您必須跟蹤偶數和奇數之間的潛在任意差異,這需要任意數字狀態)。

另一種方法是注意(從 MSB 到 LSB)在第i個字符x[i] ,您的子字符串在模 3 算術中必須等於 0、1 或 2; 將此值稱為S[i] x[i+1]必須是 0 或 1,這相當於乘以 2 並可選地加 1。

因此,如果您知道S[i]x[i+1] ,則可以計算S[i+1] 這個描述聽起來是不是很熟悉?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM