1 个不稳定版本

0.1.0 2020年4月29日

#2613 in 算法

自定义许可证

11KB
57

生态系统

一个小型的遗传算法库,使用 Rust 编写。

要在项目中使用此库,只需将其 Cargo.toml 依赖项中的以下行添加即可

ecosystem = "0.1"

注意,以下使用说明假定您对遗传算法有基本了解。如果您对该主题一无所知,但希望了解更多信息,我可以推荐 The Coding Train 的入门友好视频系列,您可以在这里找到。

基础

此库围绕两个关键组件构建:生物体和生态系统。

生物体

Organism 特质实现在任何类型上将使其成为 Ecosystem 的一部分,从而启用遗传功能。它要求您填写三个方法

  1. fitness:此方法应返回一个指标,表明生物体执行其被分配的任务的效率如何
  2. breed:此方法应返回一个具有父母双方属性(“遗传物质”)混合的子生物体
  3. 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特征,这需要我们填写fitnessbreedmutate方法。

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