遺傳演算法 是一種啟發式最佳化演算法,模擬達爾文自然選擇與孟德爾遺傳定律,透過**選擇**、**交叉**、**突變** 迭代演化,在龐大搜尋空間中尋找近似最優解。適用於:函數最佳化、排程問題、路徑規劃、參數調參、神經網路架構搜尋等傳統演算法難以處理的 NP-Hard 問題。本文提供一個 **C# 完整物件導向實作架構**,包含:
Individual:個體 (染色體、基因、適應度、編碼/解碼、交叉、突變運算子多載)Population:族群 (初始化、適應度統計、選擇/交叉/突變委派策略、ICloneable/IDisposable)IGeneticStrategy:演化策略介面 (選擇、交叉、突變)IFitness:適應度計算介面 (可換裝不同問題域)Coder:編碼/解碼工具 (二進位字串 ↔ 實數)using System;
using System.Collections.Generic;
namespace GeneticAlgorithm.Core
{
///
/// 適應度計算策略介面:針對特定問題域實作
///
public interface IFitness
{
///
/// 計算適應度
///
/// 表現型數值 (解碼後的實數)
/// 適應度值 (越大越好)
double Calculate(double phenotype);
}
///
/// 演化策略介面:封裝選擇、交叉、突變演算法
///
public interface IGeneticStrategy
{
///
/// 選擇操作:根據適應度挑選下一代族群
///
Population Select(Population population);
///
/// 交叉操作:配對個體交換基因片段
///
void Crossover(Population population);
///
/// 突變操作:隨機翻轉基因位
///
void Mutation(Population population);
}
}
namespace GeneticAlgorithm.Core
{
///
/// 二進位字串與實數雙向轉換 (固定長度、定義域映射)
///
public static class Coder
{
///
/// 實數 → 二進位字串 (固定長度)
///
public static string ToBinary(double value, double min, double max, int geneLength)
{
double normalized = (value - min) / (max - min); // 0.0 ~ 1.0
normalized = Math.Max(0, Math.Min(1, normalized)); // 夾擠
int intVal = (int)(normalized * ((1 << geneLength) - 1));
return Convert.ToString(intVal, 2).PadLeft(geneLength, '0');
}
///
/// 二進位字串 → 實數 (映射回定義域)
///
public static double ToReal(string binary, double min, double max)
{
int intVal = Convert.ToInt32(binary, 2);
int maxInt = (1 << binary.Length) - 1;
double normalized = (double)intVal / maxInt;
return min + normalized * (max - min);
}
}
}
using System;
using System.Text;
namespace GeneticAlgorithm.Core
{
///
/// 個體:基因型 (二進位字串)、表現型 (實數)、適應度
///
public class Individual : ICloneable
{
private string _gene; // 基因型:二進位字串 (如 "101011...")
private double _fitness; // 適應度快取
private static IFitness _fitnessCalculator = new DefaultFitness(); // 預設適應度計算器
///
/// 靜態屬性:設定全域適應度計算策略
///
public static IFitness FitnessCalculator
{
get => _fitnessCalculator;
set => _fitnessCalculator = value ?? new DefaultFitness();
}
///
/// 基因型 (唯讀)
///
public string Gene => _gene;
///
/// 適應度 (唯讀,懶惰計算)
///
public double Fitness
{
get
{
if (double.IsNaN(_fitness))
_fitness = _fitnessCalculator.Calculate(Variable);
return _fitness;
}
}
///
/// 表現型:解碼後的實數值 (對應問題域變數)
///
public double Variable => Coder.ToReal(_gene, -1.0, 2.0); // 定義域 [-1, 2]
///
/// 建構函式:指定基因字串
///
public Individual(string gene)
{
_gene = gene ?? throw new ArgumentNullException(nameof(gene));
_fitness = double.NaN; // 標記未計算
}
///
/// 建構函式:指定基因與自訂適應度計算器
///
public Individual(string gene, IFitness fitnessCalculator)
{
_gene = gene ?? throw new ArgumentNullException(nameof(gene));
_fitnessCalculator = fitnessCalculator ?? new DefaultFitness();
_fitness = double.NaN;
}
///
/// 隨機產生個體 (指定基因長度)
///
public static Individual CreateRandom(int geneLength = 22)
{
var rd = new Random();
var sb = new StringBuilder(geneLength);
for (int i = 0; i < geneLength; i++)
sb.Append(rd.Next(0, 2));
return new Individual(sb.ToString());
}
#region 交叉運算子多載 (*)
///
/// 單點/兩點交叉:隨機選取交叉區間,交換基因片段
///
public static int operator *(Individual ind1, Individual ind2)
{
if (ind1._gene.Length != ind2._gene.Length)
throw new ArgumentException("基因長度必須相同");
var rd = new Random();
int len = ind1._gene.Length;
// 隨機選取起始與結束位置 (確保區間非空)
int start = rd.Next(0, len - 1);
int end = rd.Next(start + 1, len);
// 若該區段基因相同,重新選取 (避免無效交叉)
while (ind1._gene.Substring(start, end - start) == ind2._gene.Substring(start, end - start))
{
start = rd.Next(0, len - 1);
end = rd.Next(start + 1, len);
}
// 建構新基因
var sb1 = new StringBuilder(ind1._gene);
var sb2 = new StringBuilder(ind2._gene);
for (int i = start; i < end; i++)
{
char temp = sb1[i];
sb1[i] = sb2[i];
sb2[i] = temp;
}
ind1._gene = sb1.ToString();
ind2._gene = sb2.ToString();
ind1._fitness = double.NaN; // 使適應度失效,下次重新計算
ind2._fitness = double.NaN;
return end - start; // 回傳交叉片段長度
}
///
/// 實例方法版交叉
///
public int Crossover(Individual other) => this * other;
#endregion
#region 突變
///
/// 單點突變:隨機翻轉一個基因位 (0↔1)
///
public int Mutation()
{
var rd = new Random();
int pos = rd.Next(0, _gene.Length);
var sb = new StringBuilder(_gene);
sb[pos] = sb[pos] == '0' ? '1' : '0';
_gene = sb.ToString();
_fitness = double.NaN;
return pos;
}
#endregion
#region 輔助方法
///
/// 基因字串轉十進位整數 (除錯用)
///
public int ToMetrication() => Convert.ToInt32(_gene, 2);
public override string ToString() => _gene;
public override bool Equals(object obj) => obj is Individual other && _gene == other._gene;
public override int GetHashCode() => _gene.GetHashCode();
public object Clone() => new Individual(_gene);
#endregion
}
///
/// 預設適應度函數:f(x) = x * sin(10πx) + 2.0 (單峰/多峰測試函數)
///
public class DefaultFitness : IFitness
{
public double Calculate(double x)
{
return x * Math.Sin(10 * Math.PI * x) + 2.0;
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace GeneticAlgorithm.Core
{
///
/// 族群:個體集合、適應度統計、演化操作委派
///
public class Population : CollectionBase, ICloneable, IDisposable
{
private IGeneticStrategy _strategy;
public Population() { }
public Population(IGeneticStrategy strategy)
{
_strategy = strategy ?? throw new ArgumentNullException(nameof(strategy));
}
///
/// 演化策略 (可執行期熱插拔)
///
public IGeneticStrategy Strategy
{
get => _strategy;
set => _strategy = value ?? throw new ArgumentNullException(nameof(value));
}
///
/// 索引子存取個體
///
public Individual this[int index]
{
get => (Individual)List[index];
set => List[index] = value ?? throw new ArgumentNullException(nameof(value));
}
///
/// 新增個體
///
public void Add(Individual ind) => List.Add(ind ?? throw new ArgumentNullException(nameof(ind)));
///
/// 初始化族群:隨機產生指定數量個體
///
public void Initialize(int populationSize, int geneLength = 22)
{
for (int i = 0; i < populationSize; i++)
Add(Individual.CreateRandom(geneLength));
}
#region 適應度統計屬性
///
/// 最大適應度
///
public double MaxFitness
{
get
{
double max = double.MinValue;
foreach (Individual ind in List)
if (ind.Fitness > max) max = ind.Fitness;
return max;
}
}
///
/// 平均適應度
///
public double AverageFitness => SumFitness / Count;
///
/// 適應度總和
///
public double SumFitness
{
get
{
double sum = 0;
foreach (Individual ind in List)
sum += ind.Fitness;
return sum;
}
}
#endregion
#region 演化操作 (委派給 Strategy)
public Population Select() => _strategy?.Select(this);
public void Crossover() => _strategy?.Crossover(this);
public void Mutation() => _strategy?.Mutation(this);
#endregion
///
/// 列印族群資訊 (除錯用)
///
public void Print()
{
for (int i = 0; i < Count; i++)
{
var ind = (Individual)List[i];
Console.WriteLine($"個體[{i:D3}] 基因={ind.Gene} 表現型={ind.Variable:F6} 適應度={ind.Fitness:F6}");
}
}
#region ICloneable / IDisposable
public object Clone()
{
var clone = new Population(_strategy);
foreach (Individual ind in List)
clone.List.Add(ind.Clone());
return clone;
}
public void Dispose()
{
_strategy = null;
List.Clear();
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
namespace GeneticAlgorithm.Core
{
///
/// 簡單演化策略:輪盤選擇 + 單點交叉 + 單點突變
///
public class SimpleGeneticStrategy : IGeneticStrategy
{
private readonly Random _rd = new Random();
private readonly double _crossoverRate = 0.8;
private readonly double _mutationRate = 0.05;
public SimpleGeneticStrategy(double crossoverRate = 0.8, double mutationRate = 0.05)
{
_crossoverRate = crossoverRate;
_mutationRate = mutationRate;
}
///
/// 輪盤選擇:適應度比例選擇
///
public Population Select(Population population)
{
var newPop = new Population(this);
double totalFitness = population.SumFitness;
if (totalFitness <= 0) return population.Clone() as Population; // 避免除零
for (int i = 0; i < population.Count; i++)
{
double pick = _rd.NextDouble() * totalFitness;
double accumulated = 0;
foreach (Individual ind in population.List)
{
accumulated += ind.Fitness;
if (accumulated >= pick)
{
newPop.Add((Individual)ind.Clone());
break;
}
}
}
return newPop;
}
///
/// 隨機配對交叉
///
public void Crossover(Population population)
{
var individuals = new List();
foreach (Individual ind in population.List)
individuals.Add(ind);
// 隨機打亂配對順序
for (int i = individuals.Count - 1; i > 0; i--)
{
int j = _rd.Next(i + 1);
var temp = individuals[i];
individuals[i] = individuals[j];
individuals[j] = temp;
}
// 成對交叉
for (int i = 0; i < individuals.Count - 1; i += 2)
{
if (_rd.NextDouble() < _crossoverRate)
individuals[i] * individuals[i + 1]; // 使用運算子多載
}
// 更新族群
population.List.Clear();
foreach (var ind in individuals)
population.List.Add(ind);
}
///
/// 隨機突變
///
public void Mutation(Population population)
{
foreach (Individual ind in population.List)
{
if (_rd.NextDouble() < _mutationRate)
ind.Mutation();
}
}
}
}
using System;
using GeneticAlgorithm.Core;
namespace GeneticAlgorithm.Demo
{
class Program
{
static void Main()
{
Console.WriteLine("=== C# 遺傳演算法示範:尋找 f(x)=x*sin(10πx)+2 在 [-1,2] 最大值 ===");
// 1. 建立族群與策略
var strategy = new SimpleGeneticStrategy(crossoverRate: 0.8, mutationRate: 0.05);
var population = new Population(strategy);
population.Initialize(populationSize: 100, geneLength: 22);
// 2. 迭代演化
const int MAX_GENERATIONS = 200;
for (int gen = 0; gen < MAX_GENERATIONS; gen++)
{
// 選擇 → 交叉 → 突變
var selected = population.Select();
selected.Crossover();
selected.Mutation();
// 精英保留:保留前代最佳個體
var bestPrev = population[0];
for (int i = 1; i < population.Count; i++)
if (population[i].Fitness > bestPrev.Fitness) bestPrev = population[i];
selected[0] = (Individual)bestPrev.Clone(); // 取代第一個
population = selected; // 世代更替
// 監控輸出
if (gen % 20 == 0 || gen == MAX_GENERATIONS - 1)
{
var best = population[0];
for (int i = 1; i < population.Count; i++)
if (population[i].Fitness > best.Fitness) best = population[i];
Console.WriteLine($"第 {gen:D3} 代 | 最大適應度={best.Fitness:F6} | 平均={population.AverageFitness:F6} | 最佳x={best.Variable:F6}");
}
}
// 3. 輸出最終結果
var finalBest = population[0];
for (int i = 1; i < population.Count; i++)
if (population[i].Fitness > finalBest.Fitness) finalBest = population[i];
Console.WriteLine("\n=== 最終結果 ===");
Console.WriteLine($"最佳解 x = {finalBest.Variable:F6}");
Console.WriteLine($"最大值 f(x) = {finalBest.Fitness:F6}");
Console.WriteLine($"基因型 = {finalBest.Gene}");
Console.ReadKey();
}
}
}
| 設計模式 | 應用位置 | 效益 |
|---|---|---|
| Strategy | IGeneticStrategy / IFitness | 演化演算法與適應度函數可熱插拔,支援不同問題域 |
| Operator Overloading | Individual.operator * | 交叉操作語法直觀 (ind1 * ind2),符合領域語言 |
| Lazy Evaluation | Individual.Fitness 屬性 | 適應度僅在首次存取時計算,避免重複運算 |
| Prototype (ICloneable) | Individual / Population | 精英保留、族群複製無需序列化,高效能 |
| Static Factory | Individual.CreateRandom | 封裝隨機初始化邏輯,統一基因長度 |
| Separation of Concerns | Coder 類別 | 編碼/解碼邏輯與個體/族群解耦,易於擴充 Gray Code、Real-valued GA |