Implementación de Moore Machines en C++

Máquinas de Moore: una máquina de Moore es básicamente un DFA con una salida asociada con cada estado. Estas máquinas se pueden usar para una amplia variedad de tareas, como contar las ocurrencias de una substring particular en una string dada, encontrar el complemento a 2 de un número binario , etc.

Funcionamiento de la máquina Moore:

  • Tiene una salida asociada a cada estado.
  • Al tomar entrada, pasa al siguiente estado.
  • Al llegar al siguiente estado, imprime la salida del siguiente estado.
  • Esto continúa hasta que se alcanza el final de la entrada.

Algunas Aplicaciones de la Máquina Moore

Aplicación 1: 
Dada una string S que consta de a y b , y una substring «abb» , la tarea es contar la ocurrencia de la substring dada str en la string dada S utilizando Moore Machines.

Ejemplos: 

Entrada: S = “babbbbabb” 
Salida: 
00010001 
ocurrencias: 2 
Explicación: 
La substring “abb” aparece dos veces en la string dada. 
Por lo tanto, para cada substring «abb» se produce un ‘1’. 
El número de 1 es 2.
Entrada: S = “ab” 
Salida: 
000 
ocurrencias: 0 
Explicación: 
No hay ninguna ocurrencia de la substring “abb” en la string dada. 
Por lo tanto, el número de 1s es 0. 
 

Enfoque:
La Máquina de Moore requerida para este problema viene dada por: 
 

La tabla de transición para la máquina se proporciona a continuación: 
 

Para implementar esto, cree una estructura para mapear la entrada a su siguiente estado: 
 

struct item
{
     int value;
     State* next;
};

Luego, incluye esta estructura como un miembro de datos de nuestra clase State . La clase tiene tres miembros de datos:

  • Input_1: Esta es una variable de tipo elemento definida anteriormente. Asigna el primer tipo de entrada ‘a’ a su siguiente estado.
  • Input_2: Esta también es una variable de tipo elemento. Asigna el segundo tipo de entrada ‘b’ a su siguiente estado.
  • m_out: Esta es la salida asociada con cada estado de la Máquina de Moore .

Cada objeto de la clase se comporta como un estado. Toma entrada y va al siguiente estado apropiado. Para pasar al siguiente estado, se puede utilizar un puntero de objeto. Cada objeto también tiene una entrada asociada a él.

Las siguientes funciones miembro se utilizarán para trabajar con estos datos:

  • Initialize(): esto inicializa el objeto de clase (estado) con entradas y los siguientes estados correspondientes.
  • Transition(): actúa como una tabla de transición para la máquina. Toma un carácter de la string de entrada y lo pasa al estado actual, que luego pasa al siguiente estado apropiado después de producir una salida.
  • Traverse(): esta función toma una string de entrada y carácter por carácter la pasa a la función de transición y devuelve la string de salida.
  • mooreOut(): Esta función define los estados requeridos (objetos) y los inicializa a los valores requeridos. Luego pasa la string de entrada a la función poligonal y recibe la string de salida.
  • countStr(): esta función cuenta las apariciones de 1 en la string de salida y lo devuelve.

El siguiente paso es almacenar el estado actual de la máquina mientras le pasa entradas de string. Esto se puede hacer usando un puntero de objeto estático como miembro de datos de clase

A continuación se muestra la implementación del enfoque anterior:
 

C++14

// C++ program for the above approach
 
#include <bits/stdc++.h>
using namespace std;
 
// Define a class named State
class State {
private:
    // Item
    struct item {
        char value;
        State* next;
    };
 
    // Three states
    item Input1;
    item Input2;
    char m_out;
 
public:
    // Constructor
    State()
        : Input1{ ' ', nullptr },
          Input2{ ' ', nullptr },
          m_out{ ' ' }
    {
    }
 
    // Member functions
    static State* m_ptr;
    void Initialize(item input1,
                    item input2,
                    char out);
    static char Transition(char x);
    static string Traverse(string& str,
                           int n);
};
 
// Global object pointer points to
// current state
State* State::m_ptr{ nullptr };
 
// Function that initializes the states
// with appropriate values
void State::Initialize(item input1,
                       item input2,
                       char out)
{
    Input1 = input1;
    Input2 = input2;
    m_out = out;
}
 
// Transition function that takes each
// character of string
char State::Transition(char x)
{
    char ch{};
 
    // Prints the output
    if ((*m_ptr).Input1.value == x) {
 
        // Output the current state
        cout << (*m_ptr).m_out;
        ch = (*m_ptr).m_out;
 
        // Next input state
        m_ptr = (*m_ptr).Input1.next;
    }
    else {
 
        // Output the current state
        cout << (*m_ptr).m_out;
        ch = (*m_ptr).m_out;
 
        // Next input state
        m_ptr = (*m_ptr).Input2.next;
    }
 
    // Return ch
    return ch;
}
 
// Takes the whole string and pass
// it through machine
string State::Traverse(string& str,
                       int n)
{
    string str1{};
 
    // Add all the transition state to
    // the string str1
    for (int i = 0; i < n; i++)
        str1 += Transition(str[i]);
 
    // Append output
    str1 += (*m_ptr).m_out;
    cout << (*m_ptr).m_out << endl;
 
    // Return str1
    return str1;
}
 
// Function that create states and
// produce output
string mooreOut(string str, int n)
{
    State q1, q2, q3, q4;
 
    // Initializing the states
    q1.Initialize({ 'a', &q2 },
                  { 'b', &q1 }, '0');
    q2.Initialize({ 'a', &q2 },
                  { 'b', &q3 }, '0');
    q3.Initialize({ 'a', &q2 },
                  { 'b', &q4 }, '0');
    q4.Initialize({ 'a', &q2 },
                  { 'b', &q1 }, '1');
    State::m_ptr = &q1;
 
    // Traverse the string str1
    string str1{ State::Traverse(str, n) };
    return str1;
}
 
// Function that counts the occurrences
// of 1 in the output string
int countStr(string& str, int n)
{
    int count{};
 
    // Count the 1s in str
    for (int i = 0; i < n; i++) {
        if (str[i] == '1')
            count++;
    }
 
    // Return count
    return count;
}
 
// Driver Code
int main()
{
 
    // Given string
    string str{ "babbabbabbb" };
 
    int n{ static_cast<int>(str.length()) };
 
    // Function Call
    string str1{ mooreOut(str, n) };
    int n1{ static_cast<int>(str.length()) };
 
    // Print the count of substring
    cout << "abb occurs " << countStr(str1, n1)
         << " times\n";
    return 0;
}
Producción: 

000010010010
abb occurs 3 times

 

Complejidad de tiempo: O(N) 
Espacio auxiliar: O(N)
Aplicación 2: 
Dada una string binaria str , la tarea es encontrar el complemento a 2 de la string dada str .
 

Entrada: str = “111010000” 
Salida: 000110000
Entrada: str = “111” 
Salida: 001 
 

Enfoque: la idea es comenzar desde el bit más a la derecha y pasarlo a la máquina que da la salida. Pase toda la cuerda así, de derecha a izquierda. Se pueden observar las siguientes observaciones:
Por ejemplo: La string dada es «111010000». Ahora, el complemento a 2 está dado por el complemento a 1 de (str) + 1. Por lo tanto, 
 

str =           "111010000"
1s compliment = "000101111"
                +        1
---------------------------
2s complement =  000110000

Aquí podemos observar que desde el bit de más a la derecha se copian los bits de la misma manera hasta que aparece el 1. Después de eso, todos los bits se invierten.
Por lo tanto, la idea es definir una máquina de Moore para comenzar a recibir información del lado derecho. Siempre que el bit sea 0 , da la misma salida (0) . Cuando se encuentra 1 , entonces da 1 para eso. Después de esto, para cualquier bit tomado como entrada, su inverso se da como salida.

La máquina de Moore para este problema se muestra a continuación:

Para implementar esto primero, asigne la tabla de transición para esta máquina. Se necesitan tres estados, es decir, tres objetos de clase Estado definidos al principio: 
 

  • Estado1 => Inicializar ({‘0’, &Estado1}, {‘1’, &Estado2}, ‘0’)
  • Estado2 => Inicializar ({‘0’, &Estado2}, {‘1’, &Estado3}, ‘1’)
  • Estado3 => Inicializar ({‘0’, &Estado2}, {‘1’, &Estado3}, ‘0’)

Después de la inicialización, se necesita una función de transición como la definida anteriormente. Toma una entrada y para eso imprime la salida del estado actual y pasa al siguiente estado asignando la entrada al siguiente estado usando la tabla de transición definida anteriormente. Luego, para atravesar la string, la transición comienza desde el bit más a la derecha y continúa hasta el bit más a la izquierda.

A continuación se muestra la implementación del enfoque anterior:

C++

// C++ program for the above approach
 
#include <bits/stdc++.h>
using namespace std;
 
// Define a class named State
class State {
private:
    struct item {
        char value;
        State* next;
    };
    item Input1;
    item Input2;
    char m_out;
 
public:
    // Constructors
    State()
        : Input1{ ' ', nullptr },
          Input2{ ' ', nullptr },
          m_out{ ' ' }
    {
    }
    static State* m_ptr;
 
    // Member Functions
    void Initialize(item input1,
                    item input2,
                    char out);
    static char Transition(char x);
    static string Traverse(string& str,
                           int n);
};
 
// Global object pointer points to
// current state
State* State::m_ptr{ nullptr };
 
// Function that initializes the states
// with appropriate values
void State::Initialize(item input1,
                       item input2,
                       char out)
{
    Input1 = input1;
    Input2 = input2;
    m_out = out;
}
 
// Transition function takes each
// character of string
char State::Transition(char x)
{
    char ch{};
 
    // Prints the output
    if ((*m_ptr).Input1.value == x) {
 
        // Output the current state
        cout << (*m_ptr).m_out;
        ch = (*m_ptr).m_out;
 
        // Next input state
        m_ptr = (*m_ptr).Input1.next;
    }
    else {
 
        // Output the current state
        cout << (*m_ptr).m_out;
        ch = (*m_ptr).m_out;
 
        // Next input state
        m_ptr = (*m_ptr).Input2.next;
    }
 
    // Return ch
    return ch;
}
 
// Takes the whole string and passes
// through machine
string State::Traverse(string& str, int n)
{
    string str1{};
 
    // Add all the transition state to
    // the string str1
    for (int i = n - 1; i >= 0; i--) {
        str1 += Transition(str[i]);
    }
 
    // To read the characters from end
    // therefore we need to reverse
    reverse(str1.begin(), str1.end());
 
    return str1;
}
 
// Function to create states and
// produce output
string mooreOut(string str, int n)
{
    State q1, q2, q3;
 
    // Initializing the states
    q1.Initialize({ '0', &q1 },
                  { '1', &q2 }, '0');
    q2.Initialize({ '0', &q2 },
                  { '1', &q3 }, '1');
    q3.Initialize({ '0', &q2 },
                  { '1', &q3 }, '0');
    State::m_ptr = &q1;
    return State::Traverse(str, n);
}
 
// Driver Code
int main()
{
    // Given string
    string str{ "111010000" };
    int n{ static_cast<int>(str.length()) };
 
    // Function Call
    string str1{ mooreOut(str, n) };
 
    // Print the output
    cout << "2's complement: " << str1;
    return 0;
}
Producción: 

2's complement: 000110000

 

Complejidad de tiempo: O(N), donde N es la longitud de la string binaria dada. 
Espacio Auxiliar: O(N)
 

Publicación traducida automáticamente

Artículo escrito por gbhardwaj y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *