Pular para conteúdo

3.3 Geradores de Padrões🔗

Introdução🔗

Sabemos que os padrões têm comprimento fixo e podem ser gerados com base em uma função. No entanto, às vezes é útil ter padrões de comprimento infinito, como ao gerar números aleatórios. É aí que entram os geradores de padrões. Semelhante aos geradores Python, onde nem todos os valores são mantidos na memória de uma só vez, exceto quando os geradores Python geralmente têm um fim – os geradores de padrões do FoxDot não tem! Vamos dar uma olhada no gerador de padrões PRand para começar, então você encontrará uma lista de todos os geradores de padrões disponíveis com alguns exemplos de código abaixo.

O gerador PRand nos fornece um suprimento infinito de números aleatórios entre um valor mínimo e máximo determinados ou a partir de um conjunto de valores fornecidos em uma lista. Não podemos simplesmente imprimir o objeto como fazemos com os padrões, porque não podemos imprimir uma lista infinita de valores que ainda nem foram calculados:

>>> my_gen = PRand(0, 10)
>>> print(my_gen)
PRand(0, 10)

Para ver quais valores estão em my_gen, precisamos acessá-lo usando indexação ou fatiamento.

>>> my_gen[0]
4

>>> my_gen[1]
6

>>> my_gen[:10]
P[4, 6, 1, 8, 3, 8, 8, 8, 0, 1]

A indexação do PRand nos fornece valores únicos e o fatiamento retorna um padrão de valores. Observe que os dois primeiros valores são 4 e 6. Um padrão de gerador (quase) sempre retornará o mesmo valor para um determinado índice. Você pode aplicar qualquer método de padrão ao objeto padrão obtido a partir do corte.

Você também pode performar operações matemáticas básicas nos geradores de padrões:

>>> my_gen2 = my_gen * 2
>>> print(my_gen2)
PRand(Mul 2)

>>> my_gen2[:10]
P[8, 12, 2, 16, 6, 16, 16, 16, 0, 2]

Imprimir o novo PRand nos dá algumas informações sobre ele, mas não muitas – apenas que é um objeto PRand existente e que foi multiplicado por 2. Você também pode usar suas próprias funções personalizadas e aplicá-las aos geradores de padrões da mesma forma que as aplica aos padrões; através do método transform. Vamos criar uma função que retorne 5 quando uma entrada for ímpar e 3 quando a entrada for par:

>>> def odd_test(num):
...    return 5 if num % 2 == 1 else 3

>>> my_odd_gen = odd_test(PRand(0, 10))
>>> print(my_odd_gen)
3

O num % 2 retorna um novo PRand e não um número, portanto, quando perguntado "isso é igual a 1?", ele sempre retornará False e, consequentemente, 3. Para realmente aplicar a função aos valores em nosso gerador de padrões, precisamos usar o método de transformação e fornecê-lo com a função.

>>> my_odd_gen = PRand(0, 10).transform(odd_test)
>>> print(my_odd_gen)
PRand(lambda None)

>>> print(my_odd_gen[:10])
P[3, 3, 3, 5, 3, 3, 5, 3, 5, 3]

Imprimir o novo padrão gerador nos mostra que temos um novo PRand que foi transformado (o lambda) sem nenhum valor de entrada. Fatia-lo retorna um padrão de 3s e 5s, exatamente como esperávamos.

O que acontece se não estivermos armazenando nossos geradores de padrão em uma variável e os imprimirmos?

>>> print(PRand(0, 10)[:10])
P[8, 5, 6, 0, 2, 2, 7, 3, 4, 4]

>>> print(PRand(0, 10)[:10])
P[1, 0, 0, 8, 0, 3, 4, 4, 2, 9]

Recebemos uma lista diferente de valores. Isso ocorre porque estamos inicializando um novo PRand cada vez que executamos o código. Isso pode ser um pouco incômodo, especialmente ao usar o FoxDot, pois muitas vezes você fornece objetos Pattern como geradores de padrões dessa maneira e não os armazena primeiro em uma variável. Se você quiser ter certeza de obter os mesmos valores do seu gerador de padrões, pode especificar a semente, que, quando definida com o mesmo número, força o gerador de números aleatórios do computador a gerar o mesmo conjunto de números.

>>> print(PRand(0, 10, seed=1)[:10])
P[2, 9, 1, 4, 1, 7, 7, 7, 10, 6]

>>> print(PRand(0, 10, seed=1)[:10])
P[2, 9, 1, 4, 1, 7, 7, 7, 10, 6]

Tipos de Geradores de Padrões🔗

PRand(lo, hi, seed=None) / PRand([values])🔗

Retorna uma série de números inteiros aleatórios entre lo e hi, inclusive. Se hi for omitido, o intervalo será entre 0 e lo. Uma lista de valores pode ser fornecida no lugar do intervalo e PRand retornará uma série de valores escolhidos aleatoriamente da lista.

>>> PRand(5, 10)[:10]
P[9, 10, 7, 10, 5, 10, 6, 5, 5, 7]

>>> PRand(5)[:10]
P[5, 4, 5, 5, 4, 1, 5, 5, 2, 0]

>>> PRand([1, 2, 3])[:10]
P[3, 2, 2, 2, 1, 3, 3, 2, 2, 1]

PxRand(lo, hi, seed=None) / PxRand([values])🔗

Idêntico a PRand, mas os elementos não serão repetidos.

>>> PxRand(5, 10)[:10]
P[6, 10, 8, 6, 9, 6, 9, 6, 10, 7]

>>> PxRand(5)[:10]
P[4, 3, 2, 1, 4, 1, 0, 5, 2, 3]

>>> PxRand([1, 2, 3])[:10]
P[2, 3, 2, 1, 3, 2, 3, 1, 3, 2]

PwRand([values], [weights])🔗

Usa uma lista de pesos (weights) para indicar a frequência com que os itens com o mesmo índice da lista de valores são selecionados. Um peso de 2 significa que é duas vezes mais provável que seja selecionado do que um item com um peso de 1.

>>> PwRand([0, 1], [1, 2])[:10]
P[1, 0, 0, 1, 1, 1, 1, 0, 1, 1]

>>> PwRand([0, 1, 2], [1, 2, 3])[:10]
P[1, 2, 2, 2, 2, 2, 0, 2, 2, 1]

PWhite(lo, hi)🔗

Retorna números aleatórios de ponto flutuante entre lo e hi.

>>> PWhite(0,5)[:10]
P[4.19058560392058, 2.776022501076935, 1.0643923909231003, 1.638195673989835, 0.8591848958936567, 2.461472231869311, 3.3546237751457335, 1.1854918335554943, 1.8310777165408907, 1.1767145868610336]

PChain(mapping_dictionary)🔗

Baseado em uma simples Cadeia de Markov de uso de probabilidades iguais. Aceita um dicionário de itens; estados e possíveis estados futuros. Cada estado futuro tem a mesma probabilidade de ser escolhido. Se um possível estado futuro não for válido, será gerado um KeyError.

>>> markov_chain = {
...    0: [3, 4],
...    3: [0, 4, 5],
...    4: [3, 0],
...    5: [3, 4],
... }
>>> PChain(markov_chain)[:10]
P[3, 0, 4, 0, 4, 3, 5, 4, 3, 5]

PWalk(max=7, step=1, start=0)🔗

Retorna uma série de números inteiros em que cada elemento está separado do outro por um tamanho step e os valores estão no intervalo +/- max. O primeiro elemento pode ser escolhido usando start.

>>> PWalk()[:10]
P[0, 1, 2, 1, 2, 3, 2, 3, 4, 5]

>>> PWalk(20, 5, 5)[:10]
P[5, 10, 15, 20, 15, 20, 15, 20, 15, 20]

PDelta(deltas, start=0)🔗

Pega uma lista de deltas (pequenos incrementos/decrementos) e retorna a série de números gerada pela soma desses valores em sequência. Usar um único número positivo criará uma série infinitamente crescente. Defina o valor inicial usando start.

>>> PDelta([0.1, 0.5, -0.3])[:10]
P[0, 0.1, 0.6, 0.3, 0.4, 0.9, 0.6, 0.7, 1.2, 0.9]

>>> PDelta([0.5])[:10]
P[0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]

>>> PDelta([0.5, -0.5], start=1)[:10]
P[1, 1.5, 1.0, 1.5, 1.0, 1.5, 1.0, 1.5, 1.0, 1.5]

PSquare()🔗

Retorna a série de números quadrados.

>>> PSquare()[:10]
P[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> PSquare()[10:20] % 7
P[2, 2, 4, 1, 0, 1, 4, 2, 2, 4]

PIndex()🔗

Retorna a série de números inteiros.

>>> PIndex()[:10]
P[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

PFibMod()🔗

Retorna a sequência de Fibonacci.

>>> PFibMod()[:10]
P[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

PZ12(tokens=[0, 1], p=[1, 0.5])🔗

Implementação simples do algoritmo Z12 para números aleatórios pré-determinados. Usar um valor irracional para p, no entanto, resulta em uma ordem de valores não determinada. Experimental – funciona apenas com 2 valores.

>>> PZ12([0, 1], [1, 0.5])[:15]
P[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]

>>> PZ12([0, 1], [1, math.sqrt(2)])[:15]
P[1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1]