我需要学习如何设计DFA,以便在给定任何数字'n'的情况下,它都接受二进制字符串{0,1},其十进制等效数可以被'n'整除。
对于不同的'n',会有不同的DFA,但是有人可以给出一个基本方法,我应该遵循此基本方法来处理任何0 <n <10的数字。
我需要学习如何设计DFA,以便在给定任何数字'n'的情况下,它都接受二进制字符串{0,1},其十进制等效数可以被'n'整除。
对于不同的'n',会有不同的DFA,但是有人可以给出一个基本方法,我应该遵循此基本方法来处理任何0 <n <10的数字。
n
必须具有log(n)
尾随零。
Answers:
下面,我写了一个n
等于5的答案,但是您可以应用相同的方法来绘制DFA的任意值n
和“任何位置数字系统”,例如二进制,三进制...
首先将术语“完整DFA”简化为在δ:Q×Σ→Q的完整域上定义的DFA, 称为“完整DFA”。换句话说,我们可以说;在完整DFA的转换图中,没有丢失的边(例如,对于Q中的每个状态,Σ中的每种语言符号都存在一个输出边)。注意:有时我们将部分DFA定义为δ⊆Q×Σ→Q(请参阅:如何在DFA的定义中读取“δ:Q×Σ→Q”)。
步骤1:当您将ω除以ω时,n
提醒可以是0、1,...,(n-2)或(n-1)。如果是余数,则0
意味着ωn
不能被整除。因此,在我的DFA中,将存在一个状态q r,它对应于余数r
,其中0 <= r <= (n - 1)
DFA中的状态总数为n
。
在∑上处理了一个数字串ω之后,最终状态为q r表示ω%n => r(%提醒运算符)。
在任何自动机中,状态的目的就像存储元素。原子状态中的状态存储了一些信息,例如风扇的开关,可以判断风扇处于“关闭”状态还是“开启”状态。对于n = 5,DFA中的五个状态对应于五个提醒信息,如下所示:
使用以上信息,我们可以开始绘制五个状态的过渡图TD,如下所示:
图1
因此,有5个状态对应5个余数。在处理了一个字符串ω之后,如果最终状态变为q 0,则意味着输入字符串的十进制等效项可以被5整除。在上图中,q 0被标记为两个同心圆的最终状态。
另外,我已经定义了转换规则δ:(q 0,0)→q 0作为'0'
状态q 0处的符号的自环,这是因为任何字符串的十进制等效项仅'0'
由0组成,而0可被整除n
。
步骤2:上述TD不完整;并且只能处理'0'
s的字符串。现在添加更多的边,以便它可以处理后续数字的字符串。下表列出了可以在下一步中添加的新过渡规则:
──────┬──────┬─────────┐ │数字│二元│余数(%5) │结束状态│ ├──────┼──────┼─────────┼ │One│1│1│q 1 │ ├──────┼──────┼─────────┼ │Two│10│2│q 2 │ ├──────┼──────┼─────────┼ │Three│11│3│q 3 │ ├──────┼──────┼─────────┼ │Four│100│4│q 4 │ ──────┴──────┴─────────┘
'1'
,应该有一个转换规则δ:(q 0,1)→q 1 '10'
,最终状态应该为Q 2,和过程'10'
,我们只需要添加更多的转换规则δ:(Q 1,0)→q 2'11'
,最终状态为q 3,我们需要添加一个转换规则δ:(Q 1,1)→q 3'100'
,最终状态为q 4。TD已经处理了前缀字符串'10'
,我们只需要添加一个新的过渡规则δ:(Q 2,0)→q 4图2
步骤3:5 = 101
图2上的过渡图仍然不完整,并且缺少许多边,例如,对于δ:(q 2,1)- ?没有定义过渡。。并且应该存在规则来处理类似的字符串'101'
。
因为'101'
= 5可被5整除,并且接受,'101'
我将在上面的图2中添加δ:(q 2,1)→q 0。
路径: →(q 0)─1→(q 1)─0→(q 2)─1→(q 0),
用这个新规则,转换图如下:
图3
在每个步骤的下面,我选择下一个后续的二进制数字以添加缺失的边,直到获得TD作为“完整DFA”。
步骤4:六个= 110。
我们可以'11'
将图3中的当前TD处理为:→(q 0)─11→(q 3)─0→(?)。因为6%5 = 1,表示添加一个规则δ:(Q 3,0)→q 1。
图4
步骤5:7 = 111
┌──────┬──────┬─────────┬──────┬ ──┬──────────┐ │数字│二进制│余数(%5) │结束状态│路径 │添加 │ ├──────┼──────┼─────────┼──────┼ ──┼──────────┤ │Seven│111│7%5 = 2│q 2 │q 0 ─11→q 3 │ q 3 ─1→q 2 │ └──────┴──────┴─────────┴──────┴ ──┴──────────┘
图5
步骤6:八= 1000
──────┬──────┬────────┬ ────────┐ │数字│二进制│余数(%5) │结束状态│路径 │添加 │ ├──────┼──────┼─────────┼ ────────┤ │Eight│1000│8%5 = 3│q 3 │q 0 ─100→q 4 │q 4 ─0→q 3 │ ──────┴──────┴────────┴ ────────┘
图6
步骤7:九= 1001
──────┬──────┬────────┬ ────────┐ │数字│二进制│余数(%5) │结束状态│路径 │添加 │ ├──────┼──────┼─────────┼ ────────┤ │Nine│1001│9%5 = 4│q 4 │q 0 ─100→q 4 │q 4 ─1→q 4 │ ──────┴──────┴────────┴ ────────┘
图7
在TD-7中,边的总数为10 == Q×Σ= 5×2。这是一个完整的DFA,可以接受所有可能的二进制字符串,这些十进制等效项可以被5整除。
步骤1与二进制完全相同,使用图1。
步骤2添加零,一,二
────────┬──────┬───────┬ ──────┐ │十进制│三进制│余数(%5) │结束状态│ 添加 │ ├────────┼────────┼──────── ──────┤ │零│0│0│q0│δ:(q0,0)→q0│ ├────────┼────────┼──────── ──────┤ │一个│1│1│q1│δ:(q0,1)→q1│ ├────────┼────────┼──────── ──────┤ │两个│2│2│q2│δ:(q0,2)→q3│ ────────┴──────┴───────┴ ──────┘
图8
步骤3加三,四,五
────────┬──────┬───────┬ ────┐ │十进制│三进制│余数(%5) │结束状态│ 添加 │ ├────────┼────────┼──────── ────┤ │3│10│3│q3│δ:(q1,0)→q3│ ├────────┼────────┼──────── ────┤ │四│11│4│q4│δ:(q1,1)→q4│ ├────────┼────────┼──────── ────┤ │5│12│0│q0│δ:(q1,2)→q0│ ────────┴──────┴───────┴ ────┘
图9
步骤4加上六,七,八
────────┬──────┬───────┬ ────┐ │十进制│三进制│余数(%5) │结束状态│ 添加 │ ├────────┼────────┼──────── ────┤ │六│20│1│q1│δ:(q2,0)→q1│ ├────────┼────────┼──────── ────┤ │7│21│2│q2│δ:(q2,1)→q2│ ├────────┼────────┼──────── ────┤ │8│22│3│q3│δ:(q2,2)→q3│ ────────┴──────┴───────┴ ────┘
图10
步骤5加九,十,十一
────────┬──────┬───────┬ ────┐ │十进制│三进制│余数(%5) │结束状态│ 添加 │ ├────────┼────────┼──────── ────┤ │九│100│4│q4│δ:(q3,0)→q4│ ├────────┼────────┼──────── ────┤ │十│101│0│q0│δ:(q3,1)→q0│ ├────────┼────────┼──────── ────┤ │11│102│1│q1│δ:(q3,2)→q1│ ────────┴──────┴───────┴ ────┘
图11
步骤6加上十二,十三,十四
────────┬──────┬───────┬ ──────┐ │十进制│三进制│余数(%5) │结束状态│ 添加 │ ├────────┼────────┼────────┼ ──────┤ │十二│110│2│q2│δ:(q4,0)→q2│ ├────────┼────────┼────────┼ ──────┤ │十三│111│3│q3│δ:(q4,1)→q3│ ├────────┼────────┼────────┼ ──────┤ │14││112│4│q4│δ:(q4,2)→q4│ └────────┴──────┴───────┴ ──────┘
图12
转换图12中的边总数为15 = Q×Σ= 5 * 3(完整的DFA)。这个DFA可以接受所有{0,1,2}范围内的字符串,这些十进制等效项可以被5整除。
如果您在每一步中注意到,表中有3个条目,因为在每一步中,我都将从状态中添加所有可能的输出边制作完整的DFA(我添加了一条边,以便剩余的q r状态为r
)!
此外,请记住,两种常规语言的结合也是常规的。如果您需要设计一个接受二进制字符串的DFA,则这些十进制等效项可以被3或5整除,然后绘制两个单独的DFA以被3和5整除,然后将两个DFA合并以构造目标DFA(对于1 <= n <= 10您必须合并10个DFA)。
如果要求您绘制接受二进制字符串的DFA,使得十进制等效项可被5和3整除,那么您正在寻找DFA可被15整除的东西(但是6和8呢?)。
注意:仅当数字和底数之间没有公因子时(例如,在第一个示例中不存在5和2之间,在第二个示例中不存在5和3之间),使用此技术绘制的DFA才会最小化DFA ,因此,上面构造的两个DFA都被最小化DFA。如果您有兴趣进一步阅读有关数字和基础文章的可能的迷你状态:可除性和状态复杂性。n
n
b
下面,我添加了一个Python脚本,我在学习Python库pygraphviz时很有趣。我正在添加它,希望它能以某种方式对某人有所帮助。
因此,我们可以运用上述技巧来绘制DFA,以识别'b'
可被给定数字整除的任何基数的数字字符串'n'
。在这种情况下,DFA的状态总数为n
(n
余数),边数应等于'b'*'n'-即完整的DFA:'b'= DFA语言中的符号数,而'n'=状态数。
使用上面的技巧,我在下面编写了Python脚本来绘制DFA作为输入base
和number
。在脚本中,函数会逐步divided_by_N
填充DFA的转换规则base * number
。在每个步骤num
编号中,我都num_s
使用function转换为数字字符串baseN()
。为了避免处理每个数字字符串,我使用了一个临时数据结构lookup_table
。在每个步骤中,num_s
都会评估并存储数字字符串的最终状态,lookup_table
以供下一步使用。
对于DFA的过渡图,我已经draw_transition_graph
使用Pygraphviz库编写了一个函数(非常易于使用)。要使用此脚本,您需要安装graphviz
。为了在过渡图中添加彩色边缘,我为每个符号get_color_dict
函数随机生成了颜色代码。
#!/usr/bin/env python
import pygraphviz as pgv
from pprint import pprint
from random import choice as rchoice
def baseN(n, b, syms="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
""" converts a number `n` into base `b` string """
return ((n == 0) and syms[0]) or (
baseN(n//b, b, syms).lstrip(syms[0]) + syms[n % b])
def divided_by_N(number, base):
"""
constructs DFA that accepts given `base` number strings
those are divisible by a given `number`
"""
ACCEPTING_STATE = START_STATE = '0'
SYMBOL_0 = '0'
dfa = {
str(from_state): {
str(symbol): 'to_state' for symbol in range(base)
}
for from_state in range(number)
}
dfa[START_STATE][SYMBOL_0] = ACCEPTING_STATE
# `lookup_table` keeps track: 'number string' -->[dfa]--> 'end_state'
lookup_table = { SYMBOL_0: ACCEPTING_STATE }.setdefault
for num in range(number * base):
end_state = str(num % number)
num_s = baseN(num, base)
before_end_state = lookup_table(num_s[:-1], START_STATE)
dfa[before_end_state][num_s[-1]] = end_state
lookup_table(num_s, end_state)
return dfa
def symcolrhexcodes(symbols):
"""
returns dict of color codes mapped with alphabets symbol in symbols
"""
return {
symbol: '#'+''.join([
rchoice("8A6C2B590D1F4E37") for _ in "FFFFFF"
])
for symbol in symbols
}
def draw_transition_graph(dfa, filename="filename"):
ACCEPTING_STATE = START_STATE = '0'
colors = symcolrhexcodes(dfa[START_STATE].keys())
# draw transition graph
tg = pgv.AGraph(strict=False, directed=True, decorate=True)
for from_state in dfa:
for symbol, to_state in dfa[from_state].iteritems():
tg.add_edge("Q%s"%from_state, "Q%s"%to_state,
label=symbol, color=colors[symbol],
fontcolor=colors[symbol])
# add intial edge from an invisible node!
tg.add_node('null', shape='plaintext', label='start')
tg.add_edge('null', "Q%s"%START_STATE,)
# make end acception state as 'doublecircle'
tg.get_node("Q%s"%ACCEPTING_STATE).attr['shape'] = 'doublecircle'
tg.draw(filename, prog='circo')
tg.close()
def print_transition_table(dfa):
print("DFA accepting number string in base '%(base)s' "
"those are divisible by '%(number)s':" % {
'base': len(dfa['0']),
'number': len(dfa),})
pprint(dfa)
if __name__ == "__main__":
number = input ("Enter NUMBER: ")
base = input ("Enter BASE of number system: ")
dfa = divided_by_N(number, base)
print_transition_table(dfa)
draw_transition_graph(dfa)
执行它:
~/study/divide-5/script$ python script.py
Enter NUMBER: 5
Enter BASE of number system: 4
DFA accepting number string in base '4' those are divisible by '5':
{'0': {'0': '0', '1': '1', '2': '2', '3': '3'},
'1': {'0': '4', '1': '0', '2': '1', '3': '2'},
'2': {'0': '3', '1': '4', '2': '0', '3': '1'},
'3': {'0': '2', '1': '3', '2': '4', '3': '0'},
'4': {'0': '1', '1': '2', '2': '3', '3': '4'}}
~/study/divide-5/script$ ls
script.py filename.png
~/study/divide-5/script$ display filename
输出:
DFA接受以4为底的数字字符串,这些数字可以被5整除
同样,输入base = 4和number = 7生成-dfa接受以“ 4”为基数的数字字符串,这些数字可被“ 7”
Btw整除,尝试更改filename
为.png
或.jpeg
。
引用那些我用来编写此脚本的内容:
➊函数baseN
从“将整数转换为python中给定数字基的字符串”
➋要安装“ pygraphviz”:“ Python看不到pygraphviz”
➌要学习使用Pygraphviz:“ Python- FSM”
➍为每种语言符号生成随机的十六进制颜色代码:“如何使用.join和for循环制作随机的十六进制代码生成器?”
我知道我来晚了,但我只想在@Grijesh提供的正确答案中添加一些内容。我只想指出@Grijesh提供的答案不会产生最小的DFA。虽然答案肯定是获得DFA的正确方法,但是如果您需要最小的DFA,则必须研究除数。
像例如二进制数一样,如果除数是2的幂(即2 ^ n),则所需的最小状态数将是n + 1。您将如何设计这样的自动机?只需查看二进制数字的属性即可。对于一个数字,例如8(即2 ^ 3),其所有倍数的最后3位将为0。例如,二进制数40为101000。因此,要使语言接受任何可以被8整除的数字,我们只需要一个自动机,它查看最后3位是否为0,我们可以仅在4个状态而不是8个状态中进行操作。这是机器复杂度的一半。
实际上,这可以扩展到任何基础。对于三进制基数系统,例如,如果我们需要设计一个自动机以除以9,我们只需要查看输入的最后2个数字是否为0。可以再次在3种状态下完成此操作。
尽管除数不是很特殊,但是我们只需要通过@Grijesh的答案即可。例如,在二进制系统中,如果我们采用3或7或21的除数,那么我们将仅需要具有这么多的状态。因此,对于二进制系统中的任何奇数n,我们需要n个状态来定义接受n的所有倍数的语言。另一方面,如果数字是偶数但不是2的幂(仅在二进制数的情况下),则需要将数字除以2,直到得到奇数,然后才能找到最小状态数。将产生的奇数与我们除以2的次数相加。
例如,如果我们需要找到DFA接受所有可以被20整除的二进制数的最小状态数,则可以执行以下操作:
20/2 = 10
10/2 = 5
因此,我们的答案是5 + 1 + 1 = 7
。(1 + 1是因为我们将数字20除以了两次)。
您可以使用简单的模块化算法构建DFA。我们可以w
使用以下规则解释哪个是k元数的字符串
V[0] = 0
V[i] = (S[i-1] * k) + to_number(str[i])
V[|w|]
w
是代表的数字。如果修改此规则以查找w mod N
,则规则变为此。
V[0] = 0
V[i] = ((S[i-1] * k) + to_number(str[i])) mod N
并且每个V[i]
都是0到N-1之间的数字之一,它对应于DFA中的每个状态。我们可以将其用作状态转换。
看一个例子。
k = 2,N = 5
| V | (V*2 + 0) mod 5 | (V*2 + 1) mod 5 |
+---+---------------------+---------------------+
| 0 | (0*2 + 0) mod 5 = 0 | (0*2 + 1) mod 5 = 1 |
| 1 | (1*2 + 0) mod 5 = 2 | (1*2 + 1) mod 5 = 3 |
| 2 | (2*2 + 0) mod 5 = 4 | (2*2 + 1) mod 5 = 0 |
| 3 | (3*2 + 0) mod 5 = 1 | (3*2 + 1) mod 5 = 2 |
| 4 | (4*2 + 0) mod 5 = 3 | (4*2 + 1) mod 5 = 4 |
k = 3,N = 5
| V | 0 | 1 | 2 |
+---+---+---+---+
| 0 | 0 | 1 | 2 |
| 1 | 3 | 4 | 0 |
| 2 | 1 | 2 | 3 |
| 3 | 4 | 0 | 1 |
| 4 | 2 | 3 | 4 |
现在您可以看到一个非常简单的模式。您实际上可以构建DFA转换,只需从左到右,从上到下,从0到N-1编写重复数字。
n
是微不足道的,对吗?