Fractais em Python. Passo a passo

Olá Habr! O post de hoje sobre fractais surgiu como parte de um tema do Python , em particular o Matplotlib. Siga o exemplo do autor e avise que na postagem há muitas animações pesadas que podem nem funcionar em um dispositivo móvel. Mas que lindo.



Todos gostam de ler

Fractais são lindos. Eles se alinham de acordo com um padrão muito complexo e são preservados sem distorção em qualquer ampliação! Neste artigo, veremos como você pode facilmente desenhar fractais de vários tipos, usando uma ferramenta chamada L-Systems e o módulo Turtle para Python para executar desenhos passo a passo.

Neste artigo, não entraremos excessivamente em detalhes técnicos; em vez disso, restrito-me a uma breve introdução, mostro muitos exemplos animados e códigos com os quais você pode gerar esse exemplo. Se você quiser pular a teoria e assistir à animação, vá direto aos exemplos. Além disso, no final, apontarei vários recursos, tanto na redação de códigos quanto na matemática básica, que você pode querer explorar.

O que é um fractal?

Para começar, vamos dar uma definição "frouxa" de fractal. Em princípio, um fractal é uma figura geométrica que demonstra as mesmas propriedades, independentemente do grau de aumento.

Essa definição não é perfeita, então aqui está uma mais precisa no site do Math World:
Um fractal é um objeto ou quantidade que demonstra auto-similaridade (no sentido formal) em qualquer escala. Um objeto exibe estruturas não idênticas em escalas diferentes, mas estruturas do mesmo "tipo" devem aparecer em todos os níveis do fractal. Nesse caso, o gráfico é plotado em um sistema de coordenadas com uma escala logarítmica, onde a magnitude e a escala são contadas ao longo dos eixos, então o gráfico é uma linha reta com uma inclinação que reflete a dimensão do fractal. - Mundo da Matemática

Como desenhar fractais usando Python?

Normalmente, a renderização de fractais é complexa, pois a natureza profunda dos fractais é determinada pelo conceito de recursão. Falando sobre gráficos e seus desenhos, geralmente pensamos que eles são formados por pixels ou vetores, mas o número de pixels ou vetores é sempre limitado e os fractais são infinitamente recursivos por definição. Assim, tentando aplicar um fractal à grade de coordenadas, teremos que parar em algum momento, e é por isso que estamos falando de "iterações" neste caso. A cada iteração, o fractal se torna mais complicado e, em algum momento, torna-se impossível distinguir duas de suas iterações uma após a outra (esse momento ocorre quando as mudanças ocorrem em um nível comparável ao tamanho do pixel). É lógico parar por aqui, mas, como regra, o formato do fractal aparece mais rápido, e você pode parar ainda mais cedo.

Dois exemplos semelhantes são a ilha quadrada de Koch, cuja estrutura surge claramente após três iterações, e o dragão Carter-Heitway, para o qual oito iterações são suficientes para construir a estrutura completa. O número necessário de iterações depende muito do fractal em particular com o qual estamos trabalhando.

Obviamente, existem muitas bibliotecas Python para plotagem, entre as quais a mais popular é o Matplotlib, mas elas geralmente são projetadas para desenhar estatísticas e desenhar gráficos conhecidos. Em particular, o Matplotlib contém algumas construções de baixo nível que podem ser usadas para construir fractais, mas desta vez vamos nos concentrar no módulo imerecidamente pouco conhecido da biblioteca padrão chamada Turtle.

Módulo Turtle

na documentação do Pythonlemos: “Os gráficos da tartaruga são uma ferramenta popular para o primeiro conhecimento de crianças com programação. Ele fez parte da linguagem de programação original do logotipo, desenvolvida por Wally Förzeg e Seymour Peiper em 1966. ”

A conclusão é que a tartaruga reconhece três comandos por padrão:

  • Rastrear para a frente
  • Girar ângulo esquerdo
  • Girar ângulo direito

Nota: outros comandos são fornecidos na biblioteca padrão, mas aqui usaremos apenas esses três.

Também podemos:

  • Silenciar registro
  • Ativar gravação

Essas características parecem simples demais para desenhar em um gráfico tão complexo como um fractal, contando apenas com elas, mas usaremos outra ferramenta que usa apenas esse pequeno conjunto de instruções. Eu estou falando sobre sistemas-L.

Sistemas

L Um sistema L é uma maneira de representar estruturas recursivas (por exemplo, fractais) como uma sequência de caracteres e reescrever essa sequência várias vezes. Novamente, damos uma definição formal:
O sistema Lindenmeyer, também conhecido como sistema L, é um mecanismo de reescrita de linhas que pode ser usado para gerar fractais com dimensões de 1 a 2 - Math World

Tendo entendido o que é um sistema L, podemos criar estruturas recursivas, mas primeiro, vamos descobrir quais componentes precisamos para isso. Cada sistema L possui:

  • : , L-.
  • : .
  • : , .

Nota para os fãs de Ciência da Computação: Se você estudou Ciência da Computação em profundidade, todos os itens acima podem lembrá-lo de algo. De fato, gramáticas formais são definidas de maneira muito semelhante; a principal diferença é que, diferentemente das gramáticas, aqui a cada iteração, tantas regras se aplicam quanto possível, e não apenas uma. Portanto, os sistemas L são um subconjunto de gramáticas sem contexto.

Dado que vamos usar o Turtle para criar gráficos e o sistema L para representar o que vamos traçar, precisamos criar um relacionamento entre eles.

Como na Turtle temos apenas as equipes listadas acima, atribuímos a cada uma delas um símbolo; o alfabeto será composto por esses caracteres.

  • F: rastejar para a frente
  • +: vire à direita
  • -: vire a esquerda

Para que isso funcione, um ângulo deve ser fornecido para cada fractal; este será o ângulo em que a tartaruga girará para a direita ou esquerda. Por uma questão de simplicidade, concordamos que apenas um canto deve ser fornecido e escreveremos o sistema L, mantendo isso em mente.
O axioma e as instruções para criar seqüências de caracteres dependerão apenas do fractal, mas o fractal deve ser escrito de tal maneira que possa ser representado apenas por esses três caracteres. Portanto, há uma restrição, em virtude da qual só podemos construir fractais de linha única, ou seja, algo como o conjunto de Cantor não pode ser obtido dessa maneira. Mas isso é apenas uma simplificação, porque sempre podemos inserir dois outros comandos para avançar sem gravar e o mesmo para retroceder.

Agora vamos aos exemplos!

Exemplos animados

Os exemplos a seguir foram disponibilizados on-line a partir de várias fontes publicamente disponíveis, e eu decidi portá-los para o Python usando o módulo Turtle, centralizá-los, colori-los e fornecer uma maneira de exportar para um formato vetorial.

ATENÇÃO: as animações propostas são bastante amplas; recomenda-se assisti-las apenas com boa internet. O código Repl pode não funcionar porque consome seus recursos e pode haver problemas com a exibição de fractais em dispositivos móveis.

Nota: O Skulpt usa SEU NAVEGADOR para renderizar e criar animações; portanto, quando você trava, fica atrasado ou qualquer comportamento estranho, geralmente é suficiente apenas para reproduzir novamente a animação ou recarregar a página. Pode não funcionar no dispositivo móvel.

Os exemplos são dados em ordem de complexidade (na minha opinião subjetiva), então a coisa mais interessante está no final.

Floco de neve Koch

axiom = "F--F--F"
rules = {"F":"F+F--F+F"}
iterations = 4 # TOP: 7
angle = 60


Ilha da Praça Koch

axiom = "F+F+F+F"
rules = {"F":"F-F+F+FFF-F-F+F"}
iterations = 2 # TOP: 4
angle = 90


Cristal

axiom = "F+F+F+F"
rules = {"F":"FF+F++F+F"}
iterations = 3 # TOP: 6
angle = 90


Floco de neve quadrado

axiom = "F--F"
rules = {"F":"F-F+F+F-F"}
iterations = 4 # TOP: 6
angle = 90


Fractal Wicheka

axiom = "F-F-F-F"
rules = {"F":"F-F+F+F-F"}
iterations = 4 # TOP: 6
angle = 90


Levy Curve

axiom = "F"
rules = {"F":"+F--F+"}
iterations = 10 # TOP: 16
angle = 45


Tapete Sierpinski

axiom = "YF"
rules = {"X":"YF+XF+Y", "Y":"XF-YF-X"}
iterations = 1 # TOP: 10
angle = 60


Estrutura de Sierpinski

axiom = "FXF--FF--FF"
rules = {"F":"FF", "X":"--FXF++FXF++FXF--"}
iterations = 7 # TOP: 8
angle = 60


Quadrado

axiom = "F+F+F+F"
rules = {"F":"FF+F+F+F+FF"}
iterations = 3 # TOP: 5
angle = 90


Azulejos

axiom = "F+F+F+F"
rules = {"F":"FF+F-F+F+FF"}
iterations = 3 # TOP: 4
angle = 90


argolas

axiom = "F+F+F+F"
rules = {"F":"FF+F+F+F+F+F-F"}
iterations = 2 # TOP: 4
angle = 90


Cruz 2

axiom = "F+F+F+F"
rules = {"F":"F+F-F+F+F"}
iterations = 3 # TOP: 6
angle = 90


Pentaplexidade

axiom = "F++F++F++F++F"
rules = {"F":"F++F++F+++++F-F++F"}
iterations = 1 # TOP: 5
angle = 36


Curva de 32 segmentos

axiom = "F+F+F+F"
rules = {"F":"-F+F-F-F+F+FF-F+F+FF+F-F-FF+FF-FF+F+F-FF-F-F+FF-F-F+F+F-F+"}
iterations = 3 # TOP: 3
angle = 90


Peano Gosper Curve

axiom = "FX"
rules = {"X":"X+YF++YF-FX--FXFX-YF+", "Y":"-FX+YFYF++YF+FX--FX-Y"}
iterations = 4 # TOP: 6
angle = 60


Curva de Sierpinski

axiom = "F+XF+F+XF"
rules = {"X":"XF-F+F-XF+F+XF-F+F-X"}
iterations = 4 # TOP: 8
angle = 90


Questionários de Krishna

axiom = " -X--X"
rules = {"X":"XFX--XFX"}
iterations = 3 # TOP: 9
angle = 45


Fractal quadrado de Gosper

axiom = "YF"
rules = {"X": "XFX-YF-YF+FX+FX-YF-YFFX+YF+FXFXYF-FX+YF+FXFX+YF-FXYF-YF-FX+FX+YFYF-", 
        "Y": "+FXFX-YF-YF+FX+FXYF+FX-YFYF-FX-YF+FXYFYF-FX-YFFX+FX+YF-YF-FX+FX+YFY"}
iterations = 2 # TOP: 3
angle = 90


Moore Curve

axiom = "LFL-F-LFL"
rules = {"L":"+RF-LFL-FR+", "R":"-LF+RFR+FL-"}
iterations = 0 # TOP: 8
angle = 90


Curva de Hilbert

axiom = "L"
rules = {"L":"+RF-LFL-FR+", "R":"-LF+RFR+FL-"}
iterations = 8 # TOP: 9
angle = 90


Hilbert Curve II

axiom = "X"
rules = {"X":"XFYFX+F+YFXFY-F-XFYFX", "Y":"YFXFY-F-XFYFX+F+YFXFY"}
iterations = 4 # TOP: 6
angle = 90


Curva Peano

axiom = "F"
rules = {"F":"F+F-F-F-F+F+F+F-F"}
iterations = 2 # TOP: 5
angle = 90


Cruz

axiom = "F+F+F+F"
rules = {"F":"F+FF++F+F"}
iterations = 3 # TOP: 6
angle = 90


Triângulo

axiom = "F+F+F"
rules = {"F":"F-F+F"}
iterations = 2 # TOP: 9
angle = 120


Curva do dragão

axiom = "FX"
rules = {"X":"X+YF+", "Y":"-FX-Y"}
iterations = 8 # TOP: 16
angle = 90


Curva de Terdragon

axiom = "F"
rules = {"F":"F-F+F"}
iterations = 5 # TOP: 10
angle = 120


Double Dragon Curve

axiom = "FX+FX"
rules = {"X":"X+YF+", "Y":"-FX-Y"}
iterations = 6 # TOP: 16
angle = 90


Curva Tripla de Dragão

axiom = "FX+FX+FX"
rules = {"X":"X+YF+", "Y":"-FX-Y"}
iterations = 7 # TOP: 15
angle = 90


Código

Todos os exemplos acima foram obtidos usando o mesmo código e, ao trabalhar neles, surgiram algumas dificuldades (por exemplo, como manter o fractal no centro o máximo possível), trabalhando com cores, inversões, deslocamentos, além de fornecer exportação rápida para formato vetorial. Aqui eu mostro apenas a versão mais simples.
Esta versão exibe fractais em preto e branco e não está equipada com a funcionalidade de exportação

import turtle

def create_l_system(iters, axiom, rules):
    start_string = axiom
    if iters == 0:
        return axiom
    end_string = ""
    for _ in range(iters):
        end_string = "".join(rules[i] if i in rules else i for i in start_string)
        start_string = end_string

    return end_string


def draw_l_system(t, instructions, angle, distance):
    for cmd in instructions:
        if cmd == 'F':
            t.forward(distance)
        elif cmd == '+':
            t.right(angle)
        elif cmd == '-':
            t.left(angle)


def main(iterations, axiom, rules, angle, length=8, size=2, y_offset=0,
        x_offset=0, offset_angle=0, width=450, height=450):

    inst = create_l_system(iterations, axiom, rules)

    t = turtle.Turtle()
    wn = turtle.Screen()
    wn.setup(width, height)

    t.up()
    t.backward(-x_offset)
    t.left(90)
    t.backward(-y_offset)
    t.left(offset_angle)
    t.down()
    t.speed(0)
    t.pensize(size)
    draw_l_system(t, inst, angle, length)
    t.hideturtle()

    wn.exitonclick()


Código Explicação

import turtle


Primeiro você precisa importar o módulo Turtle

def create_l_system(iters, axiom, rules):
    start_string = axiom
    if iters == 0:
        return axiom
    end_string = ""
    for _ in range(iters):
        end_string = "".join(rules[i] if i in rules else i for i in start_string)
        start_string = end_string

    return end_string

Então você precisa gerar um sistema L, que será um conjunto de instruções para a tartaruga. Definimos uma função create_l_systemque recebe o número de iterações, um axioma e regras de construção. Começa com um axioma e usa uma variável auxiliar end_string; se a iteração for 0, retornará o axioma, pois alguns fractais também podem ser aplicados com zero iterações. Nesse caso, supõe-se que as regras tenham a forma de dicionários; portanto, cada chave é única, representa um símbolo e o valor indica o que e o que precisa ser substituído. Então, combinamos todas as substituições para cada personagem e, eventualmente, obtemos uma string para a próxima iteração.

def draw_l_system(t, instructions, angle, distance):
    for cmd in instructions:
        if cmd == 'F':
            t.forward(distance)
        elif cmd == '+':
            t.right(angle)
        elif cmd == '-':
            t.left(angle)

Em seguida, determinamos draw_l_systemqual aceita a tartaruga, um conjunto de instruções (saída do sistema L), o ângulo para virar à esquerda ou à direita e o comprimento de cada linha individual. Consiste em uma estrutura simples elifpara cada uma das equipes definidas anteriormente.

def main(iterations, axiom, rules, angle, length=8, size=2, y_offset=0,
        x_offset=0, offset_angle=0, width=450, height=450):

    inst = create_l_system(iterations, axiom, rules)

    t = turtle.Turtle()
    wn = turtle.Screen()
    wn.setup(width, height)

    t.up()
    t.backward(-x_offset)
    t.left(90)
    t.backward(-y_offset)
    t.left(offset_angle)
    t.down()
    t.speed(0)
    t.pensize(size)
    draw_l_system(t, inst, angle, length)
    t.hideturtle()

    wn.exitonclick()

Finalmente, 'Vamos Conversar' sobre a função main, que leva todos os parâmetros necessários para a geração de L-sistemas, bem como y_offset, x_offset, offset_angle, widthe height. Os três primeiros descrevem o deslocamento da tartaruga, é necessário apenas posicionar o gráfico na tela da maneira que queremos.

A função primeiro gera um conjunto de instruções e as salva em inst, depois inicializa a tartaruga e a tela e coloca a tartaruga em um ponto específico, depois desenha um gráfico de acordo com as instruções e aguarda um clique para fechar.

Considerações Especiais

Como mencionei acima, muitas restrições são deixadas aqui. Primeiro, não fornecemos à tartaruga a capacidade de se mover sem renderizar; isso exigiria outro personagem. Também não há símbolo para recuar e lembrar as posições anteriores. Eles não foram necessários para todos os fractais discutidos acima, mas são necessários para alguns outros (por exemplo, árvores fractais).

Recursos adicionais

A Internet possui muitos recursos em fractais, onde são considerados do ponto de vista da programação e do ponto de vista da matemática. Os dois seguintes me pareceram especialmente interessantes: 3Blue1Brown (math) e CodingTrain (code).

O artigo foi inspirado em um post do Math World e no artigo Paula Burka.

All Articles