迭代函数系统生成分形

迭代函数系统(Iterated Function System,IFS)可以用来创建分形图案,他所创建的图形永远是自相似的。
有一个很著名的树叶图形的绘制便是使用了此原理。

在一个二维平面中,有4种映射函数,可以将一个点映射到另一个 位置:

1.
    x(n+1)= 0
    y(n+1) = 0.16 * y(n)

2.
    x(n+1) = 0.2 * x(n) − 0.26 * y(n)
    y(n+1) = 0.23 * x(n) + 0.22 * y(n) + 1.6

3.
    x(n+1) = −0.15 * x(n) + 0.28 * y(n)
    y(n+1) = 0.26 * x(n) + 0.24 * y(n) + 0.44

4.
    x(n+1) = 0.85 * x(n) + 0.04 * y(n)
    y(n+1) = −0.04 * x(n) + 0.85 * y(n) + 1.6


可以看到,x(n+1)是x(n)和y(n)的函数,y(n+1)也是如此。即这一次的计算要使用到上一次的结果,因此称之为迭代。给定一个初始点 x(0),y(0),经过上面的映射函数的映射,便可以得到平面中许多点,这些点构成的图形便是分形图案。这个系统就叫做迭代函数系统。
但是,一共有4个映射函数,我怎么知道每次迭代是要使用哪一个呢?
因此,还需要给每个映射函数规定一个概率,按照概率来进行选择。

源码如下,注释很详细:

# 分形树叶
import numpy as np
import matplotlib.pyplot as pl
import time

# 蕨类植物叶子的迭代函数和概率
eq1 = np.array([[0,0,0],[0,0.16,0]])
p1 = 0.01
eq2 = np.array([[0.2,-0.26,0],[0.23,0.22,1.6]])
p2 = 0.07
eq3 = np.array([[-0.15, 0.28, 0],[0.26,0.24,0.44]])
p3 = 0.07
eq4 = np.array([[0.85, 0.04, 0],[-0.04, 0.85, 1.6]])
p4 = 0.85

def ifs(p, eq, init, n):
    # 迭代向量的初始化
    pos = np.ones(3, dtype=np.float)
    pos[:2] = init # pos=[x,y,1]

    # 通过函数概率,计算函数的选择序列
    p = np.add.accumulate(p) # 累加,[p1,p1+p2,p1+p2+p3,...]
    rands = np.random.rand(n) # array,0~1的随机数,长度为10
    select = np.ones(n, dtype=np.int) # 想要得到的效果:select[i]=m表示第i个序列为m(0<=m<=len(p)-1)
    for i,x in enumerate(p[::-1]): # p[x:y:z]表示从x遍历到y,间隔为z。p[::-1]表示将前两个省略,间隔为-1,前两个默认为p[len(p)-1]到p[0]
        select[rands

    # 结果的初始化
    result = np.zeros((n,2), dtype=np.float)
    c = np.zeros(n, dtype=np.float)

    for i in range(n):
        eqidx = select[i] # 所选函数的下标
        temp = np.dot(eq[eqidx], pos) # 迭代公式
        pos[:2] = temp # 更新迭代向量

        # 保存结果
        result[i] = temp # 第i次的结果:[x,y]
        c[i] = eqidx # 第i次的选择

    return result[:,0],result[:,1],c

start = time.clock()
x, y, c = ifs([p1,p2,p3,p4],[eq1,eq2,eq3,eq4], [0,0], 10000)
print(time.clock() - start)
pl.figure(figsize=(6,6))
pl.subplot(121)
pl.scatter(x, y, s=1, c="g", marker="s", linewidths=0) # s=1表示散列点大小为1,c="g"表示点的颜色为green,marker="s"表示点的形状为正方形(速度最快)
pl.axis("equal")
pl.axis("off")
pl.subplot(122)
pl.scatter(x, y, s=1,c = c, marker="s", linewidths=0) # c=c表示按照每次迭代选择的序列来作为颜色,方便知道四个函数产生的点的大概位置
pl.axis("equal")
pl.axis("off")
pl.subplots_adjust(left=0,right=1,bottom=0,top=1,wspace=0,hspace=0)
pl.gcf().patch.set_facecolor("white")
pl.show()

结果如下:


叶子上的每一个枝叶,都与原叶形状相同。
右图的蓝色枝干为1%的函数生成的点,蓝色和绿色的两个叶子是概率为7%的函数生成的点,剩下的黄色叶子为85的函数生成的点。

实验一:
以上是10万个点生成的图。如果改为1000个点,效果如下:

可以看出:
1、点的产生并不是从下而上累计的,而是每次都会有一定概率落在某个映射函数的结果集所在位置上,随着点数的增多,轮廓越来越明显和精细。
2、当起始点定了之后,每一个映射函数都有自己的一个映射空间,不会超出这个空间。比如两个7%的函数的映射空间就是在蓝叶子和绿叶子的轮廓中,不会跳出这个范围。

实验二:
将起始点(0,0)改为不均衡的数,如(10,1),(-1,10)等。发现结果的形状相同。说明与起始点的值无关。这是因为,映射函数是坐标的线性变换,也是仿射变换,即不改变原有的直线性和平行性,只是进行翻转和伸缩。因此在同一个函数的作用下,两个不同位置的点进行的翻转动作是相同的,因此得到的结果形状也是相同的。

你可能感兴趣的:(Machine,Learning,python)