dart - 遗传算法在 Dart/Flutter 中吐出奇怪的结果
问题描述
在我的应用程序中,我使用遗传算法来为某个问题获得良好的结果。算法本身是同步的,但调用它的函数是异步的,以防止 UI 冻结。
在我的算法中,变量bestSolution
保持算法迄今为止产生的最佳解决方案。只要算法找到比当前更好的解决方案,它就会改变bestSolution
。当它改变时,我也会将新的打印bestSolution
到日志中。
算法完成后(目前在 500 代后完成),bestSolution
传递给调用遗传算法的函数以更新 UI。但是最后的 bestSolution 与之前的任何值都完全不同。我尝试了多次,最终结果从未登录到控制台,对于算法试图实现的结果来说也是一个非常糟糕的结果。
我将把算法以及一些日志输出放在这里,调用它的函数只是调用solve()
my 的函数GeneticAlgorithmSolver
。很感谢任何形式的帮助。
算法:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:meal_tracker/model/meal.dart';
class GeneticAlgorithmSolver {
Random random = Random();
List<Meal> breakfasts;
List<Meal> lunches;
List<Meal> dinners;
double proteinGoal;
double carbGoal;
double fatGoal;
int generationCount;
int populationCount;
double mutationChance;
List currentGen;
GeneticAlgorithmSolver(
{this.breakfasts,
this.lunches,
this.dinners,
this.proteinGoal,
this.carbGoal,
this.fatGoal,
this.generationCount,
this.populationCount,
this.mutationChance})
: assert(breakfasts.length > 0),
assert(lunches.length > 0),
assert(dinners.length > 0);
List bestSolution;
double bestSolutionFitness = 0.0;
//This function generates the first generation, and then calls nextGen() as often as generationCount.
//At the end it returns bestSolution, which is changed throughout the algorithm within the nextGen() function.
List solve() {
currentGen = generateFirstGen(
breakfasts: breakfasts, lunches: lunches, dinners: dinners);
print("population size: " + currentGen.length.toString());
for (int i = 0; i < generationCount; i++) {
print("Starting generation: " + i.toString());
currentGen = nextGen(currentGen);
if (generationCount - i == 1) {
print(bestSolution[0].toJson());
}
}
print(bestSolutionFitness);
print(bestSolution[0].toJson());
return bestSolution;
}
//This function returns as many random solutions as populationCount, therefore generating the first generation.
//I'm pretty sure that the bug is not in here.
List<List<Meal>> generateFirstGen(
{@required List breakfasts,
@required List lunches,
@required List dinners}) {
List<List<Meal>> gen = [];
for (int i = 0; i < populationCount; i++) {
List<Meal> list = [
breakfasts[(random.nextDouble() * (breakfasts.length - 1)).round()],
lunches[(random.nextDouble() * (lunches.length - 1)).round()],
dinners[(random.nextDouble() * (dinners.length - 1)).round()]
];
for (int y = 0; y < list.length; y++) {
for (int z = 0; z < list[y].foods.length; z++) {
list[y].setFactor(z, random.nextDouble());
}
}
gen.add(list);
}
return gen;
}
double calculateFitness(num current, num max) {
double fitness = (current / max).clamp(0, 2);
if (fitness > 1) {
return 2 - fitness;
}
assert(fitness > 0 && fitness <= 1);
return fitness;
}
//This function takes the current generation and sorts the results based on their fitness(how good they are)
//Then it merges results and returns a new generation.
List<List<Meal>> nextGen(List<List<Meal>> currentGen) {
List<List<Meal>> nextGen = [];
List<Map<String, dynamic>> fitnessMap = [];
currentGen.forEach((f) {
double protein = 0.0;
double fat = 0.0;
double carb = 0.0;
for (int i = 0; i < f.length; i++) {
protein += f[i].calculateProtein();
fat += f[i].calculateFat();
carb += f[i].calculateCarbs();
}
fitnessMap.add({
"day": f,
"fitness": (calculateFitness(protein, proteinGoal) +
calculateFitness(fat, fatGoal) +
calculateFitness(carb, carbGoal)) /
3
});
});
List sortedGen = [];
//Simple Bubblesort to sort the results based on their fitness.
bool sorted = false;
for (int i = 0; i < fitnessMap.length && !sorted; i++) {
sorted = true;
for (int y = 0; y < fitnessMap.length - i - 1; y++) {
if (fitnessMap[y]["fitness"] > fitnessMap[y + 1]["fitness"]) {
sorted = false;
Map temp = fitnessMap[y];
fitnessMap[y] = fitnessMap[y + 1];
fitnessMap[y + 1] = temp;
}
}
}
print(List.generate(fitnessMap.length, (i) => fitnessMap[i]["fitness"]));
//Since fitnessMap is sorted, the last item should be the best(the one with the highest fitness)
Map bestResult = fitnessMap[fitnessMap.length - 1];
//If then the current best result is better than the overall best result, the current best becomes the new overall best.
if (bestResult["fitness"] > bestSolutionFitness) {
print("=======NEW BEST======= : " + bestResult["fitness"].toString());
print(List.generate(
bestResult["day"].length, (i) => bestResult["day"][i].toJson()));
double protein = 0.0;
double fat = 0.0;
double carb = 0.0;
for (int i = 0; i < bestResult["day"].length; i++) {
protein += bestResult["day"][i].calculateProtein();
fat += bestResult["day"][i].calculateFat();
carb += bestResult["day"][i].calculateCarbs();
}
var calcut = (calculateFitness(protein, proteinGoal) +
calculateFitness(fat, fatGoal) +
calculateFitness(carb, carbGoal)) /
3;
print("=======Calculat======= : " + calcut.toString());
bestSolution = bestResult["day"];
print(bestSolution[0].toJson());
print(bestResult["day"][0].toJson());
bestSolutionFitness = bestResult["fitness"];
}
sortedGen = List.generate(fitnessMap.length, (i) => fitnessMap[i]["day"]);
//Here is the merging process, it's very messy and the bug is not here for sure.
for (int i = 0; i < populationCount; i++) {
List child;
List firstParent = sortedGen[min(
(sqrt(random.nextDouble()) * (sortedGen.length - 1)).round(),
sortedGen.length - 1)];
child = firstParent;
int firstIndex = random.nextInt(child.length);
int secondIndex = random.nextInt(child.length);
List secondParent = sortedGen[min(
(sqrt(random.nextDouble()) * (sortedGen.length - 1)).round(),
sortedGen.length - 1)];
for (int i = min(firstIndex, secondIndex);
i < max(firstIndex, secondIndex);
i++) {
child[i] = secondParent[i];
}
for (int i = 0; i < child.length; i++) {
if (random.nextDouble() <= mutationChance) {
switch (i) {
case 0:
child[i] = breakfasts[
(random.nextDouble() * (breakfasts.length - 1)).round()];
break;
case 1:
child[i] =
lunches[(random.nextDouble() * (lunches.length - 1)).round()];
break;
case 2:
child[i] =
dinners[(random.nextDouble() * (dinners.length - 1)).round()];
break;
}
}
}
for (int i = 0; i < child.length; i++) {
for (int y = 0; y < child[i].getItemCount; y++) {
if (random.nextDouble() <= mutationChance) {
child[i].setFactor(y, random.nextDouble());
}
}
}
nextGen.add(child);
}
return nextGen;
}
}
这是一个示例日志输出,我将代数减少到 20,因此 StackOverflow 不会抱怨垃圾邮件:
I/flutter (21224): Starting generation: 0
I/flutter (21224): [0.5313978709043187, 0.5313978709043187, 0.5313978709043187, 0.5313978709043187, 0.5313978709043187, 0.7299423655395717, 0.7299423655395717, 0.7299423655395717, 0.7374132001763957, 0.8297048120160494]
I/flutter (21224): =======NEW BEST======= : 0.8297048120160494
I/flutter (21224): [{name: Haferflocken mit Milch, ingredients: [{food: {name: Haferflocken, fat: 7.1, saturatedFat: 1.5, carb: 56.0, sugar: 1.1, fiber: 9.7, protein: 11.0, sodium: 0.02, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: grain, barcode: 4019339242018}, amount: 150.0, scalability: 100.0, factor: 0.819958805652279}, {food: {name: Vollmilch, fat: 4.0, saturatedFat: 2.6, carb: 4.9, sugar: 4.9, fiber: null, protein: 3.3, sodium: 0.11, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: dairy, barcode: 4101530001157}, amount: 400.0, scalability: 200.0, factor: 0.2969825022643108}]}, {name: Reis mit Hänchen, ingredients: [{food: {name: Naturreis, fat: 2.2, saturatedFat: 0.6, carb: 74.0, sugar: 0.7, fiber: 2.2, protein: 7.8, sodium: 0.01, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: vegetables, barcode: 4019339212189}, amount: 100.0, scalability: 75.0, factor: 0.69
I/flutter (21224): =======Calculat======= : 0.8297048120160494
I/flutter (21224): {name: Haferflocken mit Milch, ingredients: [{food: {name: Haferflocken, fat: 7.1, saturatedFat: 1.5, carb: 56.0, sugar: 1.1, fiber: 9.7, protein: 11.0, sodium: 0.02, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: grain, barcode: 4019339242018}, amount: 150.0, scalability: 100.0, factor: 0.819958805652279}, {food: {name: Vollmilch, fat: 4.0, saturatedFat: 2.6, carb: 4.9, sugar: 4.9, fiber: null, protein: 3.3, sodium: 0.11, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: dairy, barcode: 4101530001157}, amount: 400.0, scalability: 200.0, factor: 0.2969825022643108}]}
I/flutter (21224): {name: Haferflocken mit Milch, ingredients: [{food: {name: Haferflocken, fat: 7.1, saturatedFat: 1.5, carb: 56.0, sugar: 1.1, fiber: 9.7, protein: 11.0, sodium: 0.02, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: grain, barcode: 4019339242018}, amount: 150.0, scalability: 100.0, factor: 0.819958805652279}, {food: {name: Vollmilch, fat: 4.0, saturatedFat: 2.6, carb: 4.9, sugar: 4.9, fiber: null, protein: 3.3, sodium: 0.11, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: dairy, barcode: 4101530001157}, amount: 400.0, scalability: 200.0, factor: 0.2969825022643108}]}
I/flutter (21224): Starting generation: 1
I/flutter (21224): [0.5283462335935579, 0.5283462335935579, 0.5283462335935579, 0.5283462335935579, 0.5283462335935579, 0.5283462335935579, 0.5283462335935579, 0.7075198382526259, 0.7075198382526259, 0.7075198382526259]
I/flutter (21224): Starting generation: 2
I/flutter (21224): [0.5283462335935579, 0.5283462335935579, 0.5283462335935579, 0.5283462335935579, 0.5283462335935579, 0.5283462335935579, 0.737783580588736, 0.737783580588736, 0.737783580588736, 0.737783580588736]
I/flutter (21224): Starting generation: 3
I/flutter (21224): [0.738428332595959, 0.738428332595959, 0.738428332595959, 0.738428332595959, 0.738428332595959, 0.738428332595959, 0.738428332595959, 0.738428332595959, 0.738428332595959, 0.738428332595959]
I/flutter (21224): Starting generation: 4
I/flutter (21224): [0.7258426264768459, 0.7258426264768459, 0.7258426264768459, 0.7258426264768459, 0.7258426264768459, 0.7258426264768459, 0.7258426264768459, 0.7258426264768459, 0.7258426264768459, 0.7258426264768459]
I/flutter (21224): Starting generation: 5
I/flutter (21224): [0.5589823816277794, 0.5589823816277794, 0.5589823816277794, 0.5589823816277794, 0.5589823816277794, 0.5589823816277794, 0.5589823816277794, 0.5589823816277794, 0.5589823816277794, 0.5589823816277794]
I/flutter (21224): Starting generation: 6
I/flutter (21224): [0.5574443375428283, 0.5574443375428283, 0.5574443375428283, 0.5574443375428283, 0.5574443375428283, 0.5574443375428283, 0.5574443375428283, 0.5574443375428283, 0.5574443375428283, 0.5574443375428283]
I/flutter (21224): Starting generation: 7
I/flutter (21224): [0.48318974082968635, 0.48318974082968635, 0.48318974082968635, 0.48318974082968635, 0.48318974082968635, 0.48318974082968635, 0.48318974082968635, 0.48318974082968635, 0.48318974082968635, 0.48318974082968635]
I/flutter (21224): Starting generation: 8
I/flutter (21224): [0.4932906839506798, 0.4932906839506798, 0.4932906839506798, 0.4932906839506798, 0.4932906839506798, 0.4932906839506798, 0.4932906839506798, 0.4932906839506798, 0.4932906839506798, 0.4932906839506798]
I/flutter (21224): Starting generation: 9
I/flutter (21224): [0.4807063467379004, 0.4807063467379004, 0.4807063467379004, 0.8154292293208805, 0.8154292293208805, 0.8154292293208805, 0.8154292293208805, 0.8154292293208805, 0.8154292293208805, 0.8154292293208805]
I/flutter (21224): Starting generation: 10
I/flutter (21224): [0.4718540151810169, 0.4718540151810169, 0.4718540151810169, 0.4718540151810169, 0.4718540151810169, 0.4718540151810169, 0.4718540151810169, 0.4718540151810169, 0.4718540151810169, 0.4718540151810169]
I/flutter (21224): Starting generation: 11
I/flutter (21224): [0.5314764343948387, 0.5314764343948387, 0.5314764343948387, 0.5314764343948387, 0.5314764343948387, 0.5314764343948387, 0.5314764343948387, 0.5314764343948387, 0.5314764343948387, 0.5314764343948387]
I/flutter (21224): Starting generation: 12
I/flutter (21224): [0.5256306490767777, 0.5256306490767777, 0.5256306490767777, 0.5256306490767777, 0.5256306490767777, 0.5256306490767777, 0.5256306490767777, 0.5256306490767777, 0.5256306490767777, 0.5256306490767777]
I/flutter (21224): Starting generation: 13
I/flutter (21224): [0.48081998808270626, 0.48081998808270626, 0.48081998808270626, 0.48081998808270626, 0.48081998808270626, 0.48081998808270626, 0.48081998808270626, 0.48081998808270626, 0.48081998808270626, 0.48081998808270626]
I/flutter (21224): Starting generation: 14
I/flutter (21224): [0.46594810317838503, 0.46594810317838503, 0.46594810317838503, 0.46594810317838503, 0.46594810317838503, 0.46594810317838503, 0.46594810317838503, 0.46594810317838503, 0.46594810317838503, 0.46594810317838503]
I/flutter (21224): Starting generation: 15
I/flutter (21224): [0.6831840326295633, 0.6831840326295633, 0.6831840326295633, 0.6831840326295633, 0.6831840326295633, 0.6831840326295633, 0.6831840326295633, 0.6831840326295633, 0.6831840326295633, 0.6831840326295633]
I/flutter (21224): Starting generation: 16
I/flutter (21224): [0.6642059362880226, 0.6642059362880226, 0.6642059362880226, 0.6642059362880226, 0.6642059362880226, 0.6642059362880226, 0.6642059362880226, 0.6642059362880226, 0.6642059362880226, 0.6642059362880226]
I/flutter (21224): Starting generation: 17
I/flutter (21224): [0.4404899494909354, 0.4404899494909354, 0.4404899494909354, 0.4404899494909354, 0.4404899494909354, 0.4404899494909354, 0.4404899494909354, 0.4404899494909354, 0.4404899494909354, 0.4404899494909354]
I/flutter (21224): Starting generation: 18
I/flutter (21224): [0.6809769097776095, 0.6809769097776095, 0.6809769097776095, 0.6809769097776095, 0.6809769097776095, 0.6809769097776095, 0.6809769097776095, 0.6809769097776095, 0.6809769097776095, 0.6809769097776095]
I/flutter (21224): Starting generation: 19
I/flutter (21224): [0.6818085009500662, 0.6818085009500662, 0.6818085009500662, 0.6818085009500662, 0.6818085009500662, 0.6818085009500662, 0.6818085009500662, 0.6818085009500662, 0.6818085009500662, 0.6818085009500662]
I/flutter (21224): {name: Haferflocken mit Milch, ingredients: [{food: {name: Haferflocken, fat: 7.1, saturatedFat: 1.5, carb: 56.0, sugar: 1.1, fiber: 9.7, protein: 11.0, sodium: 0.02, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: grain, barcode: 4019339242018}, amount: 150.0, scalability: 100.0, factor: 0.3877294684577154}, {food: {name: Vollmilch, fat: 4.0, saturatedFat: 2.6, carb: 4.9, sugar: 4.9, fiber: null, protein: 3.3, sodium: 0.11, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: dairy, barcode: 4101530001157}, amount: 400.0, scalability: 200.0, factor: 0.7549218291162099}]}
I/flutter (21224): 0.8297048120160494
I/flutter (21224): {name: Haferflocken mit Milch, ingredients: [{food: {name: Haferflocken, fat: 7.1, saturatedFat: 1.5, carb: 56.0, sugar: 1.1, fiber: 9.7, protein: 11.0, sodium: 0.02, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: grain, barcode: 4019339242018}, amount: 150.0, scalability: 100.0, factor: 0.3877294684577154}, {food: {name: Vollmilch, fat: 4.0, saturatedFat: 2.6, carb: 4.9, sugar: 4.9, fiber: null, protein: 3.3, sodium: 0.11, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: dairy, barcode: 4101530001157}, amount: 400.0, scalability: 200.0, factor: 0.7549218291162099}]}
I/flutter (21224): {name: Haferflocken mit Milch, ingredients: [{food: {name: Haferflocken, fat: 7.1, saturatedFat: 1.5, carb: 56.0, sugar: 1.1, fiber: 9.7, protein: 11.0, sodium: 0.02, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: grain, barcode: 4019339242018}, amount: 150.0, scalability: 100.0, factor: 0.3877294684577154}, {food: {name: Vollmilch, fat: 4.0, saturatedFat: 2.6, carb: 4.9, sugar: 4.9, fiber: null, protein: 3.3, sodium: 0.11, vitaminA: null, vitaminC: null, vitaminD: null, vitaminE: null, iron: null, calcium: null, foodtype: dairy, barcode: 4101530001157}, amount: 400.0, scalability: 200.0, factor: 0.7549218291162099}]}
正如您所看到的,每次算法都会打印=======NEW BEST======= : //new best score
,然后是新的最佳解决方案的 JSON。最后的 JSON 文本是函数solve()
返回的内容。如您所见,结尾的 JSON 文本不同于任何以前打印的 JSON。
提前感谢任何人甚至阅读本文并试图帮助我!
解决方案
您需要进行复制Meals
,List<Meals>
而不是在您的变异过程中对它们进行变异。注意你是怎么做的
List child;
List firstParent = sortedGen[min(
(sqrt(random.nextDouble()) * (sortedGen.length - 1)).round(),
sortedGen.length - 1)];
child = firstParent;
int firstIndex = random.nextInt(child.length);
int secondIndex = random.nextInt(child.length);
List secondParent = sortedGen[min(
(sqrt(random.nextDouble()) * (sortedGen.length - 1)).round(),
sortedGen.length - 1)];
for (int i = min(firstIndex, secondIndex);
i < max(firstIndex, secondIndex);
i++) {
child[i] = secondParent[i];
}
然后你做:
for (int i = 0; i < child.length; i++) {
for (int y = 0; y < child[i].getItemCount; y++) {
if (random.nextDouble() <= mutationChance) {
child[i].setFactor(y, random.nextDouble());
}
}
}
在这种情况下,您只是在变异组成父母的饭菜。相反,您需要进行深层复制。
推荐阅读
- scala - Akka 流 actor-conflation-ratelimit-actor 丢弃前几条消息(有时)
- java - 在邮递员中发送有效负载时,有没有办法从另一个表的主键中插入外键值的值?
- android - 为什么 Job 服务的 onStartJob 方法是每秒调用一次?
- python - 如果数据框中的大多数列相等,则 Pandas 设置值
- javascript - 有没有办法从另一个孩子调用子组件的函数?
- maven - Maven不更新依赖
- javascript - 渲染并保存 300dpi 图像
- spring-boot - 如何将 spring.freemarker.template-loader-path 指向依赖项 jar 中的模板?
- json - 如何通过多级分组从 SQLite 中获取嵌套的 JSON 数据?
- html - 如何在下拉列表中设置选项的宽度?