BÀI TOÁN TÌM ĐƯỜNG ĐI NGẮN NHẤT

11 1.9K 32
BÀI TOÁN TÌM ĐƯỜNG ĐI NGẮN NHẤT

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

BÀI TOÁN TÌM ĐƯỜNG ĐI NGẮN NHẤT (The Traveling Salesman Problem - TSP) I/ GIỚI THIỆU BÀI TOÁN Đây là một bài toán cổ điển: Một thương gia phải đi qua nhiều thành phố. Hãy vạch lộ trình đi qua tất cả các thành phố đó sao cho quãng đường đingắn nhất. Biết rằng mỗi thành phố chỉ đi qua một lần. Bài toán TSP khó giải quyết, vì để tìm được lời giải ta phải tiến hành tìm kiếm trên tất cả lộ trình có thể, và như vậy dẫn tới phí tổn thời gian tính toán rất lớn. Backtracking và các kỹ thuật khác có thể rút ngắn phạm vi tìm kiếm trong một số điều kiện nhưng vẫn chỉ là sự hoàn thiện của giải pháp tìm kiếm toàn diện. Khoa học máy tính vẫn chưa tìm ra được một giải thuật cụ thể có hiệu quả để giải những bài toán tương tự như TSP có kích thước lớn. Giải thuật Gene tỏ ra hiệu quả trong việc giải các bài toán có thông tin không đầy đủ. Vì vậy giải thuật này sẽ được sử dụng để hiện thực chương trình. II/ CÁC KỸ THUẬT GHÉP CHÉO MỚI Giải thuật Gene mô phỏng sự tiến hoá của thế giới tự nhiên, ở đó thông tin về một cấu trúc sống được mã hoá dưới dạng nhiễm sắc thể. Do đó để áp dụng giải thuật Gene vào bài toán TSP, ta cũng phải tìm cách mã hoá các lời giải của bài toán dưới dạng các chuỗi nhiễm sắc thể và xây dựng qui tắc đánh giá độ thích nghi của nó theo ngữ cảnh của bài toán. Kí hiệu các thành phố là A, B, C, . mỗi nhiễm sắc thể - sự mã hoá của lời giải - sẽ là một danh sách hoán vị của A, B, C . biểu diễn lộ trình mà người thương gia đã đi qua. Thí dụ GFHBACDE sẽ là kí hiệu của hành trình từ G -> F -> H -> . -> E. Mỗi thành phố (gene) sẽ chỉ xuất hiện trong danh sách này chỉ một lần. Vấn đề này dẫn tới khó khăn không thể áp dụng các kĩ thuật sinh sản, đột biến thường được sử dụng trong giải thuật Gene chuẩn. Thí dụ: Với ý nghĩa ghép chéo thông thường ta nhận được từ cặp bố mẹ 1, 2 các con cái sau: - Ghép chéo tại một điểm: Bố mẹ Con cái ---------------------------------------------------- #1 ABC|DEFGH ABC|BACDE #2 GFH|BACDE GFH|BACDE - Ghép chéo tại hai điểm: Bố mẹ Con cái ---------------------------------------------------- #1 AB|CDE|FGH AB|HBA|FGH #2 GF|HBA|CDE GF|CDE|CDE Điều kiện chỉ xuất hiện một lần trong danh sách hoán vị ở hai thí dụ trên đã bị vi phạm. Do đó đòi hỏi phải xây dựng các kĩ thuật ghép chéo mới phù hợp với ý nghĩa của bài toán. 2.1- GHÉP CHÉO RIÊNG PHẦN (Partially matched crossover - PMX) PMX được cải tiến từ phương pháp ghép chéo hai điểm thông thường. Bắt đầu bằng việc chọn ra hai điểm, thí dụ 2 và 5: Cha mẹ 1: AB|CDE|FGH Cha mẹ 2: GF|HBA|CDE Giải thuật PMX nhận thấy gene H có trong nhiễm sắc thể 2 sẽ được thay thế bởi gene C trong nhiễm sắc thể 1, vì vậy nó sẽ chuyển đổi H của nhiễm sắc thể 1 thành C và C trong nhiễm sắc thể 2 sang H. Quá trình được thực hiện tương tự với hai gene còn lại và cuối cùng nhận được các con cái: Con cái 1: ED|HBA|FGC Con cái 2: GF|CDE|HBA Mỗi con cái là một hoán vị mới, hoán chuyển một số gene trong khi vẫn giữ nguyên các đoạn cấu trúc của bố mẹ. 2.2- GHÉP CHÉO CÓ THỨ TỰ (Order crossover - OX) OX dời chỗ một số gen và dịch chuyển các gene khác. Lấy ví dụ ở trên, với kí hiệu "-" biểu thị gene bỏ trống ở vị trí đó: Cha mẹ 1: AB|CDE|FGH Cha mẹ 2: GF|HBA|CDE Con cái 1: --|HBA|FG- Con cái 2: GF|CDE|--- tiếp theo, ở sau điểm ghép chéo thứ hai, OX sẽ dịch các gene sang trái (có thể coi các đầu mút của nhiễm sắc thể nối với nhau nếu cần thiết), điền các vị trí trống của gene và để trống đoạn hoán đổi: Con cái 1: BA|---|FGH Con cái 2: DE|---|GFC Cuối cùng OX sẽ hoán đổi đoạn ghép chéo nằm giữa hai điểm, tạo ra các con cái mới: Con cái 1: BA|CDE|FGH Con cái 2: DE|HBA|GFC Trong khi PMX giữ nguyên vị trí của gene trong chuỗi nhiễm sắc thể, thì OX lại duy trì thứ tự các gene trong chuỗi. 2.3- GHÉP CHÉO CÓ CHU KÌ (Cycle crossover - CX) Nguyên tắc làm việc của CX hoàn toàn khác, nó thực hiện việc hoán đổi một tập hợp cụ thể các gen. Thí dụ: Cha mẹ 1: ABCDEFGH Cha mẹ 2: GFHBACDE Để tạo con cái, CX bắt đầu với các thành phố đầu tiên trong các nhiễm sắc thể bố mẹ: Con cái 1: G------- Con cái 2: A------- Tìm trong cha mẹ 1, CX thấy G ở vị trí 7 và thực hiện hoán đổi ở vị trí đó: Con cái 1: G-----D- Con cái 2: A-----G- Quá trình tìm và hoán đổi tiếp tục cho đến khi gene bị thay thế đầu tiên trong cha mẹ 1, A, được bắt gặp. Cuối cùng con cái nhận được như sau: Con cái 1: GECBAFDH Con cái 2: ABHDECGE 2.4- PHÉP ĐẢO VỊ TRÍ (Inversion operator) Để tăng tính đa dạng có thể đưa thêm vào bài toán phép hoán vị, thực hiện việc đảo ngược vị trí các gene trong chuỗi nhiễm sắc thể. Thí dụ: ABC|DEFGH đảo thành ABCHGFED G|FHB|ACDE đảo thành GBHFACDE 2.5- PHÉP ĐỘT BIẾN Được định nghĩa lại dưới dạng hoán vị vị trí của hai gene. Thí dụ: Hoán vị các gene A và E trong chuỗi ABCDEFGH sẽ cho con cái là EBDEAGFH. III/ SƠ ĐỒ GIẢI THUẬT GEN CỦA BÀI TOÁN TSP Sau khi xây dựng phép mã hoá dưới dạng nhiễm sắc thể và định nghĩa các phép toán sinh sản, có thể đưa ra sơ đồ giải thuật Gene để hiện thực chương trình: PHÉP HOÁN VỊ JOSEPHUS Theo sơ đồ, cộng đồng ban đầu được hình thành bằng phép hoán vị Josephus. Giả sử có một danh sách các thành phố được kí hiệu từ A tới H, có thể tạo một hoán vị bằng việc chọn điểm xuất phát và một số đếm - thí dụ F và 5. Giả thiết xem danh sách có dạng vòng tròn bằng cách nối liền hai đầu mút, đếm 5 thành phố ở bên phải F ta được C - thành phố đầu tiên trong danh sách hoán vị. Tiếp tục từ C đếm 5 thành phố khác sẽ được thành phố thứ hai trong danh sách hoán vị - H. Lập lại quá trình này cuối cùng ta nhận được danh sách hoán vị CHFEGBDA. Như vậy bằng cách chọn ngẫu nhiên điểm bắt đầu và số đếm, chương trình có thể tạo ra một tập gồmcác danh sách hoán vị khác. IV/ HIỆN THỰC CHƯƠNG TRÌNH Số thành phố được chọn trước là 10 , kí hiệu A, B, C, mà không tạo phần input. Population: Số thành viên (lộ trình tìm kiếm) cần kiểm tra. Việc giữ lại thành viên tốt nhất của thế hệ cha mẹ trong thế hệ con cái, và sự tham gia của 2 cá thể cha mẹ trong các phép toán sinh sản đòi hỏi Population phải là số lẻ. Generations: Số thế hệ cần kiểm tra. Report Frequency: Bao nhieu thế hệ thì thông báo kết quả một lần? Show Best: Số nhiễm sắc thể được xuất cho từng lần thông báo. Oper Probability: Xác xuất các phép toán sinh sản được sử dụng khi tạo con cái. Mutation, Inversion, PM Crossover, Order Crossover: Nếu đánh dấu checkbox thì các phép toán sinh sản tương ứng sẽ được chọn theo xác xuất tương ứng. Linear Normalization Các thông số này dùng để điều chỉnh lại các fitness của các thành viên trong cộng đồng vì chúng có thể chênh lệch không nhiều. Sơ đồ giải thuật Gene của bài toán TSP ở trên có thể hiện thực bằng đoạn chương trình chính sau đây: typedef size_t CityChrom[10]; static const size_t CSZ = 10 * sizeof(size_t); const size_t POP_SZ = PopSize; static const char *cityName[10] = { "A ", "E ", "G ", "I ", "C ", "F ", "D ", "H ", "B ", "E " }; static const double distance[10][10] = { {0.0, 220.0, 90.0, 155.0, 133.0, 123.0, 182.0, 89.0, 105.0, 141.0}, {220.0,0.0, 135.0, 55.0, 173.0, 117.0, 124.0, 122.0, 222.0, 85.0}, {90.0, 135.0, 0.0, 92.0, 69.0, 34.0, 95.0, 56.0, 98.0, 57.0}, {155.0,55.0, 92.0, 0.0, 145.0, 84.0, 116.0, 68.0, 184.0, 54.0}, {133.0,173.0, 69.0, 145.0, 0.0, 60.0, 72.0, 125.0, 70.0, 91.0}, {123.0,117.0, 34.0, 84.0, 60.0, 0.0, 61.0, 76.0, 106.0, 33.0}, {182.0,124.0, 95.0, 116.0, 72.0, 61.0, 0.0, 134.0, 137.0, 66.0}, {89.0, 122.0, 56.0, 68.0, 125.0, 76.0, 134.0, 0.0, 142.0, 73.0}, {105.0,222.0, 98.0, 184.0, 70.0, 106.0, 137.0, 142.0, 0.0, 139.0}, {141.0,85.0, 57.0, 54.0, 91.0, 33.0, 66.0, 73.0, 139.0, 0.0} }; if (distance==NULL) MessageBox("Khong du bo nho"); //tạo các vùng lưu trữ cộng đồng và mảng fitness CityChrom *pop = new CityChrom[POP_SZ]; CityChrom *newpop = new CityChrom[POP_SZ]; double *fit = new double[POP_SZ]; // các pointers dùng cho việc sắp xếp mảng fitness CityChrom *ptrp = pop - 1; double * ptrf = fit -1; // khai báo các biến size_t g, i, j, k, l, n, s, t, p1, p2, inc; double vf; CityChrom vp; RouletteWheel *rw; RandDev devgen; // bánh xe roulette dùng chọn phép toán sinh sản double operwt[5]; if (Mutation) operwt[0] = WeightM; else operwt[0] = 0.0; if (Inversion) operwt[1] = WeightI; else operwt[1] = 0.0; if (PMX) operwt[2] = WeightP; else operwt[2] = 0.0; if (CX) operwt[3] = WeightC; else operwt[3] = 0.0; if (OX) operwt[4] = WeightO; else operwt[4] = 0.0; RouletteWheel ow(5,operwt); // khởi tạo cộng đồng ban đầu bằng phép hoán vị Josephus for (i = 0; i < POP_SZ; ++i) { int plist[10]; memset(plist,0,CSZ); s = size_t(devgen() * 8.0) + 1; j = size_t(devgen() * 10.0); k = 0; while (1) { pop[i][k] = j; plist[j] = 1; if (k == 9) break; for (l = 0; l < s; ++l) { do { ++j; if (j>9) j = 0; } while (plist[j] == 1); } ++k; } } // bắt đầu vòng lặp chính của chương trình g=0; while (1) { // tính fitness for (i =0; i < POP_SZ; ++i) { fit[i] = 0.0; for (j = 1; j < 10; ++j) fit[i] += distance[pop[i][j-1]][pop[i][j]]; } // sắp mảng để chuẩn bị cho phép chuẩn hoá tuyến tính for (inc = 1; inc <= POP_SZ / 9; inc = 3 * inc + 1); for ( ; inc > 0; inc /= 3) { for (i = inc + 1; i <= POP_SZ; i += inc) { vf = ptrf[i]; memcpy(vp, ptrp[i], CSZ); j = i; while ((j > inc) && (ptrf[j - inc] > vf)) { ptrf[j] = ptrf[j - inc]; memcpy(ptrp[j], ptrp[j - inc], CSZ); j -= inc; } ptrf[j] = vf; memcpy(ptrp[j],vp,CSZ); } } // ngừng chương trình nếu điều kiện dừng thoả if (g == TestSize) break; // áp dụng fitness scaling fit[0] = FitLinBase; i =1; while (1) { if (fit[i -1] <= FitLinDec) break; fit[i] = fit[i - 1] - FitLinDec; ++i; } for ( ; i < POP_SZ; ++i) fit[i] = FitLinMin; // chọn phần tử tốt nhất memcpy(newpop[0], pop[0], CSZ); // tạo thế hệ mới rw = new RouletteWheel(POP_SZ,fit); for (i = 1; i < POP_SZ; i += 2) { // chọn cha mẹ để sinh sản p1 = rw->GetIndex(); do { p2 = rw->GetIndex(); } while (p2 == p1); memcpy(newpop[i], pop[p1], CSZ); memcpy(newpop[i+1],pop[p2], CSZ); // bỏ qua phần còn lại của vòng lặp nếu không có phép toán // được chọn if (devgen() > OperChance /100.0F) continue; // chọn phép toán switch (ow.GetIndex()) { case 0: // mutation for (n = 0; n < 2; ++n) { // chọn chỉ số j = size_t(devgen() * 10.0F); do { k = size_t(devgen() * 10.0F); } while (k== j); // hoán đổi chỉ số tham chiếu thành phố t = newpop[i+n][k]; newpop[i+n][k] = newpop[i+n][j]; newpop[i+n][j] = t; } break; case 1: // inversion for (n = 0; n < 2; ++n) { //chọn chỉ số j = size_t(devgen() * 9.0F); do { k = size_t(devgen() * 10.0F); } while (k <= j); // tính toán chiều dài s = (k- j +1) / 2; //đảo for (l = 0; l < s; ++l) { t = newpop[i+n][k]; newpop[i+n][k] = newpop[i+n][j]; newpop[i+n][j] = t; ++j; --k; } } break; case 2: // partially crossover j = size_t(devgen() * 9.0F); do { k = size_t(devgen() * 10.0F); } while (k <= j); // hoán chuyển thành phố for (n=j; n<=k; ++n) { if (pop[p1][n] != pop[p2][n]) { s = TAFindCity(newpop[i+1], pop[p1][n]); t=newpop[i+1][n]; newpop[i+1][n]=newpop[i+1][s]; newpop[i+1][s]=t; s=TAFindCity(newpop[i],pop[p2][n]); t=newpop[i][n]; newpop[i][n]=newpop[i][s]; newpop[i][s]=t; } } break; case 3: // cycle crossover j=size_t(devgen() * 10.0F); t=pop[p1][j]; while (1) { newpop[i][j]=pop[p2][j]; newpop[i+1][j]=pop[p1][j]; if (newpop[i][j] == t) break; j = TAFindCity(pop[p1], newpop[i][j]); } break; case 4:// order crossover j=size_t(devgen() * 9.0F); do { k=size_t(devgen() * 10.0F); } while (k <= j); if ((j == 0) && (k == 9)) { memcpy(vp,newpop[i], CSZ); memcpy(newpop[i], newpop[i+1], CSZ); memcpy(newpop[i+1],vp,CSZ); break; } if (k ==9) n=0; else n=k+1; // dịch chuyển và điền vị trí gen bỏ trống do { while (1) { s =TAFindCity(pop[p2], newpop[i][n]); if ((s<j) || (s>k)) break; // shift members if (n==9) l=0; else l=n+1; while (1) { if (l==0) newpop[i][9]=newpop[i][0]; else newpop[i][l1]=newpop[i][l]; if (l==k) break; if (l==9) l=0; else ++l; } } while (1) { s=TAFindCity(pop[p1], newpop[i+1][n]); if ((s<j) || (s>k)) break; // dịch các thành phần if (n==9) l=0; else l=n+1; while (1) { if (l==0) newpop[i+1][9]=newpop[i+1][0]; else newpop[i+1][l- 1]=newpop[i+1][l]; if (l==k) break; if (l==9) l=0; else ++l; } } if (n==9) n=0; else [...]... một lần thực hiện chương trình với giá trị thông số nhập trong hộp thoại Giá trị 559 cũng chính là lới giải của bài toán V/ NHẬN XÉT: Để tìm ra lời giải đúng của bài toán trong trường hợp số thành phố là 10, ta phải xét (10)!= 3628800 tổ hợp Giải thuật Gen trong trường hợp này cho thấy có thể tìm ra lời giải trong số tổ hợp ít hơn nhiều (thế hệ 90) Chương trình này khi chạy thử nghiệm 10 lần đã có 4 lần... trình này khi chạy thử nghiệm 10 lần đã có 4 lần cho lời giải đúng, các lần còn lại chỉ cho lời giải mang tính tối ưu gần đúng Để lời giải tiệm cận tới giá trị tối ưu (tránh đường cong tối ưu nằm ngang khi chưa thỏa), vai trò của phép toán đột biến rất quan trọng . BÀI TOÁN TÌM ĐƯỜNG ĐI NGẮN NHẤT (The Traveling Salesman Problem - TSP) I/ GIỚI THIỆU BÀI TOÁN Đây là một bài toán cổ đi n: Một thương gia phải đi qua. trình đi qua tất cả các thành phố đó sao cho quãng đường đi là ngắn nhất. Biết rằng mỗi thành phố chỉ đi qua một lần. Bài toán TSP khó giải quyết, vì để tìm

Ngày đăng: 05/10/2013, 16:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan