技術共有

Python34 遺伝的アルゴリズム (GA)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. 開発経緯

遺伝的アルゴリズム (GA) は、自然選択と遺伝学にヒントを得た最適化アルゴリズムです。これは、自然システムにおける適応性を研究することを目的として、1970 年代にアメリカの学者ジョン ホランドによって最初に提案され、コンピューター サイエンスの最適化問題に適用されました。

主な開発の歴史
  • 1975年: ジョン・ホランドは、著書「自然および人工システムの適応」で遺伝的アルゴリズムの概念を初めて提案しました。

  • 1980年代:遺伝的アルゴリズムは関数最適化や組み合わせ最適化などの分野で使われ始め、徐々に注目を集めてきました。

  • 1990年代:コンピュータの計算能力の向上に伴い、遺伝的アルゴリズムは工学、経済学、生物学などの分野で広く使用されています。

  • 2000年代から現在まで:遺伝的アルゴリズムの理論と応用はさらに発展し、次のようなさまざまな改良されたアルゴリズムが登場しました。遗传编程(遺伝的プログラミング)、差分进化(差分進化)など

2. 数学的原理

遺伝的アルゴリズムは、自然選択と遺伝学に基づいたランダム検索手法であり、その基本的な考え方は、生物学的進化のプロセスをシミュレートすることです。選ぶクロスそして突然変異操作により、新しい世代の集団が継続的に生成され、それによって徐々に最適解に近づきます。

遺伝的アルゴリズムの基本的な手順
  1. 初期化: 初期集団をランダムに生成します。

  2. 選ぶ: フィットネス関数に基づいてより優れた個体を選択します。

  3. クロス:親個体の遺伝子の一部を交換して新しい個体を作ります。

  4. 突然変異:個体の一部の遺伝子をランダムに変更します。

  5. 反復する: 終了条件が満たされるまで、選択、交叉、突然変異のプロセスを繰り返します。

数学的記述

集団サイズを N、各個体の染色体長を L と仮定します。遺伝的アルゴリズムの数学的プロセスは次のとおりです。

  1. 初期化: 集団を生成します。ここで、 は染色体です。

  2. 体力計算:各個人の適応度値を計算します。

  3. 選ぶ: フィットネス値に基づいて個人を選択します。一般的に使用される方法には、ルーレット選択、トーナメント選択などが含まれます。

  4. クロス: いくつかの個体を選択して交叉操作を行い、新しい個体を生成します。

  5. 突然変異: いくつかの新しい個体に対して突然変異操作を実行し、突然変異した個体を生成します。

  6. 人口を更新する: 古い個体を新しい個体に置き換えて、新世代の個体群を形成します。

  7. 終了条件: あらかじめ設定した終了条件(反復回数や適応度閾値など)に達した場合に最適解を出力します。

3. 応用シナリオ

遺伝的アルゴリズムは、その適応性と堅牢性により、多くの分野で広く使用されています。

エンジニアリングの最適化
  • 構造の最適化:軽量かつ高強度な構造を設計。

  • パラメータの最適化: 最適なパフォーマンスが得られるようにシステム パラメータを調整します。

経済と金融
  • ポートフォリオの最適化: 収益を最大化するために投資資産を配分します。

  • 市場予測: 株価と市場動向を予測します。

バイオインフォマティクス
  • 遺伝子配列の比較: DNA 配列を比較および分析します。

  • タンパク質の構造予測: タンパク質の立体構造を予測します。

機械学習
  • ニューラルネットワークトレーニング: ニューラル ネットワークの重みと構造を最適化します。

  • 機能の選択: 分類または回帰に最も有益な特徴を選択します。

4. Python サンプルの視覚的な実装

写真

次のコード例は、巡回セールスマン問題を解決する遺伝的アルゴリズムを実装しています。旅行商问题(TSP) は古典的な組み合わせ最適化問題で、巡回セールスマンが指定された一連の都市の各都市を正確に 1 回訪問し、最終的に出発都市に戻るための最短経路を見つけること、つまり総移動距離を最小限に抑えることを目的としています。コスト。物流、生産スケジュール、その他の分野で広く使用されています。

まず、中国のすべての省都(省都を含む)を定義します。中国台湾省の首都、台北) とその座標データを使用し、哈夫曼公式2 点間の距離を計算して通過します遗传算法初期集団を作成し、再生産、突然変異を起こし、次の世代を生成し、パスを継続的に最適化して、最後に各世代の最短パスの距離と対応するパスを記録し、すべての反復で最短パスを見つけて地図上に視覚的に表示します。市内のすべての場所反復回数に応じてフィットネスが変化する最適なパスだけでなく、最終結果には、重慶からすべての都市を通る最短パスとその合計距離が表示されます。合計 2 つのトレーニングが実行されました。最初の反復数は 2000 で、実行時間は約 3 分でした。2 番目の反復数は 20000 に設定され、実行時間は約 15 分でした。

一言で要約すると、重慶から出発して中国のすべての省都を訪れ、遺伝的アルゴリズムを使用して最短経路を見つけます。コードの実装は次のとおりです。

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from mpl_toolkits.basemap import Basemap
  4. import random
  5. from math import radians, sin, cos, sqrt, atan2
  6. # 设置随机种子以保证结果可重复
  7. random.seed(42)
  8. np.random.seed(42)
  9. # 定义城市和坐标列表,包括台北
  10. cities = {
  11.     "北京": (39.9042116.4074),
  12.     "天津": (39.3434117.3616),
  13.     "石家庄": (38.0428114.5149),
  14.     "太原": (37.8706112.5489),
  15.     "呼和浩特": (40.8426111.7511),
  16.     "沈阳": (41.8057123.4315),
  17.     "长春": (43.8171125.3235),
  18.     "哈尔滨": (45.8038126.5349),
  19.     "上海": (31.2304121.4737),
  20.     "南京": (32.0603118.7969),
  21.     "杭州": (30.2741120.1551),
  22.     "合肥": (31.8206117.2272),
  23.     "福州": (26.0745119.2965),
  24.     "南昌": (28.6820115.8579),
  25.     "济南": (36.6512117.1201),
  26.     "郑州": (34.7466113.6254),
  27.     "武汉": (30.5928114.3055),
  28.     "长沙": (28.2282112.9388),
  29.     "广州": (23.1291113.2644),
  30.     "南宁": (22.8170108.3665),
  31.     "海口": (20.0174110.3492),
  32.     "重庆": (29.5638106.5507),
  33.     "成都": (30.5728104.0668),
  34.     "贵阳": (26.6477106.6302),
  35.     "昆明": (25.0460102.7097),
  36.     "拉萨": (29.652091.1721),
  37.     "西安": (34.3416108.9398),
  38.     "兰州": (36.0611103.8343),
  39.     "西宁": (36.6171101.7782),
  40.     "银川": (38.4872106.2309),
  41.     "乌鲁木齐": (43.825687.6168),
  42.     "台北": (25.032969121.565418)
  43. }
  44. # 城市列表和坐标
  45. city_names = list(cities.keys())
  46. locations = np.array(list(cities.values()))
  47. chongqing_index = city_names.index("重庆")
  48. # 使用哈夫曼公式计算两点间的距离
  49. def haversine(lat1, lon1, lat2, lon2):
  50.     R = 6371.0  # 地球半径,单位为公里
  51.     dlat = radians(lat2 - lat1)
  52.     dlon = radians(lon1 - lon2)
  53.     a = sin(dlat / 2)**2 + cos(radians(lat1)) * cos(radians(lat2)) * sin(dlon / 2)**2
  54.     c = 2 * atan2(sqrt(a), sqrt(1 - a))
  55.     distance = R * c
  56.     return distance
  57. # 计算路径总距离的函数,单位为公里
  58. def calculate_distance(path):
  59.     return sum(haversine(locations[path[i]][0], locations[path[i]][1], locations[path[i + 1]][0], locations[path[i + 1]][1]) for i in range(len(path) - 1))
  60. # 创建初始种群
  61. def create_initial_population(size, num_cities):
  62.     population = []
  63.     for _ in range(size):
  64.         individual = random.sample(range(num_cities), num_cities)
  65.         # 确保重庆为起点
  66.         individual.remove(chongqing_index)
  67.         individual.insert(0, chongqing_index)
  68.         population.append(individual)
  69.     return population
  70. # 对种群进行排名
  71. def rank_population(population):
  72.     # 按照路径总距离对种群进行排序
  73.     return sorted([(i, calculate_distance(individual)) for i, individual in enumerate(population)], key=lambda x: x[1])
  74. # 选择交配池
  75. def select_mating_pool(population, ranked_pop, elite_size):
  76.     # 选择排名前elite_size的个体作为交配池
  77.     return [population[ranked_pop[i][0]] for i in range(elite_size)]
  78. # 繁殖新个体
  79. def breed(parent1, parent2):
  80.     # 繁殖两个父母生成新个体
  81.     geneA = int(random.random() * (len(parent1) - 1)) + 1
  82.     geneB = int(random.random() * (len(parent1) - 1)) + 1
  83.     start_gene = min(geneA, geneB)
  84.     end_gene = max(geneA, geneB)
  85.     child = parent1[:start_gene] + parent2[start_gene:end_gene] + parent1[end_gene:]
  86.     child = list(dict.fromkeys(child))
  87.     missing = set(range(len(parent1))) - set(child)
  88.     for m in missing:
  89.         child.append(m)
  90.     # 确保重庆为起点
  91.     child.remove(chongqing_index)
  92.     child.insert(0, chongqing_index)
  93.     return child
  94. # 突变个体
  95. def mutate(individual, mutation_rate):
  96.     for swapped in range(1, len(individual) - 1):
  97.         if random.random() < mutation_rate:
  98.             swap_with = int(random.random() * (len(individual) - 1)) + 1
  99.             individual[swapped], individual[swap_with= individual[swap_with], individual[swapped]
  100.     return individual
  101. # 生成下一代
  102. def next_generation(current_gen, elite_size, mutation_rate):
  103.     ranked_pop = rank_population(current_gen)
  104.     mating_pool = select_mating_pool(current_gen, ranked_pop, elite_size)
  105.     children = []
  106.     length = len(mating_pool) - elite_size
  107.     pool = random.sample(mating_pool, len(mating_pool))
  108.     for i in range(elite_size):
  109.         children.append(mating_pool[i])
  110.     for i in range(length):
  111.         child = breed(pool[i], pool[len(mating_pool)-i-1])
  112.         children.append(child)
  113.     next_gen = [mutate(ind, mutation_rate) for ind in children]
  114.     return next_gen
  115. # 遗传算法主函数
  116. def genetic_algorithm(population, pop_size, elite_size, mutation_rate, generations):
  117.     pop = create_initial_population(pop_size, len(population))
  118.     progress = [(0, rank_population(pop)[0][1], pop[0])]  # 记录每代的最短距离和代数
  119.     for i in range(generations):
  120.         pop = next_generation(pop, elite_size, mutation_rate)
  121.         best_route_index = rank_population(pop)[0][0]
  122.         best_distance = rank_population(pop)[0][1]
  123.         progress.append((i + 1, best_distance, pop[best_route_index]))
  124.     best_route_index = rank_population(pop)[0][0]
  125.     best_route = pop[best_route_index]
  126.     return best_route, progress
  127. # 调整参数
  128. pop_size = 500       # 增加种群大小
  129. elite_size = 100     # 增加精英比例
  130. mutation_rate = 0.005 # 降低突变率
  131. generations = 20000   # 增加迭代次数
  132. # 运行遗传算法
  133. best_route, progress = genetic_algorithm(city_names, pop_size, elite_size, mutation_rate, generations)
  134. # 找到最短距离及其对应的代数
  135. min_distance = min(progress, key=lambda x: x[1])
  136. best_generation = min_distance[0]
  137. best_distance = min_distance[1]
  138. # 图1:地图上显示城市位置
  139. fig1, ax1 = plt.subplots(figsize=(1010))
  140. m1 = Basemap(projection='merc', llcrnrlat=18, urcrnrlat=50, llcrnrlon=80, urcrnrlon=135, resolution='l', ax=ax1)
  141. m1.drawcoastlines()
  142. m1.drawcountries()
  143. m1.fillcontinents(color='lightgray', lake_color='aqua')
  144. m1.drawmapboundary(fill_color='aqua')
  145. for i, (lat, lon) in enumerate(locations):
  146.     x, y = m1(lon, lat)
  147.     m1.plot(x, y, 'bo')
  148.     ax1.text(x, y, str(i), fontsize=8)
  149. ax1.set_title('City Locations')
  150. plt.show()
  151. # 图2:适应度随迭代次数变化
  152. fig2, ax2 = plt.subplots(figsize=(105))
  153. ax2.plot([x[1for x in progress])
  154. ax2.set_ylabel('Distance (km)')
  155. ax2.set_xlabel('Generation')
  156. ax2.set_title('Fitness over Iterations')
  157. # 在图中标注最短距离和对应代数
  158. ax2.annotate(f'Gen: {best_generation}nDist: {best_distance:.2f} km'
  159.              xy=(best_generation, best_distance), 
  160.              xytext=(best_generation, best_distance + 100),
  161.              arrowprops=dict(facecolor='red', shrink=0.05))
  162. plt.show()
  163. # 图3:显示最优路径
  164. fig3, ax3 = plt.subplots(figsize=(1010))
  165. m2 = Basemap(projection='merc', llcrnrlat=18, urcrnrlat=50, llcrnrlon=80, urcrnrlon=135, resolution='l', ax=ax3)
  166. m2.drawcoastlines()
  167. m2.drawcountries()
  168. m2.fillcontinents(color='lightgray', lake_color='aqua')
  169. m2.drawmapboundary(fill_color='aqua')
  170. # 绘制最优路径和有向箭头
  171. for i in range(len(best_route) - 1):
  172.     x1, y1 = m2(locations[best_route[i]][1], locations[best_route[i]][0])
  173.     x2, y2 = m2(locations[best_route[i + 1]][1], locations[best_route[i + 1]][0])
  174.     m2.plot([x1, x2], [y1, y2], color='g', linewidth=1, marker='o')
  175. # 只添加一个代表方向的箭头
  176. mid_index = len(best_route) // 2
  177. mid_x1, mid_y1 = m2(locations[best_route[mid_index]][1], locations[best_route[mid_index]][0])
  178. mid_x2, mid_y2 = m2(locations[best_route[mid_index + 1]][1], locations[best_route[mid_index + 1]][0])
  179. ax3.annotate('', xy=(mid_x2, mid_y2), xytext=(mid_x1, mid_y1), arrowprops=dict(facecolor='blue', shrink=0.05))
  180. # 在最优路径图上绘制城市位置
  181. for i, (lat, lon) in enumerate(locations):
  182.     x, y = m2(lon, lat)
  183.     m2.plot(x, y, 'bo')
  184.     ax3.text(x, y, str(i), fontsize=8)
  185. # 添加起点和终点标记
  186. start_x, start_y = m2(locations[best_route[0]][1], locations[best_route[0]][0])
  187. end_x, end_y = m2(locations[best_route[-1]][1], locations[best_route[-1]][0])
  188. ax3.plot(start_x, start_y, marker='^', color='red', markersize=15, label='Start (Chongqing)')  # 起点
  189. ax3.plot(end_x, end_y, marker='*', color='blue', markersize=15, label='End')   # 终点
  190. # 添加总距离的图例
  191. ax3.legend(title=f'Total Distance: {best_distance:.2f} km')
  192. ax3.set_title('Optimal Path')
  193. plt.show()

中国のすべての省都の位置の視覚化:

写真

反復数は 2,000 です (最短距離は 31594 キロメートル)。

過去の反復プロセスで最短パスの反復を見つけて、最短パスを視覚化します。

写真

写真

ケース 2: 反復回数は 20,000 です (取得された最短距離は 29,768 キロメートル)

過去の反復プロセスで最短パスを持つ反復を見つけて、その反復の最短パスを視覚化します。

写真

写真

上記の内容はインターネットからまとめたものです。お役に立ちましたら、また次回お楽しみください。