¿Cómo generar cadenas aleatorias que coincidan con una expresión regular en Julia?

Respuestas:

5

Debería ser posible usar Automa.jl para construir un DFA y recorrerlo aleatoriamente. Automa utiliza una sintaxis más simple que PCRE, por lo que el lenguaje que puede describir en realidad debería ser regular.

Rápidamente reuní lo siguiente, basado principalmente en el código en dot.jl:

julia> function rand_re(machine::Automa.Machine)
           out = IOBuffer()
           node = machine.start

           while true
               if node.state  machine.final_states
                   (rand()  1 / (length(node.edges) + 1)) && break
               end

               edge, node = rand(node.edges)
               label = rand(collect(edge.labels))
               print(out, Char(label))
           end

           return String(take!(out))
       end
rand_re (generic function with 1 method)

julia> rand_re(Automa.compile(re"a[0-9][ab]+"))
"a6bbb"

julia> rand_re(Automa.compile(re"a[0-9][ab]+"))
"a9b"

julia> rand_re(Automa.compile(re"a[0-9][ab]+"))
"a3aa"

julia> rand_re(Automa.compile(re"a[0-9][ab]+"))
"a1a"

julia> rand_re(Automa.compile(re"a[0-9][ab]+"))
"a5ba"

La advertencia es que Automa utiliza conjuntos codificados en bytes para las etiquetas de borde, por lo que se debe tener más cuidado cuando escribo Char(label).

Como los estados finales aún pueden tener bordes salientes, elegí tratar la detención y cada borde con probabilidad uniforme. Creo que esto probablemente tendrá el efecto de que términos potencialmente infinitos serán muy cortos o muy largos; google "muestreadores de Boltzmann" sobre cómo resolver eso (¡no debe confundirse con el muestreo de una distribución de Boltzmann!), pero la solución es matemáticamente complicada.

phipsgabler
fuente
5

Julia tiene PCRE , lo que significa que sus expresiones regulares son mucho más poderosas que las verdaderas expresiones regulares. Y de hecho, están completos. Sospecho que hay un montón de ciencias informáticas teóricas interesantes sobre esto. Sospecho que su tarea para PCRE podría resultar imposible debido al problema de detención . Pero aún así, lo que podemos hacer es probar un montón de cadenas aleatorias y tirar las que no coinciden. Y para expresiones regulares simples, eso es muy útil. Sin embargo, no se garantiza que responda.

Si uno quisiera una expresión regular más estricta, como las cubiertas por Automa.jl , probablemente haya algo mejor que se pueda hacer, ya que puede recorrer la máquina de estados resolviéndola de a poco. Esperemos que alguien que sepa Automa.jl pueda publicar su propia respuesta.

Código

using Random: randstring

function rand_matching(regex; max_len=2^16, max_attempts=1000)
    for _ in max_attempts
        str  = randstring(max_len)
        m = match(regex, str)
        if m != nothing
            # rather than return whole string, 
            # just return the shortest bit that matches
            return m.match
        end
    end
    error("Could not find any string that matches regex")
end

manifestación:

julia> @time rand_matching(r"\d\d")
  0.013517 seconds (34.34 k allocations: 1.998 MiB)
"38"

julia> @time rand_matching(r"\d\d")
  0.001497 seconds (11 allocations: 128.656 KiB)
"44"

julia> @time rand_matching(r"a\d\d")
  0.000670 seconds (11 allocations: 128.656 KiB)
"a19"

julia> @time rand_matching(r"a\d\d")
  0.000775 seconds (11 allocations: 128.656 KiB)
"a83"

julia> @time rand_matching(r"a\d\db")
  0.000670 seconds (11 allocations: 128.656 KiB)
"a44b"
Lyndon White
fuente
Las múltiples implementaciones encontradas para otros idiomas (incluso las que implementan PCRE) me hacen creer que la tarea no es imposible, no profundicé en los detalles, tal vez están usando una versión más estricta de PCRE. Lanzar cadenas aleatorias con la esperanza de que una coincida con la expresión regular está bien para expresiones regulares pequeñas y simples, pero no se escala muy bien ... Gracias por señalarme hacia Automa porque coincide exactamente con el siguiente paso de mi problema. ¡Y quizás pueda matar dos pájaros de un tiro!
Thomas Jalabert el
Además, incluso si no es imposible (detener el problema equiv), aún debería tener una enorme complejidad de tiempo. Puede implementar cualquier problema como una expresión regular PCRE que solo acepta la solución. Entonces, por ejemplo, puede romper el cifrado de clave pública, con un PCRE que solo acepta la clave privada, y luego solicitar una aleatoria. O implemente cualquier problema de NP-Complete, como la suma de subconjuntos, que acepte To que Fexista si la suma de subconjuntos existe. Si su generador de expresiones regulares de PCRE funciona en tiempo polinómico, felicidades ha demostrado que P = NP.
Lyndon White el
Hmm, tal vez PCRE no está de hecho completo. Podría simplemente estar equivocado allí. Solo pueden ser equivalentes a gramáticas libres de contexto. Sin embargo, pueden hacer algunas locuras, como la detección principal. Entonces defs podría estar generando números primos con su generador de cadenas al azar. Y probablemente pueda romper la criptografía de clave pública.
Lyndon White el