4 sub>─1→q4 │
└──────┴──────┴──────────────┴──────────┴──────────┴ ──────────┘
图7
在 TD-7 中,边的总数为 10 == Q × Σ = 5 × 2。它是一个完整的 DFA,可以接受所有可能的二进制字符串,这些十进制等价物可以被 5 整除。
设计 DFA 接受可被数 n 整除的三进制数:
Step-1 与二进制完全相同,使用图 1。
第二步加零、一、二
┌────────┬────────┬──────────────┬──────────┬───────── ──────┐
│十进制│三进制│余数(%5)│结束状态│加 │
├────────┼────────┼──────────────┼──────────┼───────── ──────┤
│零 │0 │0 │q0 │ δ:(q0,0)→q0 │
├────────┼────────┼──────────────┼──────────┼───────── ──────┤
│一个 │1 │1 │q1 │ δ:(q0,1)→q1 │
├────────┼────────┼──────────────┼──────────┼───────── ──────┤
│二 │2 │2 │q2 │ δ:(q0,2)→q3 │
└────────┴────────┴──────────────┴──────────┴───────── ──────┘
图 8
第三步添加三、四、五
┌────────┬────────┬──────────────┬──────────┬───────── ────┐
│十进制│三进制│余数(%5)│结束状态│加 │
├────────┼────────┼──────────────┼──────────┼───────── ────┤
│三 │10 │3 │q3 │ δ:(q1,0)→q3 │
├────────┼────────┼──────────────┼──────────┼───────── ────┤
│四 │11 │4 │q4 │ δ:(q1,1)→q4 │
├────────┼────────┼──────────────┼──────────┼───────── ────┤
│五 │12 │0 │q0 │ δ:(q1,2)→q0 │
└────────┴────────┴──────────────┴──────────┴───────── ────┘
图-9
第四步添加六、七、八
┌────────┬────────┬──────────────┬──────────┬───────── ────┐
│十进制│三进制│余数(%5)│结束状态│加 │
├────────┼────────┼──────────────┼──────────┼───────── ────┤
│六│20 │1 │q1 │ δ:(q2,0)→q1 │
├────────┼────────┼──────────────┼──────────┼───────── ────┤
│七 │21 │2 │q2 │ δ:(q2,1)→q2 │
├────────┼────────┼──────────────┼──────────┼───────── ────┤
│八 │22 │3 │q3 │ δ:(q2,2)→q3 │
└────────┴────────┴──────────────┴──────────┴───────── ────┘
图-10
第五步加九、十、十一
┌────────┬────────┬──────────────┬──────────┬───────── ────┐
│十进制│三进制│余数(%5)│结束状态│加 │
├────────┼────────┼──────────────┼──────────┼───────── ────┤
│九 │100 │4 │q4 │ δ:(q3,0)→q4 │
├────────┼────────┼──────────────┼──────────┼───────── ────┤
│十 │101 │0 │q0 │ δ:(q3,1)→q0 │
├────────┼────────┼──────────────┼──────────┼───────── ────┤
│十一 │102 │1 │q1 │ δ:(q3,2)→q1 │
└────────┴────────┴──────────────┴──────────┴───────── ────┘
图-11
第六步添加十二、十三、十四
┌────────┬────────┬──────────────┬──────────┬──────── ──────┐
│十进制 │三进制│余数(%5)│结束状态│加 │
├────────┼────────┼──────────────┼──────────┼──────── ──────┤
│十二 │110 │2 │q2 │ δ:(q4,0)→q2 │
├────────┼────────┼──────────────┼──────────┼──────── ──────┤
│十三│111 │3 │q3 │ δ:(q4,1)→q3 │
├────────┼────────┼──────────────┼──────────┼──────── ──────┤
│十四│112 │4 │q4 │ δ:(q4,2)→q4 │
└────────┴───────┴──────────────┴──────────┴──────── ──────┘
图-12
转移图图 12 中的总边数为 15 = Q × Σ = 5 * 3(一个完整的 DFA)。这个 DFA 可以接受所有包含超过 {0, 1, 2} 的字符串,这些十进制等价物可以被 5 整除。
如果您在每个步骤中注意到,表中有三个条目,因为在每个步骤中,我从一个状态添加所有可能的传出边以形成完整的 DFA(并且我添加了一条边,以便 qr 状态剩下的是r)!
要进一步补充,请记住两种正则语言的并集也是正则。如果您需要设计一个接受二进制字符串的 DFA,那些十进制等价物可以被 3 或 5 整除,然后绘制两个单独的 DFA 以可被 3 和 5 整除,然后合并两个 DFA 以构造目标 DFA(对于 1
如果要求您绘制接受二进制字符串的 DFA,使得十进制等效项可以被 5 和 3 整除,那么您正在寻找可以被 15 整除的 DFA(但是 6 和 8 呢?)。
注意:只有当数字 n 和基数之间存在 no 公因子时,使用这种技术绘制的 DFA 才会最小化 DFA,例如第一个例子中 5 和 2 之间没有 no,第二个例子中 5 和 3 之间没有 no,因此上面构造的两个 DFA 都是最小化的 DFA。如果您有兴趣进一步了解编号 n 和基数 b 的可能迷你状态,请阅读论文:Divisibility and State Complexity。
下面我添加了一个 Python 脚本,我是在学习 Python 库 pygraphviz 的时候写的。我正在添加它,希望它对某人有所帮助。
为可被数字“n”整除的基数“b”数字字符串设计 DFA:
因此我们可以应用上述技巧来绘制 DFA 以识别任何基数 'b' 中的数字字符串,这些字符串可以被给定数字 'n' 整除。在该 DFA 中,状态总数将为 n(对于 n 余数),边数应等于 'b' * 'n' - 即完整的 DFA:'b' = 语言中的符号数DFA 和 'n' = 状态数。
使用上面的技巧,下面我编写了一个 Python 脚本来为输入 base 和 number 绘制 DFA。在脚本中,函数 divided_by_N 在 base * number 步骤中填充 DFA 的转换规则。在每个 step-num 中,我使用函数 baseN() 将 num 转换为数字字符串 num_s。为了避免处理每个数字字符串,我使用了一个临时数据结构lookup_table。在每一步中,都会评估数字字符串 num_s 的结束状态并将其存储在 lookup_table 中以供下一步使用。
对于DFA的转换图,我写了一个函数draw_transition_graph使用Pygraphviz library(非常好用)。要使用此脚本,您需要安装 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 接受可以被 5 整除的以 4 为底的数字字符串
同样,输入 base = 4 和 number = 7 生成 - dfa accepting number string in base '4' those are divisible by '7'
顺便说一句,尝试将filename 更改为.png 或.jpeg。
参考我用来编写这个脚本的那些:
➊ 功能 baseN 来自 "convert integer to a string in a given numeric base in python"
➋ 安装“pygraphviz”:"Python does not see pygraphviz"
➌ 学习 Pygraphviz 的使用:"Python-FSM"
➍ 为每种语言符号生成随机十六进制颜色代码:"How would I make a random hexdigit code generator using .join and for loops?"