Julia 프로그래밍 언어로 프랙탈 만들기

모두들 안녕,

나 자신을 먼저 소개하겠습니다:
  • 고등학생.
  • 거의 항상 수학에 대한 열정이 있었습니다.
  • 약 1년 전에 프로그래밍을 시작했습니다.
  • 프랑스어(제 영어가 대충 들리면 죄송합니다)

  • 평소 수학을 좋아했기 때문에 어떻게 하면 프로그래밍을 재미있게 할 수 있을지, 내가 재미있을 수 있는 일, (기술적인) 내용을 모르는 다른 사람들과 공유할 수 있는 일이 무엇일지 고민했습니다.
    선택의 여지가 많지만 시각적 측면이 특히 보람이 있기 때문에 많은 프랙탈을 사용하게 됩니다.

    저는 높은 수준의 빠른 프로그래밍 언어인 Julia로 작업합니다. 이것은 Julia 자체에 대한 튜토리얼이 아니며 라이브러리에 대한 약간의 내용입니다. 제가 한 일과 방법을 보여주기 위한 것입니다.

    사용한 패키지:
  • 룩소르.jl
  • GLMakie.jl
  • CliffordAlgebras.jl
  • Lindenmayer.jl

  • 정말 간단하게 시작해 봅시다. 2D는 워밍업으로 스파이럴을 해봅시다. 이를 위해 이 나선의 모든 점을 플로팅하고 연결합니다. 그리고 사용자가 원하는 원의 수를 선택하게 하십시오(실제 프랙탈을 만들려면 무한대가 됩니다).

    using GLMakie
    
    
    function spiral_point(n)
        rot = (^(im*n)) # set up the rotation of the point
        point = rot/n
        (point.re, point.im)  # this point converge to (0, 0) as n grows
    end
    
    function spiral(n) # where n is the number of spirals
        V = Tuple{Float64, Float64}[]
        for i in 1:0.01:2π*n
            push!(V, spiral_point(i))
        end
        V
    end
    
    println("How many spirals do you want? (number in N)")
    const num = parse(Int, readline()) 
    spiral(num) |> lines |> display
    




    나는 개인적으로 n이 커짐에 따라 세부 사항을 충분히 변경하지 않는 것이 마음에 들지 않습니다. 처음에는 느리고 더 빠르면 더 시원하고 더 많은 세부 사항을 표시합니다. 이를 위해 그래디언트 기능을 수행해 보겠습니다.

    using GLMakie
    function gradient(n)
        f(x) =  (x/n)
    end
    
    function spiral_point(n, f)
        rot = (^(im*n))
        point = rot/f(n)
        (point.re, point.im)
    end 
    
    function spiral(n, f)
        V = Tuple{Float64, Float64}[]
        for i in 1:0.01:2π*n
            push!(V, spiral_point(i, f))
        end
        V
    end
    
    println("How many spirals do you want? (number in N)")
    const num = parse(Int, readline())
    spiral(num, gradient(num)) |> lines |> display
    




    훨씬 좋아 보인다. 커스터마이징에 유용한 기술.

    재귀는 프랙탈을 생성하는 일반적인 기술입니다. n이 작을 때 정말 멋져 보이고 n이 클 때 정말 친숙해 보이는 예를 보여드리겠습니다 :)

    using Luxor
    
    const N = 9 # change that to change the deps
    const SIZE = 2700 # change that to resize the image
    const NAME = "MyLovelyFractal.png" # change that to change the name of your .png file you'll receive
    
    function points(x, y, size, l=Tuple{Int, Int}[])
        0 == size && return x, y, [l; (x, y)]
        x, y, l = points(x, y, size-1, l)
        y -= 2^(size-1)
        x -= 2^(size-1) - 1
        x, y, l = points(x, y, size-1, l)
        x += 1
        y += 2^(size-1)
        points(x, y, size-1, l)
    end
    p(s) = points(0, 0, s)[end]
    
    function main()
        v = p(NUM)
        Drawing(SIZE, SIZE, NAME)
        origin(0, SIZE)
        setcolor("blue")
        for i in 1:length(v)-1
            line(Point(v[i])*5, Point(v[i+1])*5, :stroke)
        end
        finish()
        preview()
    end
    
    main()
    




    나는이 작은 것을 자랑스럽게 생각합니다 :)
    보시다시피 n이 커질수록 Sierpinski 삼각형처럼 보입니다.

    Sierpinski 삼각형에 대해 말하자면, 우리는 라인 섹션에 있으므로 Lindenmayer-System을 사용하여 이를 수행하는 것은 어떻습니까?

    using Lindenmayer
    Sierpinski = LSystem(Dict("G" => "F+G+F", "F" => "G-F-G"), "F")
    
    drawLSystem(Sierpinski,
        forward = 6,
        startingorientation = 0,
        turn = 60,
        iterations = 6,
        filename = "Sierpinsky.png")
    




    힐베르트의 경우:

    Hilbert = LSystem(Dict("X" => "-YF+XFX+FY-", "Y" => "+XF-YFY-FX+"), "X")
    # rotation unit = -90
    




    일부 식물의 경우:

    Plant = LSystem(Dict("F" => "FF+[+F-F-F]-[-F+F+F]"), "F")
    # rotation uni = 22.5
    




    가능성을 생각해 보세요!✨✨✨

    프랙탈을 찾을 때 식물을 그리는 많은 방법을 보게 될 것이므로 또 다른 방법을 만들어 보겠습니다. 이번에는 임의의 숫자를 사용하여 마지막으로 선 대신 점 방향으로 흩어지기 시작합니다(더 많은 유연성을 허용함).

    using GLMakie
    f1(x, y) = [0 0; 0 0.16] * [x, y]
    f2(x, y) = [0.85 0.04; -0.04 0.85] * [x, y] + [0, 1.6]
    f3(x, y) = [0.2 -0.26; 0.23 0.22] * [x, y] + [0, 1.6]
    f4(x, y) = [-0.15 0.28; 0.26 0.24] * [x, y] + [0, 0.44]
    
    function f(x, y)
        r = rand()
        r < 0.01 && return f1(x, y)
        r < 0.86 && return f2(x, y)
        r < 0.93 && return f3(x, y)
        f4(x, y)
    end
    
    function barnsley_fern(iter)
        X = [0.0]
        Y = [0.0]
        for i in 1:iter
            x, y = f(X[end], Y[end])
            push!(X, x)
            push!(Y, y)
        end
        scatter(X, Y, color=:green, markersize=1)
    end
    
    barnsley_fern(1_000_000)
    




    이제 포인트 기반 프랙탈을 수행하고 있으므로 mandelbrot 및 Julia의 세트를 수행할 수 있습니다!

    f(z, c) = z*z + c
    function is_stable(iter, z, c)
        for _ in 1:iter
            if abs(z) > 2
                return false
            end
            z = f(z, c)
        end
        true
    end
    
    function mandel(precision, X, Y, f) # passing this f will be explained just after
        Points = Tuple{Float64, Float64}[]
        for x in X
            for y in Y
                z = f(x, y)
                if is_stable(precision, z, z)
                    push!(Points, (x, y))
                end
            end
        end
        scatter(Points, markersize=1)
    end
    
    
    function julia(precision, X, Y, c, f)
        Points = Tuple{Float64, Float64}[]
        for x in X
            for y in Y
                z = f(x, y)
                if is_stable(precision, z, c)
                    push!(Points, (x, y))
                end
            end
        end
        scatter(Points, markersize=4)
    end
    
    mandel(80, -2:0.0025:2, -2:0.0025:2, Complex) |> display
    sleep(10)
    julia(80, -2:0.0005:2, -2:0.0005:2, -0.8im, Complex) |> display
    





    줄리아에서 만든 줄리아 세트, 사랑스러워요.

    이제... 왜 내가 f를 통과했습니까? Welp, 그것이 어떻게 작동하는지 보기 위해 직접 만들기로 결정할 수 있기 때문입니다 :)
    이 예제는 3D이므로 내가 만든 함수 중 일부를 재사용하지 않았지만 아이디어를 얻을 수 있습니다.

    using CliffordAlgebras, GLMakie
    const S = CliffordAlgebra(1, 1, 0)
    const e1 = S.e1
    const e2 = S.e2
    # f definition
    # is_stable definition
    import Base.abs
    abs(n::MultiVector) = vector(n) .^ 2 |> sum |> sqrt  # type piracy /(o_o)\
    
    function mandel(precision, X, Y, Z, f)
        Points = Tuple{Float64, Float64, Float64}[]
        for x in X
            for y in Y
                for z in Z
                    num = f(x, y, z)
                    if is_stable(precision, num, num)
                        push!(Points, (x, y, z))
                    end
                end
            end
        end
        Points
    end
    const RANGE = -2:0.05:2
    fun(a, b, c) = a + b*e1 + c*e2
    mandel(50, RANGE, RANGE, RANGE, fun) |> (x -> scatter(x, markersize=4))
    




    이제 Cl(1, 1, 0)에 정의된 만델브로트 세트의 3D 슬라이스가 있습니다.
    그리고 이제 마지막 부분, 애니메이션 덕분에 이 전체 세트를 볼 수 있습니다!
    fun(a, b, c)까지 동일한 코드가 있다고 가정해 보겠습니다.

    n = Observable(0.0)
    fn = lift(d -> (a, b, c) -> (a + b*e1 + c*e2 + d*e1*e2), n)
    p = lift(f -> mandel(50, RANGE, RANGE, RANGE, f), fn)
    fig, axe = scatter(p)
    const frames = 1:28
    record(fig, "Cl1-1-0_4DMandel.gif", frames; framerate=28) do frame
        n[] = -1.5+frame/10
    end
    



    보다? 통과f의 도움으로 많은 일을 할 수 있습니다!
    그래서 여기에서 끝입니다. CliffordAlgebra(1, 1, 0)에 정의된 경우 mandelbrot 세트 전체를 본 행운의 사람 중 한 명입니다(모든 누락된 점 제외...)!
    흥미로웠기를 바랍니다.

    그래서, 왜 줄리아?
    모든 컴파일 시간이고 TTFP는 진짜이기 때문에 눈치채지 못할 수도 있지만 Julia는 정말 빠릅니다. 일단 알고리즘이 다운되면 Python에서 할 수 있는 것보다 훨씬 더 많은 반복을 실행할 수 있습니다.
    그것은 정말 훌륭한 수학 능력을 가지고 있고 표기법에서 수학에 가깝고 매우 자연스럽게 느껴집니다.
    그것은 매우 표현력이 풍부하며, 무언가를 잘 작동시키는 것을 더 쉽게 만듭니다.
    멋진 커뮤니티, 저는 Human of Julia(discord) 서버의 일부 사람들을 정말 좋아합니다.

    좋은 웹페이지 즐겨찾기