IFS是迭代函数系统(Iterated Function System)的缩写. 它是一种数学工具, 常用于计算机图形学中生成分形图像. 在IFS中, 一组迭代函数被定义, 并且从一个简单的初始值开始, 通过重复应用这些函数, 可以生成复杂的结构. 这些迭代函数通常具有某种相似性或变换性质, 例如平移, 缩放, 旋转等. 在计算机图形学中, IFS用于创建分形图像, 其中每个像素或像素块被迭代函数处理, 以生成最终的图像. 这种方法可以生成具有自然现象(如山脉, 云朵, 树等)的复杂结构的图像. 此外, 随机IFS是一种特殊的IFS, 其中迭代函数和初始值都是随机选择的. 这种方法通常用于创建抽象艺术和计算机生成的艺术作品.
在本文仅在平面上考虑线性随机IFS迭代.
算法描述
在二维平面上, 我们有如下的仿射变换:
(
x
′
y
′
)
=
(
a
b
c
d
)
(
x
y
)
+
(
e
f
)
\begin{pmatrix} x^\prime\\y^\prime \end{pmatrix} =\begin{pmatrix} a&b\\c&d \end{pmatrix} \begin{pmatrix} x\\y \end{pmatrix}+ \begin{pmatrix} e\\f \end{pmatrix}
(x′y′)=(acbd)(xy)+(ef)
对于一个比较复杂的图形, 可能需要多个不同的仿射变换来实现, 仿射变换族 { ω n } \{\omega_n\} {ωn}控制着图形的结构和形状, 由于仿射变换的形式是相同的, 所以不同的形状取决于仿射变换的系数. 另外, 仿射变换族 { ω n } \{\omega_n\} {ωn}中, 每一个仿射变换被调用的概率不一定是等同的, 也就是说, 落入图形各部分中点的数目不一定相同, 这就需要引进一个新的量, 即仿射变换 ω \omega ω被调用的概率 P P P. 从而, 6个仿射变换系数 { a , b , c , d , e , f } \{a,b,c,d,e,f\} {a,b,c,d,e,f}和一个概率 P P P便组成了线性随机IFS迭代最关键的部分——IFS码.
给定一个IFS码, 我们便可以进行多次迭代, 最终生成所需的图形.
线性随机IFS迭代法可描述如下:
- 输入:IFS码 A \bm A A,迭代次数 n n n,迭代初值 x 0 \bm x_0 x0
- 输出:迭代路径 p p p
- 初始化 P P P 为 A A A 的最后1列逐元素累加
- 对于
i
i
i 从
1
1
1 到
n
n
n 进行以下步骤:
- 生成 [ 0 , 1 ) [0,1) [0,1) 均匀分布的随机数 r r r
- 在 P P P 中找到第一个大于 r r r 的下标 d d d
- 利用 A A A 的第 d d d 行进行仿射变换并加到 p p p 内
- 返回 p p p
算法实现
首先预处理如下:
#include <armadillo>
#include <stdio.h>
#include <random>
#include <ctime>
using namespace arma;
using namespace std;
再实现线性随机IFS迭代:
/*
* 线性随机IFS迭代
* ifs : IFS码
* n : 迭代次数
* x0 : 迭代初值
* path: 迭代过程保存路径
* e : 实数比较精度
*
* 返回(bool):
* true : 迭代失败
* false: 迭代成功
*/
bool IFS_iteration(const mat &ifs, unsigned n, const char *path, vec x0 = randu(2), const double &e = 1e-6)
{
if (ifs.n_cols != 7 || x0.n_elem != 2)
return true;
if (!n)
return true;
vec p = cumsum(ifs.col(6));
if (abs(p.at(p.n_elem - 1) - 1.) > e)
return true;
FILE *file;
if (fopen_s(&file, path, "w"))
return true;
fprintf(file, "x,y\n%f,%f", x0.at(0), x0.at(1));
static default_random_engine engine(time(nullptr));
static uniform_real_distribution<double> distribution;
do
{
double d(distribution(engine));
unsigned i(0);
while (p.at(i) <= d)
if (++i == p.n_elem)
{
--i;
break;
}
d = ifs.at(i, 0) * x0.at(0) + ifs.at(i, 1) * x0.at(1) + ifs.at(i, 4);
x0.at(1) = ifs.at(i, 2) * x0.at(0) + ifs.at(i, 3) * x0.at(1) + ifs.at(i, 5);
x0.at(0) = d;
fprintf(file, "\n%f,%f", x0.at(0), x0.at(1));
} while (--n);
fclose(file);
return false;
}
实例分析
已知某树木的IFS码如下所示:
A
=
(
0.06
0
0
0.6
0
0
0.1
0.04
0
0
−
0.5
0
1
0.1
0.46
0.32
−
0.34
0.38
0
0.6
0.1
0.48
−
0.15
0.17
0.42
0
1
0.23
0.43
0.37
−
0.26
0.48
0
1
0.23
0.42
−
0.36
0.35
0.31
0
0.8
0.24
)
A=\begin{pmatrix} 0.06&0&0&0.6&0&0&0.1\\ 0.04&0&0&-0.5&0&1&0.1\\ 0.46&0.32&-0.34&0.38&0&0.6&0.1\\ 0.48&-0.15&0.17&0.42&0&1&0.23\\ 0.43&0.37&-0.26&0.48&0&1&0.23\\ 0.42&-0.36&0.35&0.31&0&0.8&0.24 \end{pmatrix}
A=
0.060.040.460.480.430.42000.32−0.150.37−0.3600−0.340.17−0.260.350.6−0.50.380.420.480.31000000010.6110.80.10.10.10.230.230.24
代入程序, 利用Origin可绘制如下树木迭代相图: