2009-11-27 119 views
2

我正在做一些遗传算法的工作,并且想写我自己的GA类。由于遗传算法可以有不同的方式进行选择,变异,交叉,生成初始种群,计算适应度以及终止算法,所以我需要一种方法来插入这些方法的不同组合。我最初的方法是创建一个抽象类,将所有这些方法定义为纯虚拟,任何具体的类都必须实现它们。例如,如果我想尝试两个相同但具有不同交叉方法的GAs,则必须创建一个从GeneticAlgorithm继承的抽象类,并实现除交叉方法之外的所有方法,然后创建两个具体类它从这个类继承,只实现了交叉方法。这样做的缺点是每次我想换出一两个方法来尝试新的东西时,我必须创建一个或多个新类。如何构造遗传算法类层次结构?

是否有另一种方法可以更好地应用于此问题?

回答

1

我会将遗传算法看作是许多对象的协作,而不是一个封装整个算法的大类。基本上,您可以为每个大型变体提供一个抽象类,并为每个实现选择提供具体类。然后,您将您想要的具体课程合并为多种GA。

此外,您可能需要熟悉与策略模式: http://en.wikipedia.org/wiki/Strategy_pattern

1

我认为你是在你的方式复杂化的东西。建议您下载GAlib包。即使您只以html或pdf格式拉取文档。这些库已经存在了一段时间,我真的相信你会学会如何构建你的lib,看看在GAlib中已经做了什么。

1

从我的一部分。一些随机位:

  • 你应该检查(作为方法)的一个项目是watchmaker
  • 建设气体的最难的是找到问题的一个明智的基因表达并建立一个健身功能与良好的分配健身对于给定的人口
  • 当处理(米)任何硬约束,你可以考虑引入一个翻译 (可能)垃圾DNA和一点性能的代价
0

正如人们所说,不要让它成为一个巨人类。那太可怕了。封装不同类别的行为。策略是一个解决方案。
如果您需要示例下载源和JGAP的示例。它支持遗传编程和遗传算法。你会看到不错的漂亮设计。突变,交叉,选择,人口,基因 - 所有这些都是单独的类。您只需设置Configuration对象,在其中启动要使用的实现的已定义接口,并传递适当的算法参数并运行它。真正的包是巨大的,javadoc很好,你可以随时查看源代码或检查邮件组的某些答案。当我在寻找GA包时,我看到GAlib和其他人,但我认为这是一个非常完美的设计。

2

实现我的GA框架是如下时,我采取的方法: 创建以下类别: 代 GeneticAlgorithm GeneticAlgorithmAdapter GeneticAlgorithmParameters 人口 个人

虽然我没有实现的战略模式各种操作,我确信在GeneticAlgorithm实例上创建各种GA操作实现作为参数是微不足道的。

GeneticAlgorithm类捕获基本算法。它确实定义了各种步骤(人口创建,个体随机化,选择,交叉,变异等),并在算法运行时管理个体的种群。我想如果你想的话,你可以在这里插入不同的操作。

真正的魔力在于适配器。这是将问题域(个人的具体子类及其所有相关数据)适用于遗传算法。我在这里大量使用泛型,以便将具体类型的人口,参数和个人传递到实现中。这使我能够对适配器的实现进行智能感知和强类型检查。适配器基本上需要定义如何执行给定个体(及其基因组)的特定操作。例如,下面是适配器接口:

/// <summary> 
/// The interface for an adapter that adapts a domain problem so that it can be optimised with a genetic algorithm. 
    /// It is a strongly typed version of the adapter. 
    /// </summary> 
    /// <typeparam name="TGA"></typeparam> 
    /// <typeparam name="TIndividual"></typeparam> 
    /// <typeparam name="TPopulation"></typeparam> 
    public interface IGeneticAlgorithmAdapter<TGA, TIndividual, TGeneration, TPopulation> : IGeneticAlgorithmAdapter 
     where TGA : IGeneticAlgorithm 
     where TIndividual : class, IIndividual, new() 
     where TGeneration : class, IGeneration<TIndividual>, new() 
     where TPopulation : class, IPopulation<TIndividual, TGeneration>, new() 
    { 
     /// <summary> 
     /// This gets called before the adapter is used for an optimisation. 
     /// </summary> 
     /// <param name="pso"></param> 
     void InitialiseAdapter(TGA ga); 

     /// <summary> 
     /// This initialises the individual so that it is ready to be used for the genetic algorithm. 
     /// It gets randomised in the RandomiseIndividual method. 
     /// </summary> 
     /// <param name="ga">The genetic algorithm that is running.</param> 
     /// <param name="individual">The individual to initialise.</param> 
     void InitialiseIndividual(TGA ga, TIndividual individual); 

     /// <summary> 
     /// This initialises the generation so that it is ready to be used for the genetic algorithm. 
     /// </summary> 
     /// <param name="ga">The genetic algorithm that is running.</param> 
     /// <param name="generation">The generation to initialise.</param> 
     void InitialiseGeneration(TGA ga, TGeneration generation); 

     /// <summary> 
     /// This initialises the population so that it is ready to be used for the genetic algorithm. 
     /// </summary> 
     /// <param name="ga">The genetic algorithm that is running.</param> 
     /// <param name="population">The population to initialise.</param> 
     void InitialisePopulation(TGA ga, TPopulation population); 

     void RandomiseIndividual(TGA ga, TIndividual individual); 

     void BeforeIndividualUpdated(TGA ga, TIndividual individual); 
     void AfterIndividualUpdated(TGA ga, TIndividual individual); 

     void BeforeGenerationUpdated(TGA ga, TGeneration generation); 
     void AfterGenerationUpdated(TGA ga, TGeneration generation); 

     void BeforePopulationUpdated(TGA ga, TPopulation population); 
     void AfterPopulationUpdated(TGA ga, TPopulation population); 

     double CalculateFitness(TGA ga, TIndividual individual); 

     void CloneIndividualValues(TIndividual from, TIndividual to); 

     /// <summary> 
     /// This selects an individual from the population for the given generation. 
     /// </summary> 
     /// <param name="ga">The genetic algorithm that is running.</param> 
     /// <param name="generation">The generation to select the individual from.</param> 
     /// <returns>The selected individual.</returns> 
     TIndividual SelectIndividual(TGA ga, TGeneration generation); 

     /// <summary> 
     /// This crosses over two parents to create two children. 
     /// </summary> 
     /// <param name="ga">The genetic algorithm that is running.</param> 
     /// <param name="parentsGeneration">The generation that the parent individuals belong to.</param> 
     /// <param name="childsGeneration">The generation that the child individuals belong to.</param> 
     /// <param name="parent1">The first parent to cross over.</param> 
     /// <param name="parent2">The second parent to cross over.</param> 
     /// <param name="child">The child that must be updated.</param> 
     void CrossOver(TGA ga, TGeneration parentsGeneration, TIndividual parent1, TIndividual parent2, TGeneration childsGeneration, TIndividual child); 

     /// <summary> 
     /// This mutates the given individual. 
     /// </summary> 
     /// <param name="ga">The genetic algorithm that is running.</param> 
     /// <param name="generation">The individuals generation.</param> 
     /// <param name="individual">The individual to mutate.</param> 
     void Mutate(TGA ga, TGeneration generation, TIndividual individual); 

     /// <summary> 
     /// This gets the size of the next generation to create. 
     /// Typically, this is the same size as the current generation. 
     /// </summary> 
     /// <param name="ga">The genetic algorithm that is running.</param> 
     /// <param name="currentGeneration">The current generation.</param> 
     /// <returns>The size of the next generation to create.</returns> 
     int GetNextGenerationSize(TGA ga, TGeneration currentGeneration); 


     /// <summary> 
     /// This gets whether a cross over should be performed when creating a child from this individual. 
     /// </summary> 
     /// <param name="ga">The genetic algorithm that is running.</param> 
     /// <param name="currentGeneration">The current generation.</param> 
     /// <param name="individual">The individual to determine whether it needs a cross over.</param> 
     /// <returns>True to perform a cross over. False to allow the individual through to the next generation un-altered.</returns> 
     bool ShouldPerformCrossOver(TGA ga, TGeneration generation, TIndividual individual); 

     /// <summary> 
     /// This gets whether a mutation should be performed when creating a child from this individual. 
     /// </summary> 
     /// <param name="ga">The genetic algorithm that is running.</param> 
     /// <param name="currentGeneration">The current generation.</param> 
     /// <param name="individual">The individual to determine whether it needs a mutation.</param> 
     /// <returns>True to perform a mutation. False to allow the individual through to the next generation un-altered.</returns> 
     bool ShouldPerformMutation(TGA ga, TGeneration generation, TIndividual individual); 
    } 

我发现,这种方法很好地工作适合我,因为我可以很容易地重用GA实施不同的问题域,仅仅是写在相应的适配器。就不同的选择,交叉或变异实现而言,适配器可以调用它感兴趣的实现。当我正在研究适当的策略时,我通常会做的是在适配器中注释不同的想法。

希望这会有所帮助。我可以在必要时提供更多指导。做这样的设计公正很难。