Bài toán vận tải áp dụng giải thuật di truyền

Một phần của tài liệu (LUẬN văn THẠC sĩ) kết hợp giải thuật di truyền và tìm kiếm tabu giải bài toán tối ưu (Trang 42 - 56)

- Mã hóa: Mỗi cá thể là một ma trận (m x n) chiều thể hiện số lượng hàng từ m địa điểm cung cấp đến n địa điểm tiêu thụ. Giá trị mỗi phần tử trong ma trận là một số nguyên không âm ngẫu nhiên thể hiện số lượng vận chuyển cụ thể giữa các địa điểm cung cấp và tiêu thụ.

- Tính độ thích nghi cho từng cá thể trong quần thể ban đầu:

Vì mỗi cá thể là một lời giải của bài toán nên chúng tôi tính độ thích nghi cho mỗi cá thể chính bằng tổng chi phí vận chuyển của cá thể đó.

Khởi tạo quần thể

Trong thuật giải di truyền đầu tiên các quần thể được khởi tạo với các cá thể ngẫu nhiên. Các cá thể được khởi tạo sao cho đảm bảo các rằng buộc của bài toán, đặc biệt là rằng buộc về hàng và cột.

Mỗi cá thể là một ma trận (m x n) chiều thể hiện số lượng hàng từ m địa điểm cung cấp đến n địa điểm tiêu thụ. Giá trị mỗi phần tử trong ma trận là một số nguyên không âm ngẫu nhiên thể hiện số lượng vận chuyển cụ thể giữa các địa điểm cung cấp và tiêu thụ.

Khởi tạo quần thể ban đầu gồm 100 cá thể. Vì mỗi cá thể là một ma trận cỡ (m x n) nên để khởi tạo được 100 cá thể ban đầu, chúng tôi phải lưu trữ 100 cá thể này vào mảng 3 chiều trong đó 2 chiều đầu thể hiện vận chuyển giữa các địa điểm cung cấp và tiêu thụ, chiều thứ 3 thể hiện thứ tự của cá thể trong 100 cá thể của quần thể

Một số cá thể được khởi tạo ngẫu nhiên:

5 18 92 55 11 35 102 22 125 7 6 62 114 0 6 80 0 135 22 23 5 125 12 38 89 22 23 36 17 59 87 7 0 13 92 95 58 82 0 60 41 125 5 9 55 19 33 73 34 99 19 18 98 41 5 26 28 8 65 99 1 7 109 83 68 53 36 23 31 112 6 31 3 1 80 86 0 0 75 95 61 83 9 47 125 52 19 4 66 76 31 7 5 108 26 41

Mục tiêu của bài toán vận tải ta cần đạt được là lập kế hoạch vận chuyển hàng từ các địa điểm cung cấp đến các địa điểm tiêu thụ sao cho tổng chi phí vận chuyển là nhỏ nhất và thỏa mãn nhu cầu thu phát.

f(x) = ∑ i=1 mj=1 n cijxij

→ min (cực tiểu tổng chi phí)

Toán tử lai ghép

Phép lai là phép tạo ra các cá thể con đảm bảo mang những đặc tính của cả cha lẫn mẹ. Trong bài toán vận tải phép lai được thực hiện dựa trên phương pháp sau: Mỗi lời giải là một ma trận vuông cỡ mxn, trong đó số cột tương ứng với số điểm tiêu thụ. Một cặp cha, mẹ khi tiến hành lai ghép với nhau sẽ cho ra 2 con lai. Trong đó, con lai thứ nhất được tạo ra bởi phương pháp chọn những cột chẵn của cha và cột lẻ của mẹ, con lai thứ hai được tạo ra bởi phương pháp chọn những cột lẻ của cha và cột chẵn cửa mẹ.

Phương pháp này tạo ra con lai mang cả hai đặc điểm của cả cha lẫn mẹ, tuy nhiên con lai không đảm bảo những rằng buộc của bài toán vận tải. Ở đây ta sử dụng phương pháp làm mịn sau lai ghép để tạo con lai thỏa rằng buộc của bài toán.

Phương pháp làm mịn được thực hiện dựa trên việc tìm các hàng không thỏa mãn rằng buộc (do các cột được lấy của cha và mẹ do đó đã thỏa mãn yêu cầu rằng buộc về cột), các hàng không thỏa mãn rằng buộc sẽ tìm hàng gần nhất để chuyển sang hoặc lấy đi một số lượng hàng hóa tối đa để tiến gần đề thỏa mãn rằng buộc. Nếu hàng gần nhất được chọn và chuyển còn chưa thỏa mãn thì quy trình này được thực hiện tiếp tục với các hàng gần nhất tiếp theo.

Quy trình tiếp tục đến khi các rằng buộc là thỏa mãn (vì tổng số hàng trong một lời giải là thỏa mãn bằng với tổng số hàng trong rằng buộc, vấn đề là ta chuyển các số liệu sao cho thỏa các rằng buộc hàng, do đó thuật toán làm mịn sẽ kết thúc với một lời giải thỏa các rằng buộc của bài toán).

Thuật toán cụ thể được thể hiện như sau:

public Gen(Gen bo, Gen me) // dung trong phep lai {

tuoi = Gen.Doi; danhDau = "lai";

this.dna = new int[cap, tieu]; int chon = ran.Next(2);

for (int i = 0; i < Gen.cap; ++i) for (int j = 0; j < Gen.tieu; ++j)

if (j%2==chon) dna[i, j] = bo.dna[i, j]; else dna[i, j] = me.dna[i, j];

// sua lai bo gen

int[] tg = new int[Gen.cap]; for (int i = 0; i < Gen.cap; ++i) {

int tong = 0;

for (int j = 0; j < Gen.tieu; ++j) tong += dna[i, j];

tg[i] = tong; }

for (int i = 0; i < Gen.cap; ++i) tg[i] = tg[i] - Gen.mangCap[i]; //---

for (int i = 0; i < Gen.cap; ++i) while (tg[i] < 0)

{

for (int j = 0; j < Gen.cap; ++j) if (tg[j] > 0) { tim = j; break; } if (tim >= 0)

{

int giaTriChuyen = Math.Abs (tg[i]) > tg[tim] ? tg[tim] : Math.Abs(tg[i]);

tg[i] += giaTriChuyen; tg[tim] -= giaTriChuyen; for (int k=0;k<Gen.tieu;++k) if (dna[tim, k] > 0)

{

int cc = dna[tim, k] > giaTriChuyen ? giaTriChuyen : dna[tim, k]; dna[tim, k] -= cc; dna[i, k] += cc; giaTriChuyen -= cc; } } } this.createEval(); }

Ví dụ 1: Cá thể bố Cá thể mẹ 75 41 32 22 36 33 60 41 19 41 56 84 74 17 12 97 36 78 32 34 20 110 48 2 Con 1 Con 2 64 33 32 41 47 41 60 22 30 17 56 97 63 41 12 84 36 110 32 2 20 78 48 34 Ví dụ 2: Cá thể bố Cá thể mẹ 53 9 22 86 21 10 43 96 42 71 60 27 4 133 27 36 35 80 38 27 105 17 50 8 Con 1 Con 2 32 9 43 86 42 10 22 96 75 71 27 27 0 104 60 36 23 80 50 27 88 46 38 8 Toán tử đột biến

Trong giải thuật di truyền toán tử đột biến là một trong những phần không thể thiếu của giải thuật. Toán tử này sinh ra một cá thể có gen mang tính khác biệt bằng cách thay đổi một hoặc một đoạn gen từ một cá thể khác.

được tạo mới và phát triển dựa trên hai toán tử cơ bản là lai ghép (như đã trình bày ở phía trên) và đột biến, trong đó cá thể mới về mặt mô tả là một ma trận hai chiều. Đối với lai ghép ma trận lời giải phải mang đặc điểm của hai lời giải cha và mẹ, đối với đột biến ma trận lời giải sinh ra chỉ cần mang phần lớn đặc điểm của phần tử trước khi đột biến.

Đối với bài toán vận tải ta thu được các lời giải ở dạng một ma trận hai chiều, để đột biến chúng ta chọn một điểm P(i, j) nằm trong ma trận gọi là điểm đột biến. Điểm này được sinh ra một cách ngẫu nhiên. Tại đó lời giải nhận một giá trị đột biến, và thay đổi lời giải theo hai hướng.

Một là tăng giá trị tại đó lên bằng một lượng bằng giá trị đột biến. Hai là giảm giá trị tại đó xuống một lượng bằng giá trị đột biến.

Lời giải mới sinh ra không thảo các điều kiện rằng buộc của bài toán, vì vậy ta phải thực hiện thêm một bước để lời giải này thảo điều kiện rằng buộc. Toàn bộ thuật toán sinh một cá thể đột biến từ một cá thể bình thường P được thực hiện như sau:

Bước 1: Chọn ngẫu nhiên một vị trí đột biến x, y (x, y là một điểm nằm trong ma trận lời giải)

Bước 2: Sinh một giá trị ∆ ngẫu nhiên (|∆|< ngưỡng cho trước, ngưỡng này được lựa chọn tùy theo giá trị rằng buộc về hàng và cột của bài toán)

Bước 3: Thực hiện phép toán: P(x,y) = P(x,y) + ∆

Bước 4: Lựa chọn một điểm x`, y` (x`≠ x , y`≠ y) thực hiện phép toán: P(x` , y) = P (x` ,y) - ∆

P(x , y`) = P (x ,y’) - ∆ P(x`, y`) = P(x`, y`) + ∆

Nếu lời giải sinh ra tồn tại số âm thì chọn một giá trị ∆` nhỏ nhất (∆ > 0) có thể và thực hiện lại phép toán trên. Đồng thời gán lại giá trị cho ∆: ∆ = ∆ - ∆`.

Bước 5: Kiểm tra lời giải đã thỏa các rằng buộc của bài toán chưa, nếu đã thỏa mãn thì kết thúc thuật toán ngược lại lặp lại bước 4 với một giá trị x`, y` khác x, y và các giá trị x`, y` đã làm tại các lần thực hiện thuật toán phía trước.

Thuật toán cụ thể được thể hiện như sau:

public Gen(Gen chu) // dung de dot bien {

tuoi = Gen.Doi;

danhDau = "dotbien";

this.dna = new int[cap, tieu]; for (int i = 0; i < Gen.cap; ++i) for (int j = 0; j < Gen.tieu; ++j) dna[i, j] = chu.dna[i, j]; // dot bien

int tang = Gen.giaTri; int dj = ran.Next(Gen.tieu); int di = ran.Next(Gen.cap);

if (dna[di, dj] >= tang && (dna[di,dj]+tang) <= Gen.mangTieu[dj]) {

int tta = ran.Next ();

if (tta%2==0) tang = -tang; //--- if (tang < 0)

{

bool ok = false; int vt = -1;

if (i ! = di && (dna[i, dj] -tang) <= Gen.mangTieu[dj]) { vt = i; ok = true; break; } if (ok) { dna[di,dj]+ = tang; dna [vt,dj]- = tang; } } else { bool ok = false; int vt = -1;

for (int i = 0; i < Gen.cap; ++i)

if (i ! = di && (dna[i, dj] - tang) >= 0) { vt = i; ok = true; break; } if (ok) { dna[di, dj] + = tang; dna[vt, dj] - = tang; } } }

// sua lai bo gen

int[] tg = new int[Gen.cap]; for (int i = 0; i < Gen.cap; ++i) {

int tong = 0;

for (int j = 0; j < Gen.tieu; ++j) tong += dna[i, j];

tg[i] = tong; }

for (int i = 0; i < Gen.cap; ++i) tg[i] = tg[i] - Gen.mangCap[i]; for (int i = 0; i < Gen.cap; ++i)

while (tg[i] < 0) {

// tim thang lon hon 0 gan nhat int tim = -1;

for (int j = 0; j < Gen.cap; ++j) if (tg[j] > 0) { tim = j; break; } if (tim >= 0)

{ int giaTriChuyen = Math.Abs(tg[i]) > tg[tim] ? tg[tim] : Math.Abs(tg[i]); tg[i]+ = giaTriChuyen;

tg[tim] - = giaTriChuyen;

for (int k = 0; k < Gen.tieu; ++k) if (dna[tim, k] > 0)

{ int cc = dna[tim, k] > giaTriChuyen ? giaTriChuyen : dna[tim, k]; dna[tim, k] - = cc; dna[i, k] + = cc; giaTriChuyen - = cc; } } } this.createEval();

Ví du:

Cá thể trước đột biến Cá thể sau đột biến

21 19 38 92 18 19 38 95

84 72 21 23 87 72 21 20

25 29 61 25 25 69 61 25

(∆ = 3 vị trí đột biến [0;3])

Cá thể trước đột biến Cá thể sau đột biến

2 23 34 111 0 25 34 111

72 24 76 28 74 22 76 28

56 113 10 1 56 113 10 1

(∆ = 3 vị trí đột biến [0;1])

Cá thể trước đột biến Cá thể sau đột biến

9 31 33 97 6 31 33 100

41 42 83 34 41 42 83 34

80 87 4 9 83 87 4 6

(∆ = -3 vị trí đột biến [2;3])

Toán tử chọn lọc:

Với bài toán vận tải này ta áp dụng toán tử chọn lọc xếp hạng. Các cá thể của quần thể hiện tại được xếp theo thứ tự giảm dần của độ thích nghi. Cá thể nào có độ thích nghi tốt nhất được xếp thứ nhất và cá thể tồi nhất xếp cuối cùng. Ta tính độ thích nghi cho từng cá thể, sau đó tính độ thích nghi cho tổng các cá thể trong quần thể. Tiếp theo ta tính xác xuất lựa chọn của mỗi cá thể dựa vào công thức: Pi = f(vi) /F

- F là tổng của các giá trị thích nghi của quần thể.

Sắp xếp Pi theo thứ tự giảm dần của độ thích nghi. Khi đó ta sẽ chọn các cá thể xếp trên sao cho đủ số lượng thì dừng, các cá thể phía dưới sẽ bị loại khỏi quá trình tiến hóa.

private void HamMucTieu()// hàm mục tiêu cho bài toán {

// tính lại độ thích nghi cho toàn bộ cá thể trong quần thể for (int i = 0; i < this.realSize; ++i)

quanThe[i].createEval();

//sử dụng phương pháp loại bỏ những cá thể giống nhau ra khỏi quần thể for (int i = 0; i < this.realSize - 1; ++i)

for (int j = i + 1; j < this.realSize; ++j)

if (quanThe[i].Giong(quanThe[j])) quanThe[j].eval = 0; // tính tổng độ thích nghi toàn quần thể ban đầu

double tong = 0;

for (int i = 0; i < this.realSize; ++i) tong += quanThe[i].eval;

if (tong == 0) tong = 0.1;

// thiết đặt sx sinh tồn cho từng cá thể trong quần thể for (int i = 0; i < this.realSize; ++i)

quanThe[i].xacSuatSong=quanThe[i].eval / tong; public void ChonLocTuNhien()

{

HamMucTieu();

double[] tg = new double[realSize]; for (int i = 0; i < realSize; ++i) tg[i] = -quanThe[i].eval; Array.Sort(tg, quanThe);

}

Quá trình tiến hóa như sau:

Hai cá thể cha mẹ được chọn (ngẫu nhiên) để tiến hành lai ghép tạo ra hai cá thể mới. Chúng tôi tiến hành lai ghép bằng cách chọn một cột ngẫu nhiên từ cha mẹ, sau đó con 1 nhận giá trị của cột tương ứng từ cha, con 2 nhận giá trị cột tương ứng từ me, sau đó các giá trị còn lại trong ma trận của hai con được sinh ngẫu nhiên thỏa mãn các điều kiện ràng buộc của bài toán.

Sau khi sinh ra được 2 con rồi, chúng tôi tiếp tục tính độ thích nghi cho mỗi con bằng cách tính tổng chi phí vận chuyển. Cá thể mới sinh ra cạnh tranh với cha mẹ chúng.

Nếu các con sinh ra mà có độ thích nghi tốt hơn độ thích nghi của cha mẹ chúng tức là có chi phí vận chuyển nhỏ hơn thì các con (hoặc một con) sẽ được chọn vào thế hệ tiếp theo,còn không thì bị loại bỏ. Tương tự như vậy, nếu bố mẹ (hoặc một trong hai bố mẹ) có độ thích nghi tốt hơn độ thích nghi của các con thì sẽ được giữ lại ở thế hệ tiếp theo, ngược lại thì bị đào thải. Như vậy, tại mỗi thế hệ số lượng cá thể luôn là 100.

Áp dụng giải thuật di truyền cho bài toán vận tải, với các tham số của giải thuật có thể lựa chọn trên chương trình cài đặt, tôi thu được các kế quả khác nhau cho mỗi lần chạy. Nguyên nhân của hiện tượng này là do giải thuật di truyền mang tính ngẫu nhiên cao khi mô phỏng tiến hóa tự nhiên.

Dưới đây là một số kế quả thu được khi chạy thử nghiệm chương trình với bộ tham số đầu vào của bài toán như đã trình bày ở phần trên.

Với tham số của giải thuật di truyền được lựa chọn như hình dưới: Tôi thử nghiệm với 100 cá thể; tham số lai là 0,9; tham số đột biến là 0,1; số lần tiến hóa là 100 lần.

Hình 3.1. Các tham số của giải thuật di truyền

Sau đây là kết quả tốt nhất của giải thuật di truyền trong 5 lần chạy thử nghiệm:

Lần 1:

28 29 113 0

102 0 0 98

0 131 7 42

Độ thích nghi (giá trị tiến hóa của cá thể = 12248) Lần 2:

39 30 101 0

91 0 0 109

0 130 19 31

Độ thích nghi (giá trị tiến hóa của cá thể = 12278) Lần 3:

76 0 0 124

0 132 32 16

Độ thích nghi (giá trị tiến hóa của cá thể = 12320) Lần 4:

51 20 99 0

79 0 0 121

0 140 21 19

Độ thích nghi (giá trị tiến hóa của cá thể = 12263) Lần 5:

13 45 112 0

117 0 0 83

0 115 8 57

Độ thích nghi (giá trị tiến hóa của cá thể = 12299)

Qua năm lần chạy thuật toán chúng ta thu được các lời giải khác nhau cho bài toán vận tải, đánh giá chung có thể nhận thấy lời giải thu được là tương đối tốt so với một số giải thuật khác. Chi tiết so sánh này sẽ được trình bày chi tiết hơn lở phần sau của luận văn.

Một phần của tài liệu (LUẬN văn THẠC sĩ) kết hợp giải thuật di truyền và tìm kiếm tabu giải bài toán tối ưu (Trang 42 - 56)

Tải bản đầy đủ (PDF)

(70 trang)