Como gerar seqüências aleatórias que correspondem a um Regex em Julia?

Respostas:

5

Deve ser possível usar o Automa.jl para criar um DFA e percorrê-lo aleatoriamente. O Automa usa uma sintaxe mais simples que o PCRE, portanto, o idioma que você pode descrever deve ser regular.

Eu juntei rapidamente o seguinte, com base principalmente no código 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"

A ressalva é que o Automa usa conjuntos codificados em bytes para etiquetas de borda, portanto, mais cuidados devem ser tomados onde eu apenas escrevo Char(label).

Como os estados finais ainda podem ter arestas de saída, optei por tratar a parada e cada aresta com probabilidade uniforme. Eu acho que isso provavelmente terá o efeito de que termos potencialmente infinitos serão muito curtos ou muito longos; google "amostradores Boltzmann" para saber como resolver isso (não confunda com amostragem de uma distribuição Boltzmann!), mas a solução está matematicamente envolvida.

phipsgabler
fonte
5

Julia possui PCRE , o que significa que suas expressões regulares são muito mais poderosas que as verdadeiras expressões regulares. E estão de fato completos. Eu suspeito que há um monte de ciência da computação teórica interessante em torno disso. Suspeito que sua tarefa para PCRE possa ser provada impossível por causa do problema de interrupção . Ainda assim, o que podemos fazer é tentar um monte de strings aleatórios e jogar fora aqueles que não combinam. E para regex simples que percorre um longo caminho. Não é garantido que você responda.

Se alguém quiser regex mais rigoroso, como os cobertos pelo Automa.jl , provavelmente há algo melhor a ser feito, pois você pode percorrer a máquina de estado resolvendo-a 1 bit por vez. Espero que alguém que conheça o Automa.jl possa postar sua própria resposta.

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

demo:

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
fonte
As várias implementações encontradas para outros idiomas (mesmo aqueles que implementam o PCRE) me fazem acreditar que a tarefa não é impossível, não mergulhei nos detalhes, talvez eles estejam usando uma versão mais rígida do PCRE. Jogando seqüências aleatórias na esperança de que uma delas corresponda ao regex é aceitável para regex pequeno e simples, mas não escala muito bem ... Obrigado por me apontar para o Automa porque ele corresponde exatamente ao próximo passo do meu problema. E talvez ele possa matar dois coelhos com uma cajadada só!
Thomas Jalabert
Além disso, mesmo que não seja impossível (travando o problema equiv), ele ainda deve ter uma enorme complexidade de tempo. Você pode implementar qualquer problema como um regex PCRE que aceite apenas a solução. Assim, você pode, por exemplo, interromper a criptografia de chave pública, com um PCRE que aceita apenas a chave privada e solicitar uma aleatória. Ou implemente qualquer problema de problema NP-Complete, como soma do subconjunto, que aceite Tou Fdependa se a soma do syubset existe. Se o seu gerador de regex correspondente ao PCRE funciona em tempo polinomial, parabéns, você provou P = NP.
Lyndon Branco
Hmm, talvez o PCRE não esteja completo de fato. Eu poderia estar errado lá. Eles podem ser equivalentes apenas a gramáticas livres de contexto. Eles podem fazer algumas coisas loucas, como a detecção principal. Portanto, defs pode estar gerando números primos com seu gerador aleatório de strings. E provavelmente pode quebrar a criptografia de chave pública.
Lyndon Branco