使用Python创建分形图案

news2025/1/25 9:03:04

分形是无限复杂的模式,在不同的尺度上具有自相似性。例如,一棵树的树干会分裂成更小的树枝。这些树枝又分裂成更小的树枝,以此类推。

通过编程的方式生成分形,可以将简单的形状变成复杂的重复图案。

本文将探讨如何利用一些简单的几何学基础和编程知识,在Python中建立令人印象深刻的分形图案。

分形在数据科学中发挥着重要作用。例如,在分形分析中,对数据集的分形特征进行评估,以帮助理解基础过程的结构。此外,处于分形生成中心的循环算法可以应用于广泛的数据问题,例如从二进制搜索算法到递归神经网络。

一、目标

写一个可以画等边三角形的程序,并且在三角形的每条边上,它必须能够绘制一个稍微小一点的向外的三角形。能够根据人的意愿多次重复此过程,从而创建一些有趣的模式。

二、表示图像

把图像表示为一个二维的像素阵列。像素阵列中的每个单元格将代表该像素的颜色(RGB)。

为此,可以使用NumPy库生成像素数组,并使用Pillow将其转换为可以保存的图像。

蓝色像素的x值为3,y值为4,可以通过一个二维数组访问,如pixels[4][3] 

三、画一条线 

现在开始编码,首先,需要一个可以获取两组坐标并在它们之间画一条线的函数。

下面的代码通过在两点之间插值来工作,每一步都向像素阵列添加新的像素。你可以把这个过程看作是在一条线上逐个像素地进行着色。

可以在每个代码片段中使用连续字符“\”来容纳一些较长的代码行。


import numpy as np
from PIL import Image
import math

def plot_line(from_coordinates, to_coordinates, thickness, colour, pixels):

    # 找出像素阵列的边界
    max_x_coordinate = len(pixels[0])
    max_y_coordinate = len(pixels)

    # 两点之间沿着x轴和y轴的距离
    horizontal_distance = to_coordinates[1] - from_coordinates[1]
    vertical_distance = to_coordinates[0] - from_coordinates[0]

    # 两点之间的总距离
    distance =  math.sqrt((to_coordinates[1] - from_coordinates[1])**2 \
                + (to_coordinates[0] - from_coordinates[0])**2)

    # 每次给一个新的像素上色时,将向前走多远
    horizontal_step = horizontal_distance/distance
    vertical_step = vertical_distance/distance

    # 此时,将进入循环以在像素数组中绘制线
    # 循环的每一次迭代都会沿着线添加一个新的点
    for i in range(round(distance)):
        
        # 这两个坐标是直线中心的坐标
        current_x_coordinate = round(from_coordinates[1] + (horizontal_step*i))
        current_y_coordinate = round(from_coordinates[0] + (vertical_step*i))

        # 一旦得到了点的坐标,
        # 就在坐标周围画出尺寸为thickness的图案
        for x in range (-thickness, thickness):
            for y in range (-thickness, thickness):
                x_value = current_x_coordinate + x
                y_value = current_y_coordinate + y

                if (x_value > 0 and x_value < max_x_coordinate and \
                    y_value > 0 and y_value < max_y_coordinate):
                    pixels[y_value][x_value] = colour

# 定义图像的大小
pixels = np.zeros( (500,500,3), dtype=np.uint8 )

# 画一条线
plot_line([0,0], [499,499], 1, [255,200,0], pixels)

# 把像素阵列变成一张真正的图片
img = Image.fromarray(pixels)

# 显示得到的图片,并保存它
img.show()
img.save('Line.png')

 此函数在像素阵列的每个角之间绘制一条黄线时的结果

四、画三角形

现在有了一个可以在两点之间画线的函数,可以画第一个等边三角形了。

给定三角形的中心点和边长,可以使用公式计算出高度:h = ½(√3a)。

现在利用这个高度、中心点和边长,可以计算出三角形的每个角的位置。使用之前制作的plot_line函数,可以在每个角之间画一条线。

def draw_triangle(center, side_length, thickness, colour, pixels):
    
    # 等边三角形的高度是,h = ½(√3a)
    # 其中a是边长
    triangle_height = round(side_length * math.sqrt(3)/2)

    # 顶角
    top = [center[0] - triangle_height/2, center[1]]

    # 左下角
    bottom_left = [center[0] + triangle_height/2, center[1] - side_length/2]

    # 右下角
    bottom_right = [center[0] + triangle_height/2, center[1] + side_length/2]

    # 在每个角之间画一条线来完成三角形
    plot_line(top, bottom_left, thickness, colour, pixels)
    plot_line(top, bottom_right, thickness, colour, pixels)
    plot_line(bottom_left, bottom_right, thickness, colour, pixels)

 在500x500像素PNG的中心绘制三角形时的结果

五、生成分形 

一切都已准备就绪,可以用Python创建第一个分形。

但是最后一步是最难完成的,三角形函数为它的每一边调用自己,需要能够计算每个新的较小三角形的中心点,并正确地旋转它们,使它们垂直于它们所附着的一侧。

通过从旋转的坐标中减去中心点的偏移量,然后应用公式来旋转一对坐标,可以用这个函数来旋转三角形的每个角。

def rotate(coordinate, center_point, degrees):
    # 从坐标中减去旋转的点
    x = (coordinate[0] - center_point[0])
    y = (coordinate[1] - center_point[1])

    # Python的cos和sin函数采用弧度而不是度数
    radians = math.radians(degrees)

    # 计算旋转点
    new_x = (x * math.cos(radians)) - (y * math.sin(radians))
    new_y = (y * math.cos(radians)) + (x * math.sin(radians))

    # 将在开始时减去的偏移量加回旋转点上
    return [new_x + center_point[0], new_y + center_point[1]]

 将每个坐标旋转35度的三角形

可以旋转一个三角形后,思考如何在第一个三角形的每条边上画一个新的小三角形。

为了实现这一点,扩展draw_triangle函数,为每条边计算一个新三角形的旋转和中心点,其边长被参数shrink_side_by减少。

一旦它计算出新三角形的中心点和旋转,它就会调用draw_triangle(自身)来从当前线的中心画出新的、更小的三角形。然后,这将反过来打击同一个代码块,为一个更小的三角形计算另一组中心点和旋转。

这就是所谓的循环算法,因为draw_triangle函数现在会调用自己,直到达到希望绘制的三角形的最大深度。有这个转义句子是很重要的,因为理论上这个函数会一直循环下去(但实际上调用堆栈会变得太大,导致堆栈溢出错误)。

 

def draw_triangle(center, side_length, degrees_rotate, thickness, colour, \
                  pixels, shrink_side_by, iteration, max_depth):
    
    # 等边三角形的高度是,h = ½(√3a)
    # 其中'a'是边长
    triangle_height = side_length * math.sqrt(3)/2

    # 顶角
    top = [center[0] - triangle_height/2, center[1]]

    # 左下角
    bottom_left = [center[0] + triangle_height/2, center[1] - side_length/2]

    # 右下角
    bottom_right = [center[0] + triangle_height/2, center[1] + side_length/2]

    if (degrees_rotate != 0):
        top = rotate(top, center, degrees_rotate)
        bottom_left = rotate(bottom_left, center, degrees_rotate)
        bottom_right = rotate(bottom_right, center, degrees_rotate)

    # 三角形各边之间的坐标
    lines = [[top, bottom_left],[top, bottom_right],[bottom_left, bottom_right]]

    line_number = 0

    # 在每个角之间画一条线来完成三角形
    for line in lines:
        line_number += 1

        plot_line(line[0], line[1], thickness, colour, pixels)

        # 如果还没有达到max_depth,就画一些新的三角形
        if (iteration < max_depth and (iteration < 1 or line_number < 3)):
            gradient = (line[1][0] - line[0][0]) / (line[1][1] - line[0][1])

            new_side_length = side_length*shrink_side_by

            # 正在绘制的三角形线的中心
            center_of_line = [(line[0][0] + line[1][0]) / 2, \
                              (line[0][1] + line[1][1]) / 2]

            new_center = []
            new_rotation = degrees_rotate

            # 需要旋转traingle的数量
            if (line_number == 1):
                new_rotation += 60
            elif (line_number == 2):
                new_rotation -= 60
            else:
                new_rotation += 180
            
            # 在一个理想的世界里,这将是gradient=0,
            # 但由于浮点除法的原因,无法
            # 确保永远是这种情况
            if (gradient < 0.0001 and gradient > -0.0001):
                if (center_of_line[0] - center[0] > 0):
                    new_center = [center_of_line[0] + triangle_height * \
                                 (shrink_side_by/2), center_of_line[1]]
                else:
                    new_center = [center_of_line[0] - triangle_height * \
                                  (shrink_side_by/2), center_of_line[1]]
                    
            else:
                
                # 计算直线梯度的法线
                difference_from_center = -1/gradient

                # 计算这条线距中心的距离
                # 到新三角形的中心
                distance_from_center = triangle_height * (shrink_side_by/2)

                # 计算 x 方向的长度,
                # 从线的中心到新三角形的中心
                x_length = math.sqrt((distance_from_center**2)/ \
                                     (1 + difference_from_center**2))

                # 计算出x方向需要走哪条路
                if (center_of_line[1] < center[1] and x_length > 0):
                    x_length *= -1

                # 现在计算Y方向的长度
                y_length = x_length * difference_from_center

                # 用新的x和y值来偏移线的中心
                new_center = [center_of_line[0] + y_length, \
                              center_of_line[1] + x_length]

            draw_triangle(new_center, new_side_length, new_rotation, \
                          thickness, colour, pixels, shrink_side_by, \
                          iteration+1, max_depth)

 三角形分形,收缩边=1/2,最大深度=2

六、结论

下面是通过修改输入到draw_triangle函数的shrink_side_bymax_depth值生成的不同图像的一些示例。

有趣的是,这些多次重复的图案往往能创造出更复杂的形状,比如六边形,但却具有令人着迷的对称性。

 越来越复杂的形状开始在重复三角形的对称性中出现

 另一个分形,每次迭代使用较小的尺寸减小

分形是非常有趣的玩法,可以创造出美丽的图案。使用一些简单的概念和丰富的创造力,可以产生非常令人印象深刻的结构。

在理解分形的核心属性和应用循环算法的过程中打下的坚实基础,可以帮助理解数据科学中更复杂的分形问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/433686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ThinkSystem DM 系列混合闪存 —— 快速、灵活、可靠、安全

ThinkSystem DM 系列混合闪存 —— 快速、灵活、可靠、安全 统一存储优化混合云部署具备一流数据管理的横向扩展混合存储 挑战 实现跨闪存、磁盘和云数据驱动型业务 存储已从 IT 事后思考的问题发展成公司基础架构至关重要的组件。企业感觉迫切需要跟上爆炸式增长的数据。标…

抖音滑块以及轨迹分析

声明 本文以教学为基准、本文提供的可操作性不得用于任何商业用途和违法违规场景。 本人对任何原因在使用本人中提供的代码和策略时可能对用户自己或他人造成的任何形式的损失和伤害不承担责任。 如有侵权,请联系我进行删除。 我们在web端打开用户主页的时候,时不时的会出现滑…

tf-idf+lda分析多篇文章摘要

import pandas as pd import numpy as np import matplotlib.pyplot as plt #import seaborn as sns plt.rcParams[font.sans-serif] [KaiTi] #指定默认字体 SimHei黑体 plt.rcParams[axes.unicode_minus] False #解决保存图像是负号 import jieba import os # stop_list …

RabbitMQ的介绍和安装

文章目录 1.1 RabbitMQ介绍1.2 RabbitMQ的安装1.2.1 下载镜像1.2.2 安装启动1.2.3 测试 1.1 RabbitMQ介绍 RabbitMQ是一个开源的消息队列中间件&#xff0c;可以用于构建分布式应用程序&#xff0c;使应用程序能够快速、可靠地处理大量消息。它实现了AMQP&#xff08;高级消息队…

Vue安装脚手架 vue-cli

Vue 脚手架是 Vue 官方提供的标准化开发工具&#xff08;开发平台&#xff09; 在安装脚手架之前我们还需要安装 Node.JS 和 npm 全局安装 vue-cli 【初次安装】: 首先打开命令行 输入 npm install -g vue/cli 再按回车键 安装脚手架 安装命令行&#xff1a; npm install …

多层LCP技术的毫米波段超宽带槽天线设计

1、引言 无线宽带通信的迅猛发展需要能传输高比特率的新型宽带天线。毫米波段是短距离高比特率无线通信的重要波段。所以近年来&#xff0c;毫米波段小型高性能的超宽带天线吸引了大量的研究人员在这方面进行研究工作。 天线设计的另一个重要趋势是集成天线的射频前端电路。在…

Linux/Unix编写sh脚本,设置开机自动启动脚本在linux下

1编写.sh脚本的基本步骤&#xff1a; 创建一个新的文本文件&#xff0c;使用一个文本编辑器&#xff0c;比如vi或nano&#xff0c;来编写脚本。你需要保存文件为.sh扩展名&#xff0c;以便让系统知道这是一个shell脚本文件。 touch myprogram.sh #!/bin/sh# 进入C程序所在目…

基于JAVA的网络通讯系统设计与实现

一般来说&#xff0c;聊天工具大多数由客户端程序和服务器程序,外加服务器端用于存放客户数据的数据库组成&#xff0c;本系统采用客户机/服务器架构模式&#xff0c;通过Java提供的Socket类来连接客户机和服务器并使客户机和服务器之间相互通信&#xff0c;由于聊天是多点对多…

HTB-Cascade

HTB-Cascade 信息收集立足s.smith -> arksvc使用脚本获取CascAudit.exe加密的密码明文修改IL指令获取 arksvc -> administrator 信息收集 查看smbclient。 rpcclient空密码连接并收集信息。 收集到用户列表&#xff0c;此外没有有意思的信息。 CascGuest arksvc s.smi…

Qt/QML编程学习之心得:二进制兼容、私有实现及Q_D/Q_Q指针(三)

QML画好或说描述好界面之后,实现部分通过C++实现,Qt采用了私有实现的设计模式解决了二进制兼容的问题。 二进制兼容问题描述: 现在有一个Widget, 包含一个私有成员变量m_geometry ,编译 Widget 并且发布为MyWidgetLib 1.0 。该应用程序名字为TestAPP,基于 Qt 4.9。 cla…

【Linux网络服务】Linux网络设置

一、查看网络配置 1.1ifconfig 1.2ip a 1.3什么是mtu 最大传输单元MTU&#xff0c;是指网络能够传输的最大数据包大小&#xff0c;以字节为单位。MTU的大小决定了发送端一次能够发送报文的最大字节数。如果MTU超过了接收端所能够承受的最大值&#xff0c;或者是超过了发送路径…

异步爬虫的原理和解析

我们知道爬虫是 IO 密集型任务&#xff0c;比如如果我们使用 requests 库来爬取某个站点的话&#xff0c;发出一个请求之后&#xff0c;程序必须要等待网站返回响应之后才能接着运行&#xff0c;而在等待响应的过程中&#xff0c;整个爬虫程序是一直在等待的&#xff0c;实际上…

焦虑症会出现哪些问题 什么因素导致的焦虑症

当说起焦虑症&#xff0c;大多数人想到的就是植物神经紊乱&#xff0c;确实&#xff0c;这两种疾病是非常容易混淆的&#xff0c;甚至很多时候植物神经紊乱都会当做焦虑症进行治疗&#xff0c;虽然这种疾病大多效果不会太理想。 你们知道什么是焦虑症吗&#xff1f; 很多人当出…

Android ProtoLog动态开启相关wm logging源码分析补充

Android ProtoLog动态开启相关wm logging源码分析补充 针对上一节已经清楚了相关的代码中怎么可以打印到logcat中&#xff0c;其实本质上还就是protologtool这个工具对代码中的所有ProtoLog进行了相关的替换成了具体实现&#xff0c;最后会条件判断输出到Slog中 本文就重点来看…

【池化方法】多示例学习池化(MIL pooling)公式与代码

一般的池化方法包括最大池化、平均池化、自适应池化与随机池化&#xff0c;这几天意外看到了多示例学习池化&#xff0c;感觉挺有意思的&#xff0c;记录一下。   论文   代码 1. 多示例学习&#xff08;Multiple instance learning&#xff0c;MIL&#xff09; 经典深度学…

梯度下降算法原理详解及MATLAB程序代码(最简单)

模型就是线性规划及线性规划的对偶理论&#xff0c;单纯形法以及它的实际应用&#xff1a;整数规划及其解法(分支定界法、割平面法匈牙利算法Q)&#xff0c;目标规划&#xff0c;非线性规划动态规划、决策分析等等。 其它的一些优化算法。比如说一维搜索里面的黄金分割法、加步…

PostMan笔记(二)发送请求

1. 发送请求功能介绍 Postman是一款流行的API开发工具&#xff0c;它可以让开发人员更方便地测试、调试和使用API。其中&#xff0c;发送请求功能是Postman最为重要和基础的功能之一。 在Postman中&#xff0c;发送请求功能主要包括以下几个步骤&#xff1a; 选择请求方法&am…

数据分析时,进行数据建模该如何筛选关键特征?

1.为什么要做关键特征筛选&#xff1f; 在数据量与日俱增的时代&#xff0c;我们收集到的数据越来越多&#xff0c;能运用到数据分析挖掘的数据也逐渐丰富起来&#xff0c;但同时&#xff0c;我们也面临着如何从庞大的数据中筛选出与我们业务息息相关的数据。&#xff08;大背景…

Java的对象克隆

本节我们会讨论 Cloneable 接口&#xff0c;这个接口指示一个类提供了一个安全的 clone() 方法。 Object 类提供的 clone() 方法是 “浅拷贝”&#xff0c;并没有克隆对象中引用的其他对象&#xff0c;原对象和克隆的对象仍然会共享一些信息。深拷贝指的是&#xff1a;在对象中…

微服务---一篇学完SpringCloud

SpringCloud 1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构&#xff1a…