图 1 - 来自 计算机艺术作品的遗传艺术
无可否认,图 1 中的艺术有点令人不安,但尽管如此,它可能可以被看作是艺术,
它不是由人手绘制的,而是由计算机算法绘制的。艺术界的许多人正在尝试所谓的进化艺术,
甚至出售了他们的一些作品。遗传艺术是进化艺术的一个子集,使用遗传算法来生成艺术。
要了解有关进化和遗传艺术的更多信息,我建议访问有机、遗传和进化艺术页面。
在本文中,我们将对遗传算法进行一些实验,并创建在 Windows 窗体上生成艺术作品的适应度函数。
为简单起见,我们将使用从几个通用数学公式确定的数据点的近似值在二维空间中生成艺术作品。
我们使用的遗传算法程序利用多重表达式编程 (MEP)。多重表达式编程生成数学公式的基因序列,
在此示例中,公式的适应度与一组数据进行比较。要了解有关 MEP 的更多信息,
请查看我之前的文章,使用多重表达式编程在 C# 和 .NET 中推导勾股定理。
用数学方程式创作艺术的基本思想是,通过对 x,y 坐标进行运算的公式产生颜色。
(在三维空间中,你可以从 (x,y,z) 坐标产生颜色。)在我们的示例中,我们调整公式,
从 0-3 范围内的结果中产生 4 种可能的颜色。用于比较适应度的第一个数据集来自下面显示的简单公式:
(a + b) mod 4
此公式强制所有模拟结果为 0 到 3 之间的值。遗传算法将产生试图收敛到近似于上述公式的公式的基因。
实际上,算法最好不要收敛到正确答案 (a+b) mod 4,因为公式的近似值才是产生原始艺术作品的原因。
事实上,您可能希望在 MEP 基因中选择一些更不寻常的函数(在此示例中,
一些基因包括 sin(a*b)、a/b 和 abs (a) + abs(b)。
此外,您可以在适应度集中放置一些不准确的结果,以添加一些有趣的效果。
以下是经过 1000 代公式收敛后由基因产生的一些艺术作品:
图 2 - 根据适应度公式 (a+b) mod 4 生成的遗传艺术
看到模块 4 收敛产生了一些有趣的效果,我们为下一个艺术画廊选择了类似的公式,ab mod 4。
我们还在数据集中引入了一些 (ab) mod 5 数据,以引起一些奇怪的收敛:
图 3 - 从 (ab) mod 4 的适应度集生成的纹理
最后,根据模数函数的成功,我们根据以下内容创建了一组数字:
稍微改变一下配色方案,通过从 (0-4) 的结果中生成一组不同的颜色,
并针对 a b mod 5 数据在 1000 代上运行算法, 我们得到了下面显示的艺术纹理:
图 4 - 基于公式集 运行遗传算法的艺术作品
有些人可能会认为这种计算机生成的艺术并不完全是梵高的作品,但它确实有点吸引眼球,而且你可以创作的作品数量是无限的。通过调整测量结果集、算法使用的基因公式、产生的颜色数量和产生的颜色种类,你真的可以创作出一些很酷的抽象作品(谁知道呢,甚至可能是一件杰作!)
代码
遗传算法产生代表公式的基因。当您将一组坐标插入公式时,它会产生一个代表颜色的数字。清单 1 中的 EquationGenome 的颜色绘图方法称为Plot。 基本上,根据像素插入方程基因组时的坐标,为表单上的每个像素分配一种颜色。您可以更改整数代表的颜色以产生不同的颜色组合。您还可以以不同的间隔减少或增加颜色以更改输出的颜色分辨率。
cs
public void Plot(Form1 TheForm, Graphics g)
{
// Fill the whole form with a light gray color to erase old drawing
g.FillRectangle(Brushes.LightGray, TheForm.ClientRectangle);
// go through each x,y coordinate and plug it into the equation
// produced by the genome to derive an integer representing a
// color
for (int i = 0; i < 300; i++)
for (int j = 0; j < 300; j++)
{
double calc1 = PerformCalculation(i, j); // do calculation
if (calc1 > 4)
g.DrawLine(Pens.LightBlue, i, j, i+1, j);
else if (calc1 > 3)
g.DrawLine(Pens.Purple, i, j, i, j+1);
else if (calc1 > 2)
g.DrawLine(Pens.Red, i, j, i+1, j);
else if (calc1 > 1)
g.DrawLine(Pens.Yellow, i, j, i, j+1);
else
g.DrawLine(Pens.Navy, i, j, i+1, j);
}
}
适应度函数 a b mod 5的数据点如清单 2a 所示。每个三元组中的前两个数字分别为 a 和 b。最后一个数字是方程式 a b mod 5 的结果值。
cs
double[,] measure5 = new double[,]
{
{0, 0, 0}, {0, 1, 0}, {0, 2, 0}, {0, 3, 0}, {0, 4, 0},
{1, 1, 1}, {1, 2, 1}, {1, 3, 1}, {1, 4, 1}, {1, 5, 1},
{2, 1, 2}, {2, 2, 4}, {2, 3, 3}, {2, 4, 1},
{-2, 2, 4}, {3, -2, .111}, {3, -5, .00411}, {-1,5,-1}, {2,4,1},
{2,5,2}, {3,4,1}, {3,7, 2}, {2,9,2}, {5, 2, 0},
{6, 2, 1}, {7, 2, 4}, {4, 2, 1}, {11, 2, 1}, {3, 2, 2},
{-2, 1, -2}
};
用于与清单 2a 中的数据点进行比较的适应度函数如下所示,如清单 2b 所示。每个数据点都被读入适应度函数,并与基因组的计算结果进行比较。比较结果的总和产生了一个适应度,告诉我们我们与清单 2a 中的测量集有多接近。基因组产生的这个近似方程为我们提供了有趣的异常,从而产生了图 4 中的纹理。
cs
public double CalculateArtFitness5()
{
double fitness = 0.0;
double calc = 0.0f;
double sum = 0.0f;
// go through each data point (produced from the formula a pow b mod 5)
// and calculate a fitness of the equation
for (int i = 0; i < measure5.GetLength(0); i++)
{
calc = PerformCalculation(measure5[i, 0], measure5[i, 1]); // do calculation
// calculate a fitness number comparing the actual measured value
// to the value produced by the equation
double error = 100 - Math.Abs(measure5[i, measure5.GetLength(1)- 1] - calc); // last byte
// add it to the overall average
sum += error;
// if its an undefined result, return a low fitness
if (double.IsNaN(calc))
return .001;
}
// normalize the fitness
CurrentFitness = sum/(measure5.GetLength(0)*100);
// if its an undefined result, return a low fitness
if (double.IsNaN(CurrentFitness))
CurrentFitness = 0.001f;
// don't allow fitnesses less or equal to zero
if (CurrentFitness <= 0.0)
CurrentFitness = .001;
return CurrentFitness;
}
如果能稍微修改一下此代码,绘制三维颜色映射图,看看结果会产生什么样的景观,那就太好了。虽然我们在基因组中使用了正弦函数,但人们可以选择双曲正弦、反正切和一些非线性函数作为基因组构建的一部分,以产生其他奇怪的效果。将颜色集减少到 2 种颜色也可能很有趣。此外,将基因组更改为包含复数数学函数将为我们提供一个全新的探索领域。无论如何,尽情享受用 C# 和 .NET创造色彩缤纷的艺术吧,而不必在你的基因上涂满杂乱的颜料。
(结束)