在80年代末,研究人员克利福德·皮克沃(Clifford Pickover)开发了一种算法,用于生成一系列类似生物形态的图形,这些图形看起来像是微生物,或者可能是抽象画。这个过程非常简单,从一个复杂的变量函数F(z)开始,比如Sin(z) + z^2 + c,其中c是一个常数。然后,使用朱利亚集的标准收敛性研究,递归地处理这个函数,直到发现函数发散,即模值大于给定值。
关键在于皮克沃修改了算法,增加了一个关于结果实部和虚部大小的最终条件,但他在混淆了OR和AND运算符时犯了一个错误,正是这个错误让能够获得如此惊人的结果。
这个项目是用C#编写的,使用的是Visual Studio 2015。这个演示应用程序实际上只是一个幻灯片,展示了使用随机选择的函数生成的图像,这些函数来自一组十个函数中。常数c是随机选择的,这是获得许多不同形状的关键,即使使用相同的函数和相同的复平面子集。
首先,为这十个函数中的每一个定义了限制xmin, xmax, ymin, ymax,这些限制存储在一个名为_limits的数组中:
private double[,] _limits = new double[10, 4] { {
2, 4, -5, -3 },
{ -2, 2, -2, 2 },
{ -1.7, 1.7, -1.7, 1.7 },
{ -3, 3, -3, 3 },
{ -5, 5, -5, 5 },
{ -10, 10, -10, 10 },
{ -3, 3, -3, 3 },
{ -1.5, 1.5, -1.5, 1.5 },
{ -1.2, 1.2, -1.2, 1.2 },
{ -8, 8, -8, 8 }
};
为了处理复数,选择了System.Numerics命名空间中的Complex结构。Fz方法实现了选择的十个函数。实际上,不需要考虑太多来定义这些函数。写的几乎所有东西都会很好地工作:
private Complex Fz(Complex z, Complex c, int f) {
switch(f) {
case 0:
return Complex.Sin(z) + Complex.Pow(z, 2) + c;
case 1:
return Complex.Pow(z, 2) + Complex.Pow(z, 6) + c;
case 2:
return Complex.Pow(z, 2) + Complex.Pow(z, 5) + c;
case 3:
return Complex.Pow(z, 3) + c;
case 4:
return Complex.Sin(z) + Complex.Pow(z, 5) + c;
case 5:
return Complex.Sin(z) - Complex.Cos(z) + Complex.Pow(z, 2) + c;
case 6:
return Complex.Pow(z, 3) - Complex.Cos(z) + Complex.Pow(z, 2) + c;
case 7:
return Complex.Pow(z, 6) - Complex.Pow(z, 4) + Complex.Sinh(z) + c;
case 8:
return Complex.Pow(z, 10) - Complex.Sinh(z) + c;
default:
return Complex.Cos(z) + Complex.Pow(z, 4) + c;
}
}
这是进行计算并绘制结果的方法。选择了使用async / await异步编程,以便在显示图片时让应用程序保持响应。
没有使用Bitmap类的SetPixel方法,而是通过使用包含像素颜色的整数数组的优化方式。在过程结束时,这些数据通过一次操作复制到Bitmap中。
private Task<Bitmap> BiomorphAsync(Complex c, int f) {
return Task.Run(() => {
int[] bmpbits = new int[640 * 480];
for (int ix = 0; ix < bmpbits.Length; ix++) {
bmpbits[ix] = Color.White.ToArgb();
}
double xmin = _limits[f, 0];
double xmax = _limits[f, 1];
double ymin = _limits[f, 2];
double ymax = _limits[f, 3];
double px = (xmax - xmin) / 639;
double py = (ymax - ymin) / 479;
for (int p = 0; p < 640; p++) {
for (int q = 0; q < 480; q++) {
Complex z = new Complex(xmin + p * px, ymin + q * py);
for (int k = 1; k < 50; k++) {
z = Fz(z, c, f);
if (z.Magnitude > 100) {
break;
}
}
if (Math.Abs(z.Real) < 100 || Math.Abs(z.Imaginary) < 100) {
bmpbits[p + 640 * (479 - q)] = Color.Black.ToArgb();
}
}
}
Bitmap bmp = new Bitmap(640, 480);
BitmapData bd = bmp.LockBits(new Rectangle(0, 0, 640, 480), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
Marshal.Copy(bmpbits, 0, bd.Scan0, bmpbits.Length);
bmp.UnlockBits(bd);
return bmp;
});
}
这个过程是在表单的Load事件中启动的,并循环直到FormClosing事件被触发。常数c是随机创建的,函数也是以同样的方式选择的。
private async void frmBio_Load(object sender, EventArgs e) {
_stopBio = false;
Random rnd = new Random();
while (!_stopBio) {
pbImage.Image = await BiomorphAsync(new Complex(rnd.NextDouble() * (rnd.Next(3) + 1), rnd.NextDouble() * (rnd.Next(3) + 1)), rnd.Next(9));
await WaitmsAsync(1000);
}
}
private void frmBio_FormClosing(object sender, FormClosingEventArgs e) {
_stopBio = true;
}