我有6个整数参数,范围从0到100
这些数字的总组合数为100^6,每个组合的结果大约在-10000到100000之间,甚至可能更高。
输入数据示例:MySimulation (57, 78, 20, 10, 90, 50) = 300 <- 最佳结果MySimulation (50, 80, 10, 90, 35, 8) = 200MySimulation (4, 55, 40, 99, 40, 50) = -50 <- 最差结果
结果越高,数字组合就越好,我已经有了计算结果的公式,我只需要AI来找到一个更好的数字组合以获得更高的结果。
输出数据示例:55, 70, 25, 15, 95, 52 <- 假设这些数字组合是由AI选择的,并且在我的模拟中会产生400的结果
注意:数字的顺序也很重要。
如何使用AI减少100^6的总组合数,以便在不遍历所有100^6个组合的情况下获得最佳结果?
我计划在C#中使用Accord.NET(或者有更好的选择吗?),因为我是AI新手,所以一个代码示例会很有帮助。
回答:
欢迎进入多目标优化领域。这是我的博士论文研究的领域。解决此类问题有许多算法,但最著名的可能是NSGA-II和SPEA2。
当然,你只有一个目标:你的评分函数产生的任何结果。我认为多目标算法也适用,因为你不仅对单一解决方案感兴趣,还对它们的群体感兴趣。
我可以推荐你访问http://jmetalnet.sourceforge.net/吗?
这个想法是你将生成包含跨越100^6个可能解决方案的输入的随机向量群体。这些群体将被突变和交配以生成新的解决方案,从这些新群体中,它们将以一种方式被筛选,使得更优选的解决方案被选中留存(并在进化中生存)。
在多目标世界中,你可能会面临比较不同解决方案的适应度的问题。但在你的单目标世界中,比较适应度很简单:你只需要决定你是想要更高的数字还是更低的数字。看起来你想要更高的数字。
概述
- 创建随机解决方案群体。
- 在你的解决方案中随机进行突变/交叉操作。
- 计算每个体的适应度,并排序。
- 将最佳解决方案回采样到初始群体大小。
- 重复步骤2-4 [直到收敛:直到平均适应度>阈值?]
- 输出最终一代。
结果:
这是一个粗略的分析,值得注意的是,你可以通过平均每个参数级别的(比如)20次运行的结果来做得更好。首先,你可以看出突变率应该保持低水平,显然,更高的群体大小可以帮助(直到收敛点)。
结果格式为平均分数前后,最高600
PopSize=100,NumGens=50,MutRate=0.2,CrossRate=0.8
295.23,542.12
PopSize=100,NumGens=500,MutRate=0.2,CrossRate=0.8
298.53,565
PopSize=1000,NumGens=50,MutRate=0.2,CrossRate=0.8
301.814,579.334
PopSize=10000,NumGens=500,MutRate=0.2,CrossRate=0.8
299.8901,588
PopSize=1000,NumGens=50,MutRate=0.4,CrossRate=0.8
306.22,385.55
代码
我用了大约20分钟写这段代码,所以它并不优雅或出色。我希望它只是传达了要点。
using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;namespace moo_in_csharp{ internal class Individual { public int[] Decisions; public double Fitness; private int _numdecisions = 6; /// <summary> /// 默认构造函数。 /// </summary> public Individual() { Decisions = new int[_numdecisions]; } /// <summary> /// 用配偶的决策替换决策的前半部分。 /// </summary> /// <param name="mate"></param> public void Crossover(Individual mate) { int crossoverPoint = _numdecisions / 2; for (int i = 0; i < crossoverPoint; i++) { Decisions[i] = mate.Decisions[i]; } } /// <summary> /// 简单适应度函数,计算a+b+c+d+e+f的总和。 /// </summary> public double Evaluate() { Fitness = Decisions.Sum(); return Fitness; } /// <summary> /// 为其决策分配随机值。 /// </summary> public void Generate() { for (int i = 0; i < _numdecisions; i++) { Decisions[i] = Program.rand.Next(0, 101); } } /// <summary> /// 随机突变选择的决策。 /// </summary> public void Mutate() { for (int i = 0; i < _numdecisions; i++) { Decisions[i] = Program.rand.Next(0, 101); } } } internal class Program { public static Random rand = new Random(); private static void Main(string[] args) { //参数 int populationSize = 100; int numGenerations = 50; double mutationRate = 0.2; double crossoverRate = 0.8; //构建初始群体 List<Individual> solutions = new List<Individual>(); for (int i = 0; i < populationSize; i++) { var solution = new Individual(); solution.Generate(); solution.Evaluate(); solutions.Add(solution); } //计算初始群体的平均分数 var averageScoreBefore = solutions.Select(x => x.Evaluate()).Average(); //迭代生成代数 for (int i = 0; i < numGenerations; i++) { //通过交配顺序对的解决方案构建后代 var offspring = new List<Individual>(); for (int e = 0; e < solutions.Count() - 1; e += 2) { if (rand.NextDouble() < crossoverRate) { var newIndividual = new Individual(); solutions[e].Decisions.CopyTo(newIndividual.Decisions, 0); newIndividual.Crossover(solutions[e + 1]); offspring.Add(newIndividual); } } //将我们的后代添加到我们的解决方案中 solutions.AddRange(offspring); //以低概率突变解决方案 foreach (var solution in solutions) { if (rand.NextDouble() < mutationRate) { solution.Mutate(); } } //对我们的解决方案进行排序,并回采样到初始群体大小 solutions = solutions.OrderByDescending(x => x.Evaluate()).ToList(); solutions = solutions.Take(populationSize).ToList(); } //计算之后的平均分数 var averageScoreAfter = solutions.Select(x => x.Evaluate()).Average(); Debug.WriteLine(averageScoreBefore + "," + averageScoreAfter); } }}
其他说明
你的运行时间主要取决于你的适应度评分函数。对于简单的数学函数,这个运行时间不会很难。显然,如果涉及到一个过程,你会希望将评估次数保持在最低限度。这是我的博士研究内容,我开发了一种名为GALE的新算法: