記事ディレクトリ
1. ファジージョブショップ
1.1 ファジーな数
談話領域 X 上のファジィ集合 A はメンバーシップ関数 u(x) で表され、u(x) は値 [0,1] を取ります。A が三角ファジィ数の場合、A は (a1, aM, a2) として表すことができます。A が四隅のファジー数の場合、A は (a1, a2, a4, a5) と表現できます。それぞれのファジィ数とメンバーシップ関数は次のとおりです。
不確実な条件下でのワークの加工時間と完了時間については、三角ファジィ数を用いて記述します。納期が不確実な場合は、台形のあいまいな数値を使用して記載します。たとえば、job1 の最初のオペレーション O11 の場合、マシン 1 でのファジー処理時間は三角形のファジー数値 (4、7、12) で表され、3 つの数値はそれぞれ最良、最も可能性の高い、最悪の処理時間を表します。ファジーな処理時間を持つ 4 ジョブと 2 マシンのフレキシブル ジョブ ショップ スケジューリング問題のデータ ケースは次のとおりです。括弧内は実際の処理時間であり、無視できます。
1.2 三角ファジィ数演算
【1】酒匂正・森徹(1999).あいまいな処理時間とあいまいな納期を伴うジョブショップのスケジューリング問題に対する効率的な遺伝的アルゴリズム。コンピュータと産業工学、36(2)、325-341。doi:https://doi.org/10.1016/S0360-8352(99)00135-7
【2】酒匂正・久保田良(2000).遺伝的アルゴリズムによるファジーな処理時間とファジーな納期を使用した、多目的ジョブ ショップ スケジューリングのためのファジー プログラミング。欧州オペレーショナルリサーチジャーナル、120(2)、393-407。土井:https://doi.org/10.1016/S0377-2217(99)00094-6
ワークショップのスケジューリング問題を解決するには、処理開始時刻や処理完了時刻の計算など、処理時間の計算が必要になりますが、明確な数値演算と同様に、ファジー数値演算も非常に重要であり、主にファジー数値の合計や大規模な演算が含まれます。あいまいな数値の比較。
- 合計: 各操作のファジー完了時間を計算します [1]
- 大きく捉える: 各操作のファジー開始時間を計算する [1]
- 比較: ファジィ完了時間はファジィ処理時間の合計であるため、三角形のファジィ数になります. 最大ファジィ完了時間を最小限に抑えたい場合は、いくつかのファジィ完了時間の値を比較する必要があります[2]
- 例
2✖️2 個の JSP 質問があるとします。
ジョブ 1: マシン 1 (2、5、6)、マシン 2 (5、7、8)
ジョブ 2: マシン 2 (3、4、7)、マシン 1 (1、2) , 3)
Job1 の 2 番目の操作のファジー開始時間は (2, 5, 6)V(3, 4, 7)、つまり (3, 5, 7) となり、ファジー完了時間は (3, 5) になります。 , 7) +(5, 7, 8)=(8, 12, 15)
4 つの三角ファジィ数があるとします。 A1=(2,5,8)、A2=(3,4,9)、A3=(3) ,5 ,7),A4=(4,5,8)、比較原理に従って A4,A1,A3,A2 を取得できます。
1.3 ファジーガントチャート
各オペレーションの開始時刻と完了時刻は三角のファジー数値であるため、そのガント チャートも通常のガント チャートとは異なります。次の図に示すように、各オペレーションのファジー開始時刻は線の下にあり、ファジー完了は時間はラインの上にあり、各三角形は前述のメンバーシップ関数のイメージです。それがマシン上の最初の処理である場合、それは長方形で表されます。たとえば、O41 のファジー開始時間は (4,5,7) で、次のステップは M1 で処理されます。M1 の最速のファジー終了時間は O31 の (6,10,14) であるため、ファジーO42 の開始時刻は (6, 10,14) です。
2. FJSP+ファジィ処理時間+GA
2.1 GAアルゴリズムの設定
論文:【3】Lei、DM (2010)。ファジーな処理時間で柔軟なジョブ ショップ スケジューリングのための遺伝的アルゴリズム。国際生産研究ジャーナル、48(10)、2995-3013。土井:10.1080/00207540902814348
以前の紹介とは異なり、[3] は新しい max 演算演算子を提案しました。これはランク結果に従って直接決定され、2 つの三角ファジィ数 s、t の大きい方の値は s または t になります。
- エンコードとデコード (2 つの文字列)
2層のコーディングで表現され、1層は操作列、もう1層は選択マシンとなるが、第2層のマシン割り当てコードは各操作を順番に並べたマシンに相当し、何も持たないコーディングの最初の層に関係します。たとえば、作業 011 は最終処理であり、処理マシンは 1 です。
他の文献の 2 レベル符号化では、第 2 レベルのマシン符号化は、第 1 レベルの符号化における各演算の処理マシンに対応していますが、ここでは異なります。以下は、操作 023 最終処理などの別のエンコード方法で、処理マシンは M5 です。
適応度値は目的関数値(ファジー完了時間)です
- セレクト(トーナメントセレクト)
エリート戦略を使用します。つまり、集団の各世代で最も優れた個人を保持します。ルーレットホイールのアプローチは目標値が曖昧であるため適切ではないため、トーナメント選択が使用されます。置換では、母集団から 2 人の個体がランダムに選択され、より小さい適応度値を持つ個体が新しい母集団に入るように選択されます。
- クロス (TPX; GPX/GPPX)
母集団を 2 つの部分母集団 A と B に分割し、それぞれジョブ シーケンスとマシンの割り当てを表します。機械割付け部にはクロスは2点クロスオーバー(2点クロスオーバーTPX)を使用しています。ジョブ シーケンス部分では、一般化位置クロスオーバー (GPX) と一般化優先順位保存クロスオーバー (GPPX) の 2 種類のクロスオーバーが使用されます。 GPX: S=1231 など、parent1 から部分文字列 S を選択し、parent2 の S 内の各要素を検索します
。要素が順番に並んでいることに注意してください。たとえば、S の 1 はparent1 で 2 番目に出現するため、parent2 で 2 番目に出現する 1 を見つけます。S を削除すると、parent2 は 32414234 になります。S をparent2 に挿入すると、位置は親 1 の S の位置、つまり pos=4
GPPX: [1,2] の値を持つ文字列を設定します。親 1 から遺伝子が選択されている場合、サブテーブルは親 1(2) からの遺伝子の連続選択を表します。で、parent2 の対応する遺伝子が削除されます
- 突然変異(スワップ)
個人の場合、2 つの遺伝子がランダムに選択され、交換されます。
- アルゴリズム
N 個の個体からなる初期集団 P をランダムに生成する;
P に対してトーナメント選択を実行する;
P を 2 つの部分母集団 A と B に分割し、それぞれジョブ シーケンスとマシン割り当て部分を表す;
GPPX または GPX クロスオーバーと
母集団 A に対して突然変異を交換する; TPX クロスオーバーを実行する突然変異を交換する;
A と B をマージして新しい集団を生成し、適応度値を計算する;
最後まで繰り返す
2.2 Pythonコード
操作シーケンスは GPPX 交叉演算子を使用します。交叉率 = 0.8、突然変異率 = 0.1、母集団の数 = 100、反復回数 = 1000、2 番目のコーディング方法、以下は GA 部分、完全なコードです。https://github.com/bujibujibiuwang /solve-fuzzy-FJSP-with-GA
import random
from params import get_args
from Shop import Shop
from utils import *
class GA(object):
def __init__(self, args, shop):
self.shop = shop
self.cross_rate = args.cross_rate
self.mutate_rate = args.mutate_rate
self.pop_size = args.pop_size
self.elite_number = args.elite_number
self.chrom_size = self.shop.job_nb * self.shop.op_nb
self.pop = []
def encode(self):
init_pop = []
for _ in range(self.pop_size):
one_string = []
for _ in range(self.shop.op_nb):
one_string += list(np.random.permutation(self.shop.job_nb))
random.shuffle(one_string)
two_string = [random.randint(0, self.shop.machine_nb-1) for _ in range(self.chrom_size)]
individual = np.vstack([one_string, two_string])
init_pop.append(individual)
return np.array(init_pop)
def decode(self, pop1):
fuzzy_fitness = []
certain_fitness = []
for individual in pop1:
fuzzy_completion_time = self.shop.process_decode1(individual)
fuzzy_fitness.append(fuzzy_completion_time)
certain_fitness.append(value(fuzzy_completion_time))
return fuzzy_fitness, certain_fitness
def selection(self, pop2, fuzzy_fitness, certain_fitness):
"""
tournament selection + elite_strategy
"""
pop2 = pop2.tolist()
sorted_pop = sorted(pop2, key=lambda x: certain_fitness[pop2.index(x)], reverse=False)
new_pop = sorted_pop[:self.elite_number]
while len(new_pop) < self.pop_size:
index1, index2 = random.sample(list(range(10, self.pop_size)), 2)
if rank(fuzzy_fitness[index1], fuzzy_fitness[index2]) == fuzzy_fitness[index1]:
new_pop.append(pop[index2])
else:
new_pop.append(pop[index1])
return np.array(new_pop)
def crossover_machine(self, pop_machine):
"""
two point crossover (TPX)
"""
temp = pop_machine.copy().tolist()
new_pop = []
while len(temp) != 0:
parent1, parent2 = random.sample(temp, 2)
temp.remove(parent1)
temp.remove(parent2)
if random.random() < self.cross_rate:
pos1, pos2 = sorted(random.sample(list(range(self.chrom_size)), 2))
offspring1 = parent1[:pos1] + parent2[pos1:pos2] + parent1[pos2:]
offspring2 = parent2[:pos1] + parent1[pos1:pos2] + parent2[pos2:]
else:
offspring1 = parent1
offspring2 = parent2
new_pop.append(offspring1)
new_pop.append(offspring2)
return np.array(new_pop)
def crossover_job(self, pop_job):
"""
generalisation of the precedence preservative crossover (PPX)
"""
temp = pop_job.copy().tolist()
new_pop = []
for parent1 in temp:
if random.random() < self.cross_rate:
new_individual = []
parent2 = pop_job[random.randint(0, self.pop_size-1)].tolist()
string = random.choices([0, 1], k=self.chrom_size)
for choose in string:
if int(choose) == 0:
new_individual.append(parent1[0])
parent2.remove(parent1[0])
parent1 = parent1[1:]
else:
new_individual.append(parent2[0])
parent1.remove(parent2[0])
parent2 = parent2[1:]
new_pop.append(new_individual)
else:
new_pop.append(parent1)
return np.array(new_pop)
def mutation(self, part):
"""
swap
"""
for individual in part:
if random.random() < self.mutate_rate:
pos1, pos2 = random.sample(list(range(self.chrom_size)), 2)
individual[pos1], individual[pos2] = individual[pos2], individual[pos1]
return part
@staticmethod
def elite_strategy(pop3, fitness):
best_fitness = [np.inf, np.inf, np.inf]
best_individual = None
for k, individual in enumerate(pop3):
if rank(fitness[k], best_fitness) == best_fitness:
best_fitness = fitness[k]
best_individual = individual
return best_individual, best_fitness
2.3 試験結果
文献[3]のinstance1テストを使用し、文献[3]のinstance1は最適解[20, 31, 40]を見つけ、私は最適解[20, 31, 41]をテストします。
0:old best:[inf, inf, inf], now best:[47, 70, 89], now mean:99.9475
100:old best:[28, 41, 54], now best:[34, 42, 52], now mean:48.41
200:old best:[25, 38, 46], now best:[25, 38, 47], now mean:38.92
300:old best:[25, 36, 45], now best:[25, 36, 45], now mean:38.2425
400:old best:[21, 33, 44], now best:[21, 33, 44], now mean:35.2775
500:old best:[21, 31, 41], now best:[24, 31, 40], now mean:34.475
600:old best:[21, 31, 41], now best:[21, 31, 42], now mean:34.35
700:old best:[19, 31, 42], now best:[19, 31, 42], now mean:33.5275
800:old best:[20, 31, 41], now best:[19, 31, 42], now mean:32.6575
900:old best:[20, 31, 41], now best:[20, 31, 41], now mean:34.175
ファジィ ガント チャートは次のとおり、
反復曲線は次のとおりです。