automaton-simulate 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- automaton_simulate/__init__.py +13 -0
- automaton_simulate/automaton_main.py +271 -0
- automaton_simulate/function_affiliate.py +7 -0
- automaton_simulate/random_machine.py +49 -0
- automaton_simulate/turing_machine_formal_language.py +104 -0
- automaton_simulate/turing_machine_main.py +169 -0
- automaton_simulate/turing_machine_multitape.py +134 -0
- automaton_simulate/turing_machine_nondeterministic.py +151 -0
- automaton_simulate-0.1.0.dist-info/METADATA +22 -0
- automaton_simulate-0.1.0.dist-info/RECORD +13 -0
- automaton_simulate-0.1.0.dist-info/WHEEL +5 -0
- automaton_simulate-0.1.0.dist-info/licenses/LICENSE +21 -0
- automaton_simulate-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from turing_machine_main import TuringMachine
|
|
2
|
+
from turing_machine_multitape import MultiTapeTuringMachine
|
|
3
|
+
from turing_machine_nondeterministic import NondeterministicTuringMachine
|
|
4
|
+
from turing_machine_formal_language import Enumerator, recognizer
|
|
5
|
+
|
|
6
|
+
from automaton_main import FiniteAutomaton, PushdownAutomaton, LinearBoundedAutomaton
|
|
7
|
+
|
|
8
|
+
from random_machine import random_machine, two_symbols_machine
|
|
9
|
+
|
|
10
|
+
__version__ = "0.1.2"
|
|
11
|
+
__all__ = ["TuringMachine", "MultiTapeTuringMachine", "NondeterministicTuringMachine", "Enumerator", "recognizer",
|
|
12
|
+
"FiniteAutomaton", "PushdownAutomaton", "LinearBoundedAutomaton",
|
|
13
|
+
"random_machine", "two_symbols_machine"]
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
class FiniteAutomaton:
|
|
2
|
+
def __init__(self, states, alphabet, transition, initial_state, accept_state, initial_word):
|
|
3
|
+
|
|
4
|
+
self.states = states
|
|
5
|
+
self.alphabet = alphabet
|
|
6
|
+
self.transition = transition
|
|
7
|
+
self.initial_state = initial_state
|
|
8
|
+
self.accept_state = accept_state
|
|
9
|
+
self.current_state = initial_state
|
|
10
|
+
self.word = initial_word
|
|
11
|
+
|
|
12
|
+
def run(self):
|
|
13
|
+
for i in range(len(self.word)):
|
|
14
|
+
symbol = self.word[i]
|
|
15
|
+
if symbol not in self.alphabet:
|
|
16
|
+
raise ValueError(f"Write symbol '{symbol}' is not in alphabet")
|
|
17
|
+
|
|
18
|
+
key = (self.current_state, symbol)
|
|
19
|
+
|
|
20
|
+
if key not in self.transition:
|
|
21
|
+
raise RuntimeError(f"transition function undefined: '{key}'")
|
|
22
|
+
self.current_state = self.transition[key]
|
|
23
|
+
|
|
24
|
+
return self.current_state == self.accept_state
|
|
25
|
+
|
|
26
|
+
def __repr__(self):
|
|
27
|
+
return(
|
|
28
|
+
f"FiniteAutomaton(states={self.states}, alphabet={self.alphabet}, "
|
|
29
|
+
f"initial_state={self.initial_state!r}, accept_state={self.accept_state!r}, "
|
|
30
|
+
f"current_state={self.current_state!r}, initial_word={self.word!r}, "
|
|
31
|
+
f"transition={self.transition})"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class PushdownAutomaton:
|
|
37
|
+
|
|
38
|
+
"transition[(state, symbol, stack_top)] = (next_state, push_string)"
|
|
39
|
+
|
|
40
|
+
def __init__(self, states, alphabet, stack_alphabet, transition,
|
|
41
|
+
initial_state, accept_state, initial_word, initial_stack_symbol):
|
|
42
|
+
|
|
43
|
+
self.states = states
|
|
44
|
+
self.alphabet = alphabet
|
|
45
|
+
self.stack_alphabet = stack_alphabet
|
|
46
|
+
self.transition = transition
|
|
47
|
+
self.initial_state = initial_state
|
|
48
|
+
self.accept_state = accept_state
|
|
49
|
+
self.current_state = initial_state
|
|
50
|
+
self.word = initial_word
|
|
51
|
+
self.initial_stack_symbol = initial_stack_symbol
|
|
52
|
+
self.stack = [] if initial_stack_symbol is None else [initial_stack_symbol]
|
|
53
|
+
|
|
54
|
+
def _apply(self, key):
|
|
55
|
+
next_state, push_string = self.transition[key]
|
|
56
|
+
for ch in push_string:
|
|
57
|
+
if ch not in self.stack_alphabet:
|
|
58
|
+
raise ValueError(
|
|
59
|
+
f"stack symbol '{ch}' in push string '{push_string}' "
|
|
60
|
+
f"is not in stack alphabet")
|
|
61
|
+
self.current_state = next_state
|
|
62
|
+
if self.stack:
|
|
63
|
+
self.stack.pop()
|
|
64
|
+
self.stack.extend(reversed(push_string))
|
|
65
|
+
|
|
66
|
+
def run(self):
|
|
67
|
+
|
|
68
|
+
self.current_state = self.initial_state
|
|
69
|
+
self.stack = [] if self.initial_stack_symbol is None else [self.initial_stack_symbol]
|
|
70
|
+
|
|
71
|
+
i = 0
|
|
72
|
+
while True:
|
|
73
|
+
top = self.stack[-1] if self.stack else None
|
|
74
|
+
|
|
75
|
+
key = (self.current_state, None, top)
|
|
76
|
+
if key in self.transition:
|
|
77
|
+
self._apply(key)
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
if i == len(self.word):
|
|
81
|
+
break
|
|
82
|
+
|
|
83
|
+
symbol = self.word[i]
|
|
84
|
+
i += 1
|
|
85
|
+
if symbol not in self.alphabet:
|
|
86
|
+
raise ValueError(f"Input symbol '{symbol}' is not in alphabet")
|
|
87
|
+
|
|
88
|
+
key = (self.current_state, symbol, top)
|
|
89
|
+
if key not in self.transition:
|
|
90
|
+
raise RuntimeError(f"transition function undefined: '{key}'")
|
|
91
|
+
self._apply(key)
|
|
92
|
+
|
|
93
|
+
return self.current_state == self.accept_state
|
|
94
|
+
|
|
95
|
+
def __repr__(self):
|
|
96
|
+
return (
|
|
97
|
+
f"PushdownAutomaton(states={self.states}, alphabet={self.alphabet}, "
|
|
98
|
+
f"stack_alphabet={self.stack_alphabet}, "
|
|
99
|
+
f"initial_state={self.initial_state!r}, accept_state={self.accept_state!r}, "
|
|
100
|
+
f"current_state={self.current_state!r}, initial_word={self.word!r}, "
|
|
101
|
+
f"initial_stack_symbol={self.initial_stack_symbol!r}, "
|
|
102
|
+
f"stack={self.stack!r}, transition={self.transition})"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class LinearBoundedAutomaton:
|
|
107
|
+
#默认方法:属性及初始化
|
|
108
|
+
def __init__(self, states, alphabet, transition, initial_state, accept_state, reject_state, tape_length = 100, blank_symbol = " ", tape_alphabet = None, initial_tape = ""):
|
|
109
|
+
|
|
110
|
+
#七元组属性
|
|
111
|
+
self.states = states #有限状态集合
|
|
112
|
+
self.alphabet = alphabet #输入字母表
|
|
113
|
+
if tape_alphabet is None: #纸带字母表,包含输入字母表和空白符
|
|
114
|
+
self.tape_alphabet = list(alphabet) + [blank_symbol]
|
|
115
|
+
else:
|
|
116
|
+
self.tape_alphabet = tape_alphabet
|
|
117
|
+
self.transition = transition #转移函数
|
|
118
|
+
self.initial_state = initial_state #初始状态
|
|
119
|
+
self.accept_state = accept_state #接受状态
|
|
120
|
+
self.reject_state = reject_state #拒绝状态
|
|
121
|
+
|
|
122
|
+
#合法性检测
|
|
123
|
+
if tape_length <= 0:
|
|
124
|
+
raise ValueError("tape_length must be greater than 0")
|
|
125
|
+
|
|
126
|
+
#初始化纸带
|
|
127
|
+
self.tape_length = tape_length #纸带长度
|
|
128
|
+
self.blank_symbol = blank_symbol #空白符号
|
|
129
|
+
self.tape = [blank_symbol] * tape_length #写入初始空白符号
|
|
130
|
+
self.head = 0 #初始读写头位置
|
|
131
|
+
self.current_state = initial_state #当前状态为初始状态
|
|
132
|
+
self.steps = 0 #初始步数
|
|
133
|
+
|
|
134
|
+
#输入原始字符串
|
|
135
|
+
for i, char in enumerate(initial_tape):
|
|
136
|
+
if i < self.tape_length:
|
|
137
|
+
if char in self.tape_alphabet:
|
|
138
|
+
self.tape[i] = char
|
|
139
|
+
else:
|
|
140
|
+
raise ValueError(f"Initial_tape character '{char}' is not in tape_alphabet")
|
|
141
|
+
|
|
142
|
+
self.reset_data = (states, alphabet, transition, initial_state, accept_state, reject_state, tape_length, blank_symbol, tape_alphabet)
|
|
143
|
+
|
|
144
|
+
#读取方法
|
|
145
|
+
def read(self):
|
|
146
|
+
return self.tape[self.head]
|
|
147
|
+
|
|
148
|
+
#写入方法
|
|
149
|
+
def write(self, symbol):
|
|
150
|
+
if symbol in self.tape_alphabet:
|
|
151
|
+
self.tape[self.head] = symbol
|
|
152
|
+
else:
|
|
153
|
+
raise ValueError(f"Write symbol '{symbol}' is not in tape_alphabet") #写入符号不在纸带字母表内
|
|
154
|
+
|
|
155
|
+
#移动方法
|
|
156
|
+
def move(self, direction):
|
|
157
|
+
#读写头右移
|
|
158
|
+
if direction == "Right":
|
|
159
|
+
if self.head < self.tape_length - 1:
|
|
160
|
+
self.head += 1
|
|
161
|
+
else:
|
|
162
|
+
raise RuntimeError("Head moved out of bounds: reached right end of tape") #右方越界
|
|
163
|
+
#读写头左移
|
|
164
|
+
elif direction == "Left":
|
|
165
|
+
if self.head > 0:
|
|
166
|
+
self.head -= 1
|
|
167
|
+
else:
|
|
168
|
+
raise RuntimeError("Head moved out of bounds: reached left end of tape") #左方越界
|
|
169
|
+
#读写头不动
|
|
170
|
+
elif direction == "Stay":
|
|
171
|
+
pass
|
|
172
|
+
#方向未定义
|
|
173
|
+
else:
|
|
174
|
+
raise ValueError(f"Invalid move direction: '{direction}'") #无效的方向
|
|
175
|
+
|
|
176
|
+
#单步运行方法
|
|
177
|
+
def step(self):
|
|
178
|
+
if self.current_state == self.accept_state or self.current_state == self.reject_state:
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
symbol = self.read()
|
|
182
|
+
key = (self.current_state, symbol)
|
|
183
|
+
|
|
184
|
+
if key not in self.transition:
|
|
185
|
+
raise RuntimeError(f"transition function undefined: '{key}'")
|
|
186
|
+
new_state, write_symbol, direction = self.transition[key]
|
|
187
|
+
|
|
188
|
+
self.write(write_symbol)
|
|
189
|
+
self.move(direction)
|
|
190
|
+
self.current_state = new_state
|
|
191
|
+
self.steps += 1
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
#运行方法
|
|
195
|
+
def run(self,max_steps=10000):
|
|
196
|
+
self.steps = 0
|
|
197
|
+
while self.steps < max_steps and self.step():
|
|
198
|
+
pass
|
|
199
|
+
return self.tape_content()
|
|
200
|
+
|
|
201
|
+
#结果输出方法
|
|
202
|
+
def tape_content(self):
|
|
203
|
+
return "".join(self.tape)
|
|
204
|
+
|
|
205
|
+
#调试方法
|
|
206
|
+
def __repr__(self):
|
|
207
|
+
return(
|
|
208
|
+
f"LinearBoundedAutomaton(states={self.states}, alphabet={self.alphabet}, tape_alphabet={self.tape_alphabet}, "
|
|
209
|
+
f"initial_state={self.initial_state!r}, accept_state={self.accept_state!r}, reject_state={self.reject_state!r}, blank_symbol={self.blank_symbol!r}, "
|
|
210
|
+
f"tape_length={self.tape_length}, current_state={self.current_state!r}, initial_tape={self.tape!r}, "
|
|
211
|
+
f"head={self.head}, steps={self.steps}, "
|
|
212
|
+
f"transition={self.transition})"
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
if __name__ == "__main__":
|
|
216
|
+
LBA = LinearBoundedAutomaton(
|
|
217
|
+
states = {"q0","q_accept"},
|
|
218
|
+
alphabet = {"0","1"},
|
|
219
|
+
tape_alphabet = {"0","1"},
|
|
220
|
+
transition = {
|
|
221
|
+
("q0", "0"): ("q1", "1", "Right"),
|
|
222
|
+
("q0", "1"): ("q_accept", "1", "Stay"),
|
|
223
|
+
("q1", "0"): ("q1", "1", "Left"),
|
|
224
|
+
("q1", "1"): ("q_accept", "1", "Stay")
|
|
225
|
+
},
|
|
226
|
+
blank_symbol = "0",
|
|
227
|
+
initial_state = "q0",
|
|
228
|
+
accept_state = "q_accept",
|
|
229
|
+
reject_state = "q3",
|
|
230
|
+
initial_tape="00010"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
print(LBA)
|
|
234
|
+
print(LBA.run())
|
|
235
|
+
|
|
236
|
+
FA = FiniteAutomaton(
|
|
237
|
+
states = {"q0", "q1", "q_accept"},
|
|
238
|
+
alphabet = {"a", "b"},
|
|
239
|
+
transition = {
|
|
240
|
+
("q0", "a"): ("q1"), ("q0", "b"): ("q0"),
|
|
241
|
+
("q1", "a"): ("q1"), ("q1", "b"): ("q_accept"),
|
|
242
|
+
("q_accept", "a"): ("q1"), ("q_accept", "b"): ("q0")
|
|
243
|
+
},
|
|
244
|
+
initial_state = "q0",
|
|
245
|
+
accept_state = "q_accept",
|
|
246
|
+
initial_word = "abbab"
|
|
247
|
+
)
|
|
248
|
+
print(FA, FA.run())
|
|
249
|
+
pda = PushdownAutomaton(
|
|
250
|
+
states={'q0', 'q1', 'q2'},
|
|
251
|
+
alphabet={'a', 'b'},
|
|
252
|
+
stack_alphabet={'A', 'Z'}, # Z 是栈底标记
|
|
253
|
+
transition={
|
|
254
|
+
('q0', 'a', 'Z'): ('q0', 'AZ'), # 读 a:压入 A
|
|
255
|
+
('q0', 'a', 'A'): ('q0', 'AA'),
|
|
256
|
+
('q0', 'b', 'A'): ('q1', ''), # 读 b:弹出 A
|
|
257
|
+
('q1', 'b', 'A'): ('q1', ''),
|
|
258
|
+
('q1', None, 'Z'): ('q2', 'Z'), # ε:a、b 数量一致 → 接受态
|
|
259
|
+
},
|
|
260
|
+
initial_state='q0',
|
|
261
|
+
accept_state='q2',
|
|
262
|
+
initial_word='aaabbb',
|
|
263
|
+
initial_stack_symbol='Z',
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
for w in ['ab', 'aabb', 'aaabbb', 'aaabb', 'aab', 'aabbb']:
|
|
267
|
+
pda.word = w
|
|
268
|
+
try:
|
|
269
|
+
print(w, '->', pda.run())
|
|
270
|
+
except RuntimeError as e:
|
|
271
|
+
print(w, '-> False (拒绝:无可用转移,', e.args[0], ')')
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import string
|
|
3
|
+
from turing_machine_main import TuringMachine
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def random_machine(states_number, symbol_number):
|
|
7
|
+
|
|
8
|
+
states = tuple(f"q{i}" for i in range(states_number)) + ("q_accept", "q_reject")
|
|
9
|
+
alphabet = random.sample(string.ascii_lowercase, symbol_number)
|
|
10
|
+
blank = random.choice(alphabet)
|
|
11
|
+
|
|
12
|
+
transitions = {
|
|
13
|
+
(f"q{i}", alphabet[j]): (
|
|
14
|
+
random.choice(states),
|
|
15
|
+
random.choice(alphabet),
|
|
16
|
+
random.choice(["Left", "Right", "Stay"]),
|
|
17
|
+
)
|
|
18
|
+
for i in range(states_number)
|
|
19
|
+
for j in range(symbol_number)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (states, alphabet, transitions, "q0", "q_accept", "q_reject",
|
|
23
|
+
blank, list(alphabet))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def two_symbols_machine(states_number):
|
|
27
|
+
states = tuple(f"q{i}" for i in range(states_number)) + ("q_accept", "q_reject")
|
|
28
|
+
alphabet = ["0", "1"]
|
|
29
|
+
blank = random.choice(alphabet)
|
|
30
|
+
transitions = {
|
|
31
|
+
(f"q{i}", alphabet[j]): (
|
|
32
|
+
random.choice(states),
|
|
33
|
+
random.choice(alphabet),
|
|
34
|
+
random.choice(["Left", "Right", "Stay"]),
|
|
35
|
+
)
|
|
36
|
+
for i in range(states_number)
|
|
37
|
+
for j in range(2)
|
|
38
|
+
}
|
|
39
|
+
return (states, alphabet, transitions, "q0", "q_accept", "q_reject",
|
|
40
|
+
blank, list(alphabet))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
for i in range(100):
|
|
45
|
+
try:
|
|
46
|
+
tm = TuringMachine(*random_machine(3, 2)) # ① 必须传两个参数
|
|
47
|
+
print(f"#{i}: {tm.run()}")
|
|
48
|
+
except:
|
|
49
|
+
print(1)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
import copy
|
|
3
|
+
from turing_machine_main import TuringMachine
|
|
4
|
+
|
|
5
|
+
class Enumerator:
|
|
6
|
+
def __init__(self, machines, alphabet):
|
|
7
|
+
self.machines = list(machines)
|
|
8
|
+
self.alphabet = sorted(alphabet) if alphabet else ["0", "1"]
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def nth_string(alphabet, k):
|
|
12
|
+
m = k - 1
|
|
13
|
+
if m == 0:
|
|
14
|
+
return ""
|
|
15
|
+
base = len(alphabet)
|
|
16
|
+
m -= 1 # 跳过空串
|
|
17
|
+
length = 1
|
|
18
|
+
while m >= base ** length:
|
|
19
|
+
m -= base ** length
|
|
20
|
+
length += 1
|
|
21
|
+
chars = []
|
|
22
|
+
for _ in range(length):
|
|
23
|
+
m, r = divmod(m, base)
|
|
24
|
+
chars.append(alphabet[r])
|
|
25
|
+
return "".join(reversed(chars))
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def enumerate_string(alphabet):
|
|
29
|
+
yield ""
|
|
30
|
+
for length in itertools.count(1):
|
|
31
|
+
for chars in itertools.product(alphabet, repeat=length):
|
|
32
|
+
yield "".join(chars)
|
|
33
|
+
|
|
34
|
+
# 对所有 (j, k, i)(均 >=1)按 j+k+i 递增的顺序
|
|
35
|
+
@staticmethod
|
|
36
|
+
def enumerate_task():
|
|
37
|
+
for s in itertools.count(3):
|
|
38
|
+
for j in range(1, s - 1):
|
|
39
|
+
for k in range(1, s - j):
|
|
40
|
+
yield (j, k, s - j - k)
|
|
41
|
+
|
|
42
|
+
def run(self, max_outputs=None):
|
|
43
|
+
yielded = set()
|
|
44
|
+
outputs = 0
|
|
45
|
+
for j, k, i in self.enumerate_task():
|
|
46
|
+
if j > len(self.machines):
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
machine = copy.deepcopy(self.machines[j - 1])
|
|
50
|
+
input_string = self.nth_string(self.alphabet, k)
|
|
51
|
+
|
|
52
|
+
machine.reset()
|
|
53
|
+
machine.set_input(input_string)
|
|
54
|
+
|
|
55
|
+
steps = 0
|
|
56
|
+
try:
|
|
57
|
+
while steps < i and not machine.is_halted:
|
|
58
|
+
machine.step()
|
|
59
|
+
steps += 1
|
|
60
|
+
except RuntimeError:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
if machine.is_accept and input_string not in yielded:
|
|
64
|
+
yielded.add(input_string)
|
|
65
|
+
yield input_string
|
|
66
|
+
outputs += 1
|
|
67
|
+
if max_outputs is not None and outputs >= max_outputs:
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def recognizer(machine, input, max_steps):
|
|
72
|
+
sim = copy.deepcopy(machine)
|
|
73
|
+
sim.set_input(input)
|
|
74
|
+
while sim.steps <= max_steps:
|
|
75
|
+
if not sim.step():
|
|
76
|
+
return True
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
a = TuringMachine(
|
|
83
|
+
states = {"q0","q1", "q_accept","q_reject"},
|
|
84
|
+
alphabet = {"0","1"},
|
|
85
|
+
tape_alphabet = {"0","1"},
|
|
86
|
+
transition = {
|
|
87
|
+
("q0", "0"): ("q1", "1", "Right"),
|
|
88
|
+
("q0", "1"): ("q_accept", "1", "Stay"),
|
|
89
|
+
("q1", "0"): ("q1", "1", "Left"),
|
|
90
|
+
("q1", "1"): ("q_accept", "1", "Stay")
|
|
91
|
+
},
|
|
92
|
+
blank_symbol = "0",
|
|
93
|
+
initial_state = "q0",
|
|
94
|
+
accept_state = "q_accept",
|
|
95
|
+
reject_state = "q_reject",
|
|
96
|
+
initial_tape="00010"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
enum = Enumerator([a], ["0", "1"])
|
|
101
|
+
for output in enum.run(max_outputs=10):
|
|
102
|
+
print(output)
|
|
103
|
+
|
|
104
|
+
print(recognizer(a,"101",10), recognizer(a, "000", 1))
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
class TuringMachine:
|
|
2
|
+
"""七元组图灵机 M = (Q, Σ, Γ, δ, q0, q_accept, q_reject)"""
|
|
3
|
+
def __init__(self, states, alphabet, transition, initial_state, accept_state,
|
|
4
|
+
reject_state, blank_symbol=" ", tape_alphabet=None, initial_tape=""):
|
|
5
|
+
|
|
6
|
+
self.states = set(states)
|
|
7
|
+
self.alphabet = set(alphabet)
|
|
8
|
+
|
|
9
|
+
if tape_alphabet is None:
|
|
10
|
+
self.tape_alphabet = self.alphabet | {blank_symbol}
|
|
11
|
+
else:
|
|
12
|
+
self.tape_alphabet = set(tape_alphabet) | {blank_symbol}
|
|
13
|
+
|
|
14
|
+
self.transition = transition
|
|
15
|
+
self.initial_state = initial_state
|
|
16
|
+
self.accept_state = accept_state
|
|
17
|
+
self.reject_state = reject_state
|
|
18
|
+
self.blank_symbol = blank_symbol
|
|
19
|
+
|
|
20
|
+
for s in (initial_state, accept_state):
|
|
21
|
+
if s not in self.states:
|
|
22
|
+
raise ValueError(f"State '{s}' is not in states")
|
|
23
|
+
for (q, sym), (q2, ws, d) in transition.items():
|
|
24
|
+
if q not in self.states or q2 not in self.states:
|
|
25
|
+
raise ValueError(f"Transition {((q, sym), (q2, ws, d))} uses undefined states")
|
|
26
|
+
if ws not in self.tape_alphabet:
|
|
27
|
+
raise ValueError(f"Transition write symbol '{ws}' is not in tape_alphabet")
|
|
28
|
+
|
|
29
|
+
for char in initial_tape:
|
|
30
|
+
if char not in self.tape_alphabet:
|
|
31
|
+
raise ValueError(f"Initial_tape character '{char}' is not in tape_alphabet")
|
|
32
|
+
|
|
33
|
+
# ---- 初始化纸带:输入从下标 0 开始,读写头指向第一个字符 ----
|
|
34
|
+
self.tape = list(initial_tape)
|
|
35
|
+
self.head = 0
|
|
36
|
+
self.current_state = initial_state
|
|
37
|
+
self.steps = 0
|
|
38
|
+
|
|
39
|
+
self._initial_tape = list(self.tape)
|
|
40
|
+
|
|
41
|
+
def _ensure_cell(self):
|
|
42
|
+
while self.head < 0:
|
|
43
|
+
self.tape.insert(0, self.blank_symbol)
|
|
44
|
+
self.head += 1
|
|
45
|
+
while self.head >= len(self.tape):
|
|
46
|
+
self.tape.append(self.blank_symbol)
|
|
47
|
+
|
|
48
|
+
def read(self):
|
|
49
|
+
self._ensure_cell()
|
|
50
|
+
return self.tape[self.head]
|
|
51
|
+
|
|
52
|
+
def write(self, symbol):
|
|
53
|
+
if symbol not in self.tape_alphabet:
|
|
54
|
+
raise ValueError(f"Write symbol '{symbol}' is not in tape_alphabet")
|
|
55
|
+
self._ensure_cell()
|
|
56
|
+
self.tape[self.head] = symbol
|
|
57
|
+
|
|
58
|
+
def move(self, direction):
|
|
59
|
+
if direction == "Right":
|
|
60
|
+
self.head += 1
|
|
61
|
+
elif direction == "Left":
|
|
62
|
+
self.head -= 1
|
|
63
|
+
elif direction == "Stay":
|
|
64
|
+
pass
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError(f"Unknown direction: '{direction}'")
|
|
67
|
+
self._ensure_cell()
|
|
68
|
+
|
|
69
|
+
def step(self):
|
|
70
|
+
if self.current_state in (self.accept_state, self.reject_state):
|
|
71
|
+
return False
|
|
72
|
+
key = (self.current_state, self.read())
|
|
73
|
+
if key not in self.transition:
|
|
74
|
+
raise RuntimeError(f"transition function undefined: '{key}'")
|
|
75
|
+
new_state, write_symbol, direction = self.transition[key]
|
|
76
|
+
self.write(write_symbol)
|
|
77
|
+
self.move(direction)
|
|
78
|
+
self.current_state = new_state
|
|
79
|
+
self.steps += 1
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
def run(self, max_steps=10000):
|
|
83
|
+
self.reset()
|
|
84
|
+
while self.steps < max_steps and self.step():
|
|
85
|
+
pass
|
|
86
|
+
return self.tape_content()
|
|
87
|
+
|
|
88
|
+
def reset(self):
|
|
89
|
+
self.tape = list(self._initial_tape)
|
|
90
|
+
self.head = 0
|
|
91
|
+
self.current_state = self.initial_state
|
|
92
|
+
self.steps = 0
|
|
93
|
+
|
|
94
|
+
def set_input(self, initial_tape):
|
|
95
|
+
for i, char in enumerate(initial_tape):
|
|
96
|
+
if char not in self.tape_alphabet:
|
|
97
|
+
raise ValueError(f"Initial_tape character '{char}' is not in tape_alphabet")
|
|
98
|
+
|
|
99
|
+
tape_length = getattr(self, "tape_length", None)
|
|
100
|
+
|
|
101
|
+
if tape_length is not None and len(initial_tape) > tape_length:
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"initial_tape length {len(initial_tape)} exceeds tape_length {tape_length}"
|
|
104
|
+
)
|
|
105
|
+
if tape_length is not None:
|
|
106
|
+
self.tape = [self.blank_symbol] * tape_length
|
|
107
|
+
for i, char in enumerate(initial_tape):
|
|
108
|
+
self.tape[i] = char
|
|
109
|
+
else:
|
|
110
|
+
self.tape = list(initial_tape) or [self.blank_symbol]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
self.head = 0
|
|
114
|
+
self.current_state = self.initial_state
|
|
115
|
+
self.steps = 0
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
self._initial_tape = list(self.tape)
|
|
119
|
+
|
|
120
|
+
return self.tape
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def is_halted(self):
|
|
124
|
+
return self.current_state in (self.accept_state, self.reject_state)
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def is_accept(self):
|
|
128
|
+
return self.current_state == self.accept_state
|
|
129
|
+
|
|
130
|
+
def tape_content(self):
|
|
131
|
+
return "".join(self.tape)
|
|
132
|
+
|
|
133
|
+
def __repr__(self):
|
|
134
|
+
return (
|
|
135
|
+
f"TuringMachine(states={self.states}, alphabet={self.alphabet}, "
|
|
136
|
+
f"tape_alphabet={self.tape_alphabet}, initial_state={self.initial_state!r}, "
|
|
137
|
+
f"accept_state={self.accept_state!r}, reject_state={self.reject_state!r}, "
|
|
138
|
+
f"blank_symbol={self.blank_symbol!r}, current_state={self.current_state!r}, "
|
|
139
|
+
f"tape={''.join(self.tape)!r}, head={self.head}, steps={self.steps})"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
if __name__ == "__main__":
|
|
144
|
+
# 一台"纸带上含有 1 就接受"的图灵机,顺便演示读写头越界处理
|
|
145
|
+
def make_tm(input_tape):
|
|
146
|
+
return TuringMachine(
|
|
147
|
+
states={"q0", "q_accept", "q_reject"},
|
|
148
|
+
alphabet={"0", "1"},
|
|
149
|
+
tape_alphabet={"0", "1", "_"},
|
|
150
|
+
transition={
|
|
151
|
+
("q0", "0"): ("q0", "0", "Right"),
|
|
152
|
+
("q0", "1"): ("q_accept", "1", "Stay"),
|
|
153
|
+
("q0", "_"): ("q_reject", "_", "Stay"),
|
|
154
|
+
},
|
|
155
|
+
blank_symbol="_",
|
|
156
|
+
initial_state="q0",
|
|
157
|
+
accept_state="q_accept",
|
|
158
|
+
reject_state="q_reject",
|
|
159
|
+
initial_tape=input_tape,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
tm = make_tm("00010")
|
|
163
|
+
print(tm)
|
|
164
|
+
print("tape:", tm.run())
|
|
165
|
+
print("state:", tm.current_state, "| steps:", tm.steps)
|
|
166
|
+
|
|
167
|
+
tm2 = make_tm("00000")
|
|
168
|
+
print("tape:", tm2.run())
|
|
169
|
+
print("state:", tm2.current_state, "| steps:", tm2.steps)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
class MultiTapeTuringMachine:
|
|
2
|
+
#默认方法:属性及初始化
|
|
3
|
+
def __init__(self, tapes_number, states, alphabet, transition, initial_state, accept_state, reject_state, tape_length = 100, blank_symbol = " ", tape_alphabet = None, initial_tape = ""):
|
|
4
|
+
|
|
5
|
+
self.tapes_number = tapes_number #纸带数量
|
|
6
|
+
|
|
7
|
+
#七元组属性
|
|
8
|
+
self.states = states #有限状态集合
|
|
9
|
+
self.alphabet = alphabet #输入字母表
|
|
10
|
+
if tape_alphabet is None: #纸带字母表,包含输入字母表和空白符
|
|
11
|
+
self.tape_alphabet = alphabet + [blank_symbol]
|
|
12
|
+
else:
|
|
13
|
+
self.tape_alphabet = tape_alphabet
|
|
14
|
+
self.transition = transition #转移函数
|
|
15
|
+
self.initial_state = initial_state #初始状态
|
|
16
|
+
self.accept_state = accept_state #接受状态
|
|
17
|
+
self.reject_state = reject_state #拒绝状态
|
|
18
|
+
|
|
19
|
+
#合法性检测
|
|
20
|
+
if tape_length <= 0:
|
|
21
|
+
raise ValueError("tape_length must be greater than 0")
|
|
22
|
+
|
|
23
|
+
#初始化纸带(动态扩展列表:只存放已触及的格子,读写头越界时自动补空白符)
|
|
24
|
+
self.tape_length = tape_length #保留原属性仅供兼容/参考,不再限制纸带长度
|
|
25
|
+
self.blank_symbol = blank_symbol #空白符号
|
|
26
|
+
self.tapes = [[self.blank_symbol] for _ in range(self.tapes_number)] #每条纸带初始一格空白
|
|
27
|
+
self.heads = [0] * self.tapes_number #初始化多个读写头位置
|
|
28
|
+
self.current_state = initial_state #当前状态为初始状态
|
|
29
|
+
self.steps = 0 #初始步数
|
|
30
|
+
|
|
31
|
+
#输入原始字符串(写入第 0 条纸带,按需扩展)
|
|
32
|
+
for i, char in enumerate(initial_tape):
|
|
33
|
+
if char in self.tape_alphabet:
|
|
34
|
+
if i == 0:
|
|
35
|
+
self.tapes[0][i] = char
|
|
36
|
+
else:
|
|
37
|
+
self.tapes[0].append(char)
|
|
38
|
+
else:
|
|
39
|
+
raise ValueError(f"Initial_tape character '{char}' is not in tape_alphabet")
|
|
40
|
+
|
|
41
|
+
#读取方法
|
|
42
|
+
def read(self):
|
|
43
|
+
return [self.tapes[i][self.heads[i]] for i in range(self.tapes_number)]
|
|
44
|
+
|
|
45
|
+
#写入方法
|
|
46
|
+
def write(self, symbols):
|
|
47
|
+
for i in range(self.tapes_number):
|
|
48
|
+
if symbols[i] in self.tape_alphabet:
|
|
49
|
+
self.tapes[i][self.heads[i]] = symbols[i]
|
|
50
|
+
else:
|
|
51
|
+
raise ValueError(f"Write symbol '{symbols[i]}' is not in tape_alphabet") #写入符号不在纸带字母表内
|
|
52
|
+
|
|
53
|
+
#移动方法
|
|
54
|
+
def move(self, directions):
|
|
55
|
+
for i in range(self.tapes_number):
|
|
56
|
+
#读写头右移(越界时在纸带末端动态补充空白符)
|
|
57
|
+
if directions[i] == "Right":
|
|
58
|
+
if self.heads[i] < len(self.tapes[i]) - 1:
|
|
59
|
+
self.heads[i] += 1
|
|
60
|
+
else:
|
|
61
|
+
self.tapes[i].append(self.blank_symbol)
|
|
62
|
+
self.heads[i] += 1
|
|
63
|
+
#读写头左移(越界时在纸带左端动态补充空白符,读写头停留原位指向新补的空白格)
|
|
64
|
+
elif directions[i] == "Left":
|
|
65
|
+
if self.heads[i] > 0:
|
|
66
|
+
self.heads[i] -= 1
|
|
67
|
+
else:
|
|
68
|
+
self.tapes[i].insert(0, self.blank_symbol)
|
|
69
|
+
#读写头不动
|
|
70
|
+
elif directions[i] == "Stay":
|
|
71
|
+
pass
|
|
72
|
+
#方向未定义
|
|
73
|
+
else:
|
|
74
|
+
raise ValueError(f"Invalid move direction '{directions[i]}' for head {i}") #无效的方向
|
|
75
|
+
|
|
76
|
+
#单步运行方法
|
|
77
|
+
def step(self):
|
|
78
|
+
if self.current_state == self.accept_state or self.current_state == self.reject_state:
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
symbol = self.read()
|
|
82
|
+
key = (self.current_state, tuple(symbol))
|
|
83
|
+
|
|
84
|
+
if key not in self.transition:
|
|
85
|
+
raise RuntimeError(f"transition function undefined: '{key}'")
|
|
86
|
+
new_state, write_symbols, directions = self.transition[key]
|
|
87
|
+
|
|
88
|
+
self.write(write_symbols)
|
|
89
|
+
self.move(directions)
|
|
90
|
+
self.current_state = new_state
|
|
91
|
+
self.steps += 1
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
#运行方法
|
|
95
|
+
def run(self, max_steps=10000):
|
|
96
|
+
self.steps = 0
|
|
97
|
+
while self.steps < max_steps and self.step():
|
|
98
|
+
pass
|
|
99
|
+
return self.tape_content()
|
|
100
|
+
|
|
101
|
+
#结果输出方法
|
|
102
|
+
def tape_content(self):
|
|
103
|
+
return ["".join(self.tapes[i]) for i in range(self.tapes_number)]
|
|
104
|
+
|
|
105
|
+
#调试方法
|
|
106
|
+
def __repr__(self):
|
|
107
|
+
return(
|
|
108
|
+
f"MultiTapeTuringMachine(states={self.states}, alphabet={self.alphabet}, tape_alphabet={self.tape_alphabet}, "
|
|
109
|
+
f"initial_state={self.initial_state!r}, accept_state={self.accept_state!r}, reject_state={self.reject_state!r}, blank_symbol={self.blank_symbol!r}, "
|
|
110
|
+
f"tape_length={self.tape_length}, current_state={self.current_state!r}, initial_tape={self.tapes[0]!r}, tapes_number={self.tapes_number}, "
|
|
111
|
+
f"heads={self.heads}, steps={self.steps}, "
|
|
112
|
+
f"transition={self.transition})"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if __name__ == "__main__":
|
|
116
|
+
MTM = MultiTapeTuringMachine(
|
|
117
|
+
states = {"q0","q_accept"},
|
|
118
|
+
alphabet = {"0","1"},
|
|
119
|
+
tape_alphabet = {"0","1"},
|
|
120
|
+
transition = {
|
|
121
|
+
("q0", ("0", "0")): ("q1", ("1", "0"), ("Right", "Stay")),
|
|
122
|
+
("q1", ("0", "0")): ("q1", ("1", "0"), ("Left", "Stay")),
|
|
123
|
+
("q1", ("1", "0")): ("q_accept", ("1", "0"), ("Stay", "Stay"))
|
|
124
|
+
},
|
|
125
|
+
blank_symbol = "0",
|
|
126
|
+
initial_state = "q0",
|
|
127
|
+
accept_state = "q_accept",
|
|
128
|
+
reject_state = "q3",
|
|
129
|
+
initial_tape="00010",
|
|
130
|
+
tapes_number=2
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
print(MTM.run())
|
|
134
|
+
print(MTM)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from collections import deque
|
|
2
|
+
from turing_machine_main import TuringMachine
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class NondeterministicTuringMachine(TuringMachine):
|
|
6
|
+
|
|
7
|
+
def __init__(self, states, alphabet, transition, initial_state, accept_state,
|
|
8
|
+
reject_state, blank_symbol=" ", tape_alphabet=None, initial_tape=""):
|
|
9
|
+
|
|
10
|
+
if not isinstance(transition, dict):
|
|
11
|
+
raise TypeError("transition must be a dict")
|
|
12
|
+
for key, rules in transition.items():
|
|
13
|
+
if not isinstance(rules, list):
|
|
14
|
+
raise TypeError(
|
|
15
|
+
f"transition[{key!r}] must be a list of "
|
|
16
|
+
f"(new_state, write_symbol, direction)"
|
|
17
|
+
)
|
|
18
|
+
for rule in rules:
|
|
19
|
+
if not (isinstance(rule, tuple) and len(rule) == 3):
|
|
20
|
+
raise TypeError(
|
|
21
|
+
f"each rule in transition[{key!r}] must be a 3-tuple "
|
|
22
|
+
f"(new_state, write_symbol, direction), got {rule!r}"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
super().__init__(states, alphabet, {}, initial_state, accept_state,
|
|
26
|
+
reject_state, blank_symbol, tape_alphabet, initial_tape)
|
|
27
|
+
self.transition = transition
|
|
28
|
+
self._initial_tape = list(self.tape) or [self.blank_symbol] # 供 run() 每次从头开始
|
|
29
|
+
|
|
30
|
+
# ---------- 语义校验 ----------
|
|
31
|
+
for (state, symbol), rules in transition.items():
|
|
32
|
+
if state not in self.states:
|
|
33
|
+
raise ValueError(f"transition state '{state}' is not in states")
|
|
34
|
+
for new_state, write_symbol, direction in rules:
|
|
35
|
+
if new_state not in self.states:
|
|
36
|
+
raise ValueError(f"new state '{new_state}' is not in states")
|
|
37
|
+
if write_symbol not in self.tape_alphabet:
|
|
38
|
+
raise ValueError(f"write symbol '{write_symbol}' is not in tape_alphabet")
|
|
39
|
+
if direction not in ("Right", "Left", "Stay"):
|
|
40
|
+
raise ValueError(f"invalid move direction: '{direction}'")
|
|
41
|
+
|
|
42
|
+
# ---------- 纸带动态扩展 ----------
|
|
43
|
+
@staticmethod
|
|
44
|
+
def _move_head(tape, head, direction, blank_symbol):
|
|
45
|
+
if direction == "Right":
|
|
46
|
+
if head == len(tape) - 1:
|
|
47
|
+
tape.append(blank_symbol) # 右端动态补一格空白
|
|
48
|
+
return head + 1
|
|
49
|
+
if direction == "Left":
|
|
50
|
+
if head == 0:
|
|
51
|
+
tape.insert(0, blank_symbol) # 左端动态补一格空白
|
|
52
|
+
return 0
|
|
53
|
+
return head - 1
|
|
54
|
+
return head # Stay
|
|
55
|
+
|
|
56
|
+
def _canonical_key(self, state, tape, head):
|
|
57
|
+
start = 0
|
|
58
|
+
end = len(tape)
|
|
59
|
+
while start < head and tape[start] == self.blank_symbol:
|
|
60
|
+
start += 1
|
|
61
|
+
while end > head + 1 and tape[end - 1] == self.blank_symbol:
|
|
62
|
+
end -= 1
|
|
63
|
+
return (state, tuple(tape[start:end]), head - start)
|
|
64
|
+
|
|
65
|
+
def _next_configurations(self, state, tape, head, steps):
|
|
66
|
+
rules = self.transition.get((state, tape[head]))
|
|
67
|
+
if not rules:
|
|
68
|
+
return [] # 无转移定义:死分支
|
|
69
|
+
|
|
70
|
+
successors = []
|
|
71
|
+
for new_state, write_symbol, direction in rules:
|
|
72
|
+
new_tape = list(tape)
|
|
73
|
+
new_tape[head] = write_symbol
|
|
74
|
+
new_head = self._move_head(new_tape, head, direction, self.blank_symbol)
|
|
75
|
+
successors.append((new_state, new_tape, new_head, steps + 1))
|
|
76
|
+
return successors
|
|
77
|
+
|
|
78
|
+
def run(self, max_steps=10000):
|
|
79
|
+
# 每次 run 都从初始配置重新开始(纸带、读写头、状态全部复位)
|
|
80
|
+
self.tape = list(self._initial_tape)
|
|
81
|
+
self.head = 0
|
|
82
|
+
self.current_state = self.initial_state
|
|
83
|
+
self.steps = 0
|
|
84
|
+
|
|
85
|
+
# 广度优先搜索遍历所有非确定性分支
|
|
86
|
+
start = (self.initial_state, list(self.tape), 0, 0)
|
|
87
|
+
queue = deque([start])
|
|
88
|
+
visited = {self._canonical_key(*start[:3])} # 规范化去重,防纸带无限扩展
|
|
89
|
+
|
|
90
|
+
while queue:
|
|
91
|
+
state, tape, head, steps = queue.popleft()
|
|
92
|
+
|
|
93
|
+
if state == self.accept_state:
|
|
94
|
+
self.current_state = state
|
|
95
|
+
self.tape = tape
|
|
96
|
+
self.head = head
|
|
97
|
+
self.steps = steps
|
|
98
|
+
return self.tape_content()
|
|
99
|
+
|
|
100
|
+
if state == self.reject_state or steps >= max_steps:
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
for nxt in self._next_configurations(state, tape, head, steps):
|
|
104
|
+
nstate, ntape, nhead, nsteps = nxt
|
|
105
|
+
key = self._canonical_key(nstate, ntape, nhead)
|
|
106
|
+
if key in visited:
|
|
107
|
+
continue
|
|
108
|
+
visited.add(key)
|
|
109
|
+
queue.append(nxt)
|
|
110
|
+
|
|
111
|
+
return None # 所有分支均未接受
|
|
112
|
+
|
|
113
|
+
def step(self):
|
|
114
|
+
raise NotImplementedError(
|
|
115
|
+
"NondeterministicTuringMachine not supported step(), please use run()"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def __repr__(self):
|
|
119
|
+
return (
|
|
120
|
+
f"NondeterministicTuringMachine(states={self.states}, alphabet={self.alphabet}, "
|
|
121
|
+
f"tape_alphabet={self.tape_alphabet}, initial_state={self.initial_state!r}, "
|
|
122
|
+
f"accept_state={self.accept_state!r}, reject_state={self.reject_state!r}, "
|
|
123
|
+
f"blank_symbol={self.blank_symbol!r}, current_state={self.current_state!r}, "
|
|
124
|
+
f"tape={''.join(self.tape)!r}, head={self.head}, steps={self.steps})"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
if __name__ == "__main__":
|
|
129
|
+
# 非确定性地判定"纸带上含有 11 子串"
|
|
130
|
+
def make_ntm(input_tape):
|
|
131
|
+
return NondeterministicTuringMachine(
|
|
132
|
+
states={"q0", "q1", "q_accept", "q_reject"},
|
|
133
|
+
alphabet={"0", "1"},
|
|
134
|
+
tape_alphabet={"0", "1", "_"},
|
|
135
|
+
transition={
|
|
136
|
+
("q0", "0"): [("q0", "0", "Right")],
|
|
137
|
+
("q0", "1"): [("q0", "1", "Right"), ("q1", "1", "Right")],
|
|
138
|
+
("q0", "_"): [("q_reject", "_", "Stay")],
|
|
139
|
+
("q1", "1"): [("q_accept", "1", "Stay")],
|
|
140
|
+
},
|
|
141
|
+
blank_symbol="_",
|
|
142
|
+
initial_state="q0",
|
|
143
|
+
accept_state="q_accept",
|
|
144
|
+
reject_state="q_reject",
|
|
145
|
+
initial_tape=input_tape,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
ntm = make_ntm("01011")
|
|
149
|
+
print(ntm.run())
|
|
150
|
+
print(ntm.current_state, ntm.steps)
|
|
151
|
+
print(ntm)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: automaton-simulate
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A library for simulating automatons
|
|
5
|
+
Author-email: Dayu811 <13691283074@163.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# py-automaton
|
|
16
|
+
|
|
17
|
+
A lightweight Python library for simulating automata and turing machine.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install automaton-simulate
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
automaton_simulate/__init__.py,sha256=zkMW-VfdkxrKw6qZyH6vjCv_YKxL5h1RiuZTxG1FTkY,677
|
|
2
|
+
automaton_simulate/automaton_main.py,sha256=BMs4-quNp4OC_BbR6I7WkkD_z2ZEe35xEqajM7V_Ipc,10363
|
|
3
|
+
automaton_simulate/function_affiliate.py,sha256=JjweIMJJZ4hD8B6cKpjCja5qAlXiV6wNM0BEKpzR_RA,274
|
|
4
|
+
automaton_simulate/random_machine.py,sha256=1UnX4QhI5Zoat4KipVtwci1T2SAyKrvu5jlZHgQJqQg,1568
|
|
5
|
+
automaton_simulate/turing_machine_formal_language.py,sha256=-Oiz-GQt0tRq5TLsXTfuzbqs56Q-HoAzoUyQOevQeeQ,3110
|
|
6
|
+
automaton_simulate/turing_machine_main.py,sha256=MF1agGWx7cQxz-WdMa1TNKo-lk7EFiTTOeQY_QURn-g,6175
|
|
7
|
+
automaton_simulate/turing_machine_multitape.py,sha256=AlBwpwACdJXALNfoWx4xcpq-SL9lP7k9K7JjoxfITBU,5920
|
|
8
|
+
automaton_simulate/turing_machine_nondeterministic.py,sha256=PY5XHPkcbObGDqUdSBE4sIiGxHLIxc-UMHVo7vL7z2w,6451
|
|
9
|
+
automaton_simulate-0.1.0.dist-info/licenses/LICENSE,sha256=fU8tESbRE5wlGMODUQWtDc3LTO1z20PpiFiW51zeseI,1083
|
|
10
|
+
automaton_simulate-0.1.0.dist-info/METADATA,sha256=gwugmX2Dmn9X2qJ5RYjfMM7ChFQA1rUhvFBsyF0mRm4,581
|
|
11
|
+
automaton_simulate-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
12
|
+
automaton_simulate-0.1.0.dist-info/top_level.txt,sha256=vMJHsquhR1P1VST0e7nHSJDYuoQ5mH5ywd1KY88khco,19
|
|
13
|
+
automaton_simulate-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dayu811
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
automaton_simulate
|