Skip to content

Instantly share code, notes, and snippets.

@ebobby
Created November 25, 2019 20:23
Show Gist options
  • Select an option

  • Save ebobby/51336e243282dce3e76bd514374e7520 to your computer and use it in GitHub Desktop.

Select an option

Save ebobby/51336e243282dce3e76bd514374e7520 to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
class DFA
require 'matrix'
class << self
def optimize(dfa)
states = dfa[:states]
initial = dfa[:initial]
final = dfa[:final]
transitions = dfa[:transitions]
matrix = Matrix.scalar(states.length, 0)
transitions = Matrix.rows(transitions)
first_pass(matrix, states, final)
second_pass(matrix, states, transitions, final)
third_pass(matrix, states, transitions)
state_groups = new_state_groups(matrix, states)
result = {}
result[:states] = new_states = generate_new_states(state_groups, states)
result[:initial] = new_states[find_in_group(state_groups, initial)]
result[:final] = final.map { |f| new_states[find_in_group(state_groups, f)] }.sort.uniq
result[:transitions] = state_groups.map do |group|
new_transition = []
old_state = group.first
transitions.column_size.times do |char|
old_transition = transitions[states.index(old_state), char]
new_transition << new_states[find_in_group(state_groups, old_transition)]
end
new_transition
end
result
end
private
def find_in_group(groups, x)
groups.each_with_index.select { |g, _i| g.include?(x) }.map { |_x, i| i }.first
end
def generate_new_states(state_groups, states)
state_groups.each_with_index.map { |_s, i| states[i] }
end
def first_pass(matrix, states, final)
iterate_states(states) do |a, i, b, j|
matrix[i, j] = final?(a, final) == final?(b, final) ? 0 : 1
end
end
def second_pass(matrix, states, transitions, final)
iterate_states(states) do |_a, i, _b, j|
transitions.column_size.times do |char|
next unless matrix[i, j].zero?
matrix[i, j] =
final?(transitions[i, char], final) == final?(transitions[j, char], final) ? 0 : 2
end
end
end
def third_pass(matrix, states, transitions)
iterate_states(states) do |_a, i, _b, j|
transitions.column_size.times do |char|
next unless matrix[i, j].zero?
x = transitions[i, char]
y = transitions[j, char]
matrix[i, j] =
matrix[states.index(x), states.index(y)].zero? ? 0 : 3
end
end
end
def new_state_groups(matrix, states)
new_states = {}
iterate_states(states) do |a, i, b, j|
new_states[a] ||= []
new_states[a] << b if matrix[i, j].zero?
end
new_states.values.sort.uniq
end
def final?(state, final)
final.include?(state)
end
def iterate_states(states, &block)
states.each_with_index do |a, i|
states.each_with_index do |b, j|
block.call(a, i, b, j)
end
end
end
def print_matrix(matrix)
puts matrix.to_a.map(&:inspect)
end
end
end
automata1 = {
states: [:A, :B, :C, :D, :E, :F, :G, :H],
initial: :A,
final: [:D],
transitions: [
[:B, :A],
[:A, :C],
[:D, :B],
[:D, :A],
[:D, :F],
[:G, :E],
[:F, :G],
[:G, :D]
]
}
automata2 = {
states: [0, 1, 2, 3, 4, 5],
initial: 0,
final: [1, 2, 5],
transitions: [
[1, 2],
[3, 4],
[4, 3],
[5, 5],
[5, 5],
[5, 5]
]
}
automata_tarea = {
states: [:A, :B, :C, :D, :E, :F, :G, :H, :I],
initial: :A,
final: [:A, :E, :I],
transitions: [
[:B, :D],
[:C, :E],
[:A, :F],
[:E, :G],
[:F, :H],
[:D, :I],
[:H, :A],
[:I, :B],
[:G, :C]
]
}
DFA.optimize(automata1)
DFA.optimize(automata2)
DFA.optimize(automata_tarea)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment