実現したいこと
遺伝的アルゴリズムにて5つの制約条件を必ず守るスケジュールの自動作成を行いたい.
- ▲▲機能を動作するようにする
前提
作成するシフトは14人のスタッフ10日分です.
割り当てられるシフトは以下の通りです.
SHIFTS = ['N', 'NS', 'NB', 'D', 'I', 'G', 'Z', 'FN']
発生している問題・エラーメッセージ
満たすべき制約条件5つを必ず守るようにすると計算時間が多いせいかなかなか実行が終了しません.
交叉と突然変異の箇所がおかしいのでしょうか.
該当のソースコード
python
1import random 2 3# スケジュールの制約条件を定義 4REQUIRED_STAFF_COUNT = 7 5FN_STAFF_COUNT_MIN = 1 6FN_STAFF_COUNT_MAX = 2 7NS_STAFF_COUNT_MIN = 1 8NS_STAFF_COUNT_MAX = 2 9 10# シフトの候補 11SHIFTS = ['N', 'NS', 'NB', 'D', 'I', 'G', 'Z', 'FN'] 12 13# 遺伝的アルゴリズムのパラメータ 14POPULATION_SIZE = 100 15GENERATIONS = 10000 16MUTATION_RATE = 0.2 17 18 19class Schedule: 20 def __init__(self, genes=None): 21 if genes is None: 22 self.genes = [random.choice(SHIFTS) for _ in range(10 * 14)] 23 24 else: 25 self.genes = genes 26 27 def calculate_fitness(self): 28 # 適応度関数を定義する 29 # 制約条件を元にスケジュールの優れた程度を評価する 30 fitness = 0 31 32 # 1. 制約条件1: 毎日,z以外のシフトが割り当てられるスタッフが7名以上いるか 33 staff_counts = {shift: 0 for shift in SHIFTS} 34 for i in range(14): 35 staff_shifts = self.genes[i * 10: (i + 1) * 10] 36 for shift in staff_shifts: 37 staff_counts[shift] += 1 38 39 if staff_counts['Z'] >= REQUIRED_STAFF_COUNT: 40 print("ok_1") 41 fitness += 1 42 print("fitness" + str(fitness)) 43 else: 44 fitness = 0 45 46 # 2. 制約条件2: 7の倍数の日にちから連続して3日間FNが割り当てられるスタッフが1人以上2人以下か 47 fn_days = [i for i in range(6, 10 * 14, 7)] # 7の倍数の日にち 48 fn_staff_count = sum(1 for i in fn_days if self.genes[i:i + 3] == ['FN', 'FN', 'FN']) 49 if FN_STAFF_COUNT_MIN <= fn_staff_count <= FN_STAFF_COUNT_MAX: 50 print("ok_2") 51 fitness += 1 52 print("fitness_2" + str(fitness)) 53 else: 54 fitness = 0 55 print("fitness_2" + str(fitness)) 56 57 # 5. 制約条件5: Nは6の倍数の日にちに割り当てられなければならない 58 n_days = [i for i in range(0, 10 * 14, 6)] # 6の倍数の日にち 59 for i in n_days: 60 if self.genes[i] != 'N': 61 break 62 else: 63 64 print("ok_5") 65 fitness += 1 66 67 68 69 70 71 # 3. 制約条件3: 7の倍数の日にちから連続して2日間NSが割り当てられるスタッフが1人以上2人以下か 72 ns_days = [i for i in range(6, 10 * 14, 7)] # 7の倍数の日にち 73 ns_staff_count = sum(1 for i in ns_days if self.genes[i:i + 2] == ['NS', 'NS']) 74 if NS_STAFF_COUNT_MIN <= ns_staff_count <= NS_STAFF_COUNT_MAX: 75 print("ok_3") 76 fitness += 1 77 78 # 4. 制約条件4: Nが割り当てられたシフトの次の日は必ずzでなければならない 79 for i in range(len(self.genes) - 1): 80 if self.genes[i] == 'N' and self.genes[i + 1] != 'Z': 81 break 82 else: 83 print("ok_4") 84 fitness += 1 85 86 87 return fitness 88 89 90 def crossover(self, other): 91 # 交叉の実装 92 # 2つのスケジュールを組み合わせて新しいスケジュールを生成する 93 crossover_point = random.randint(1, len(self.genes) - 1) 94 child1_genes = self.genes[:crossover_point] + other.genes[crossover_point:] 95 child2_genes = other.genes[:crossover_point] + self.genes[crossover_point:] 96 print(child1_genes) 97 return child1_genes, child2_genes 98 99 100 def mutate(self): 101 # 突然変異の実装 102 # スケジュールの一部を変更する 103 for i in range(len(self.genes)): 104 if random.random() < MUTATION_RATE: 105 self.genes[i] = random.choice(SHIFTS) 106 107 108 109if __name__ == "__main__": 110 # 初期のランダムなスケジュールを生成 111 best_schedule = Schedule() 112 best_fitness = best_schedule.calculate_fitness() 113 print("Initial Best Fitness:", best_fitness) 114 115 generation = 0 116 while best_fitness < 5: 117 generation += 1 118 119 # 新しい世代のスケジュールを生成 120 new_schedule = Schedule() 121 122 # 交叉を行う別のスケジュールを生成 123 other_schedule = Schedule() 124 125 # 交叉を実行して新しい子スケジュールを取得 126 child1_genes, child2_genes = new_schedule.crossover(other_schedule) 127 child1 = Schedule(child1_genes) 128 child2 = Schedule(child2_genes) 129 130 # 突然変異を実行 131 child1.mutate() 132 child2.mutate() 133 134 # 新しいスケジュールの適応度を計算 135 fitness_child1 = child1.calculate_fitness() 136 fitness_child2 = child2.calculate_fitness() 137 138 # 子スケジュールのうち適応度の高い方を選択 139 if fitness_child1 > fitness_child2: 140 new_fitness = fitness_child1 141 new_schedule = child1 142 else: 143 new_fitness = fitness_child2 144 new_schedule = child2 145 146 # 新しいスケジュールがより優れていれば更新 147 if new_fitness > best_fitness: 148 best_schedule = new_schedule 149 best_fitness = new_fitness 150 print(f"Generation {generation}, Best Fitness: {best_fitness}") 151 152 # 最良のスケジュールを表示 153 print("\nFinal Best Schedule:") 154 for i in range(14): 155 staff_shifts = best_schedule.genes[i * 10: (i + 1) * 10] 156 print(f"Staff {i + 1} shifts: {' '.join(staff_shifts)}") 157
補足情報(FW/ツールのバージョンなど)
google colabで作成しています.
コードを見る限りPOPULATION_SIZEが利用されておらず
全体的に遺伝的アルゴリズムの流れにのっとっていないと思うのですが
いかがお考えでしょうか?
回答3件
あなたの回答
tips
プレビュー