1 个不稳定版本
0.1.0 | 2020年4月29日 |
---|
#2613 in 算法
11KB
57 行
生态系统
一个小型的遗传算法库,使用 Rust 编写。
要在项目中使用此库,只需将其 Cargo.toml
依赖项中的以下行添加即可
ecosystem = "0.1"
注意,以下使用说明假定您对遗传算法有基本了解。如果您对该主题一无所知,但希望了解更多信息,我可以推荐 The Coding Train 的入门友好视频系列,您可以在这里找到。
基础
此库围绕两个关键组件构建:生物体和生态系统。
生物体
将 Organism
特质实现在任何类型上将使其成为 Ecosystem
的一部分,从而启用遗传功能。它要求您填写三个方法
fitness
:此方法应返回一个指标,表明生物体执行其被分配的任务的效率如何breed
:此方法应返回一个具有父母双方属性(“遗传物质”)混合的子生物体mutate
:此方法应通过rate
参数确定的量随机修改生物体
此特质的示例用法在示例说明中进行了介绍。
生态系统
Ecosystem
是一组 Organism
,其中包含繁殖新代的功能。
您可以这样创建一个
use ecosystem::Ecosystem;
// `your_organisms` must be a vector of items which implement
// the `Organism` trait
let mut ecosystem = Ecosystem::new(your_organisms);
您可以通过调用 breed_next_generation
方法繁殖新一代的生物体。这将覆盖现有的生物体,这意味着人口数量将保持不变。
// The only argument passed to the method is the mutation rate,
// a floating-point value that describes the extent to which the
// new organisms should be mutated (randomly modified)
ecosystem.breed_next_generation(0.1);
由于了解 Ecosystem
中哪个生物体的适应度最高通常是有益的,因此它们还包括辅助方法 fittest
let the_best = ecosystem.fittest();
下一节将介绍一个简单的示例场景,在这些场景中这些基本概念得到了实际应用。
示例说明
本节将介绍如何构建一个生态系统,其中的生物体试图逼近 π 的值。
建立基础
在我们开始添加遗传功能之前,我们需要设置两件事。
首先,我们需要一种结构来表示一个“π近似器”,它将简单地保存其猜测值
struct PiApproximator {
value: f64,
}
其次,我们需要创建一个包含多个这些近似器的向量,我们可以使用一个简单的迭代器来完成这个任务
use rand::Rng;
const POPULATION_COUNT: u32 = 10;
const MAX_INITIAL_VALUE: f64 = 10.0;
fn main() {
let mut rng = rand::thread_rng();
let approximators: Vec<PiApproximator> = (0..POPULATION_COUNT)
.map(|_| PiApproximator {
value: rng.gen_range(-MAX_INITIAL_VALUE, MAX_INITIAL_VALUE),
})
.collect();
}
添加遗传功能
为了添加遗传功能,我们必须首先在PiApproximator
结构体上实现Organism
特征,这需要我们填写fitness
、breed
和mutate
方法。
use ecosystem::Organism;
impl Organism for PiApproximator {
fn fitness(&self) -> f64 { ... }
fn breed(&self, other: &Self) -> Self { ... }
fn mutate(&mut self, rate: f64) { ... }
}
让我们按顺序逐个讲解。
计算近似器的健康度的一种方法是将其猜测值与π的真实值之间的差值的倒数。这意味着一个近似器的猜测值越接近,其最终的健康度就越高。
fn fitness(&self) -> f64 {
let diff = (std::f64::consts::PI - self.value).abs();
1.0 / diff
}
繁殖近似器甚至更简单:我们只需取两个父代值的平均值
fn breed(&self, other: &Self) -> Self {
Self {
value: (self.value + other.value) / 2.0,
}
}
最后,我们可以通过随机上下移动近似器的值来变异它。rate
越高,潜在的变化就越大。
fn mutate(&mut self, rate: f64) {
let change = rand::thread_rng().gen_range(-rate, rate);
self.value += change;
}
为了将这些整合在一起,让我们构建一个由我们之前创建的近似器组成的Ecosystem
use ecosystem::Ecosystem;
fn main() {
...
let mut ecosystem = Ecosystem::new(approximators);
}
最后,我们可以设置一个循环,使其运行一定数量的代数,并在每次循环中打印最佳近似器的猜测值
const GENERATIONS: u32 = 50;
const MUTATION_RATE: f64 = 0.1;
fn main() {
...
for _ in 0..GENERATIONS {
ecosystem.breed_next_generation(MUTATION_RATE);
println!("{}", ecosystem.fittest().value);
}
}
就这样!尝试调整常数,看看你能得到什么不同的结果。
整个教程的完整代码位于此存储库的examples
文件夹中。
依赖项
~3.5MB
~62K SLoC