Mô tả thuật toán nhánh cận tuần tự giải quyết bài toán lập lịch công việc…

Một phần của tài liệu Song song hoá bài toán JSP trên một số môi trường tính toán song song và phân tán luận văn thạc sĩ (Trang 88 - 96)

6. Phương pháp nghiên cứu

3.2.2.Mô tả thuật toán nhánh cận tuần tự giải quyết bài toán lập lịch công việc…

công việc

Thuật toán nhánh cận bao gồm hai thuật toán chính để thực hiện như công trình của hai tác giả Tan Hui Woon và Sutinah Salim [12]. Thuật toán thứ nhất giúp phân nhánh và tạo ra cây phân cấp. Thuật toán toán thứ hai sử dụng để xác định cận, khi đã xác định xong cận cho các nút của cây ta có thể dựa vào cận để xác định được nhánh cây nào cho ra kết quả tối ưu nhất.

Thuật toán phân nhánh:

Bước 1: (khởi tạo)

Ω = {Tập các công đoạn bắt đầu} rij = 0 cho các (i, j) ∈ Ω

Bước 2: (chọn máy)

t(Ω) = min{rij + pij}

Chọn máy i* là máy có rij + pij nhỏ nhất để tiếp tục thực hiện Bước 3: (phân nhánh)

Tập Ω’ = { (i∗, j)|ri∗j <t(Ω) }

+ Trên tất cả (i∗, j) ∈ Ω’, mở rộng một phần lịch bằng cách lên lịch (i*,j) trên máy i*.

+ Xóa (i*, j) khỏi tập Ω.

+ Thêm một công đoạn kế tiếp sau (i*, j) vào Ω. + Quay lại bước 2 thực hiện

Thuật toán thực hiện cho đến khi tập Ω = ∅ và tất cả các phần tử trong tập Ω’ đều đã được duyệt qua.

Mô tả thuật toán: ban đầu thuật toán khởi tạo tập Ω là các nút đầu tiên được

mở rộng từ nút nguồn. Tiến hành chọn máy trong tập Ω có thời gian hoàn thành công đoạn (rij + pij) nhỏ nhất, sau đó thêm các công đoạn trong tập Ω mà thực hiện trên máy được chọn vào tập Ω’. Với mỗi phần tử trong tập Ω’ sẽ mở rộng được 1 nhánh. Thuật toán thực hiện cho đến khi tất cả các phần tử trong tập Ω’ đều đã được xét và tập Ω rỗng. Lúc này ta được cây phân cấp gồm nhiều nhánh.

Việc loại bỏ các nhánh không khả thi được xét ở điều kiện bước 3: Tập Ω’ = { (i∗, j)|ri∗j <t(Ω) }

Chỉ thêm các công đoạn từ tập Ω vào tập Ω’ mà có ri*j <t(Ω)

Thuật toán tính cận:

Bước 1:

- Tính LB (Lower Bound) là đường đi dài nhất từ U đến V Bước 2:

- Tính rij cho tất cả các công đoạn O(i, j), với rij là đường đi dài nhất từ nút nguồn đến nút O(i, j).

Bước 3:

- Tính lij cho tất cả các công đoạn O(i, j), với lij là đường đi dài nhất từ nút O(i, j) đến nút đích.

- Sau đó tính due date: dij = LB – lij + pij Bước 4: Giải quyết vấn đề máy đơn:

o Nếu Ci ≤ di thì Li = 0 o Nếu Ci > di thì Li = Ci - di

- Cận dưới có thể thu được bằng công thức sau: LBnew = LB + Li

Mô tả thuật toán: thuật toán dùng để tính cận dưới (lower bound) cho các nút

của cây phân cấp. Lịch trình tối ưu là nhánh mà có cận dưới nhỏ nhất trong cây phân cấp. Hay nói cách khác cận dưới chính là thời gian mà các công việc được hoàn thành (makespan).

Triển khai thuật toán tính cận: vấn đề chính cần giải quyết để có thể triển

khai thuật toán này là tính quãng đường dài nhất từ hai nút bất kỳ, việc tính quãng đường dài nhất chỉ có thể áp dụng trong trường hợp đồ thị có hướng và không có chu trình. Do đó để thực hiện được công việc này, tác giả đã tiến hành nghiên cứu một số thuật toán tìm kiếm đường đi ngắn nhất, sau đó từ các thuật toán này cải tiến thành thuật toán tìm đường đi dài nhất trong đồ thị có hướng không có chu trình. Một số thuật toán tìm đường đi ngắn nhất trên đồ thị được nghiên cứu sau:

Bài toán tìm đường đi ngắn nhất trên đồ thị: Cho đơn đồ thị có hướng liên thông và không có chu trình, có trọng số G=(V,E). Tìm khoảng cách đường đi ngắn nhất từ đỉnh xuất phát S ϵ V và đỉnh đích F ϵ V. Độ dài của đường đi này ký hiệu d[S, F] gọi là khoảng cách từ S đến F.

- Thuật toán Dijkstra:

Thuật toán Dijkstra mang tên của nhà khoa học máy tính người Hà Lan Edsger Dijkstra (1959). Thuật toán Dijkstra được xây dựng dựa trên cơ sở gán cho các đỉnh các nhãn tạm thời. Nhãn của mỗi đỉnh cho biết cận của độ dài đường đi ngắn nhất từ s đến nó. Các nhãn này sẽ được biến đổi theo một thủ tục lặp, mà ở mỗi bước lặp có một nhãn tạm thời trở thành nhãn cố định. Nếu nhãn của một đỉnh nào đó trở thành một nhãn cố định thì nó sẽ cho ta không phải là cận trên mà là độ dài của đường đi ngắn nhất từ đỉnh s đến nó. Thuật toán mô tả cụ thể như sau: Bước 1: Khởi tạo

Với định v ϵ V, gọi nhãn d[v] là độ dài đường đi ngắn nhất từ S đến v. Tính các d[v].

Ban đầu d[v] được khởi gán: d[S] = 0 và d[v] = ∞ với ∀v ≠ S.

Nhãn của mỗi đỉnh có hai trạng thái tự do hay cố định, nhãn tự do có nghĩa là có thể còn tối ưu hơn được nữa và nhãn cố định tức là d[v] đã bằng độ dài đường đi ngắn nhất từ S tới v nên không thể tối ưu hơn.

Để làm được điều này có thể sử dụng kỹ thuật đánh dấu: Free[v] = true hoặc Free[v] = false tùy theo d[v] tự do hay cố định. Ban đầu đặt các nhãn đều tự do.

Bước 2: Lặp

Bước lặp gồm hai thao tác:

Thao tác 1: Cố định nhãn, chọn trong các đỉnh có nhãn tự do, lấy ra đỉnh u là đỉnh có d[u] nhỏ nhất, và cố định nhãn đỉnh u.

Thao tác 2: Dùng đỉnh u, xét tất cả những đỉnh v và sửa lại các định d[v] theo công thức: d[v] = min(d[v], d[u] + c[u,v])

Bước lặp sẽ kết thúc khi mà đỉnh đích F được cố định nhãn (tìm được đường đi ngắn nhất từ S tới F); hoặc tại thao tác cố định nhãn, tất cả các đỉnh tự do đều có nhãn +∞ (không tồn tại đường đi).

Bước 3: Kết hợp với việc lưu vết đường đi trên từng bước sửa nhãn, thông báo đường đi ngắn nhất tìm được hoặc cho biết không tồn tại đường đi (d[F] = +∞).

- Thuật toán Ford Bellman:

Thuật toán Ford Bellman có thể phát biểu đơn giản như sau:

Với đỉnh xuất phát S. Gọi d[v] là khoảng cách từ S đến v với các giá trị khởi tạo là:

d[S] = 0

d[v] = +∞ nếu v ≠ S

Sau đó tối ưu hóa dần d[v] như sau:

Xét mọi cặp đỉnh u,v của đồ thị, nếu có một cặp đỉnh u, v mà đỉnh d[v] > d[u] + c[u, v] thì đặt lại d[v] = d[u] + c[u, v]. Tức là nếu độ dài đường đi từ S đến v

lớn hơn tổng độ dài đường đi từ S tới u cộng với chi phí từ u đến v thì ta sẽ hủy bỏ đường đi từ S đến v đang có và coi đường đi từ S đến v chính là đường đi từ S đến u sau đó đi tiếp từ u đến v. Chú ý rằng đặt c[u,v] = +∞ nếu (u, v) không là cung. Thuật toán sẽ kết thúc khi không thể tối ưu thêm bất kỳ một nhãn d[v] nào nữa.

Tính đúng của thuật toán: Tại bước khởi tạo thì mỗi d[v] chính là độ dài ngắn nhất của đường đi từ S tới v không quá 0 cạnh.

Giả sử khi bắt đầu bước lập thứ i (i≥1), d[v] đã bằng độ dài đường đi ngắn nhất từ S tới v qua không quá i-1 cạnh. Do tính chất: đường đi từ S tới v không quá I cạnh sẽ phải thành lập bằng cách: lấy một đường đi từ S tới một đỉnh u nào đó qua không quá i-1 cạnh, rồi đi tiếp tới v bằng cung (u, v), nên độ dài đường đi ngắn nhất từ S tới v qua không quá I cạnh sẽ được tính bằng giá trị nhỏ nhất trong các giá trị: (nguyên lý tối ưu Bellman)

Độ dài đường đi ngắn nhất từ S tới v không quá i-1 cạnh

Độ dài đường đi ngắn nhất từ S tới u không quá i-1 cạnh cộng với số cạnh (u,v) (∀ ). Vì vậy sau bước lặp tối ưu các d[v] bằng công thức:

d[v]bước i = min(d[v]bước i-1, d[u]bước i-1 + c[u,v]) (∀ )

thì các d[v] sẽ bằng độ dài đường đi ngắn nhất từ S tới v qua không quá i cạnh. Sau bước lặp tối ưu thứ n-1, ta có d[v] = độ dài đường đi ngắn nhất từ S tới v qua không quá n-1 cạnh. Vì đồ thị không có chu trình âm nên sẽ có một đường đi ngắn nhất từ S tới v là đường đi cơ bản (qua không quá n-1 cạnh). Tức là d[v] sẽ là độ dài đường đi ngắn nhất từ S tới v [3].

- Thuật toán Floyd:

Với một đơn đồ thị có hướng, trọng số G = (V, E), n đỉnh và m cạnh. Bài toán đặt ra là hãy tính tất cả các d(u,v) là khoảng cách từ u đến v. Ta có thể áp dụng thuật toán tìm đường đi ngắn nhất như là Ford Bellman để tìm lần lượt quãng đường giữa tất cả các cặp định. Nhưng có cách làm gọn hơn mà chỉ cần 1 lần chạy có thể tìm được đường đi ngắn nhất giữa mọi cặp đỉnh trong đồ thị, và đó là thuật toán Floyd.

Tư tưởng của thuật toán như sau, ví dụ có một đường đi từ u đến v và nếu đường đi từ u tới v đang có lại dài hơn đường đi từ u tới k cộng với đường đi từ k

tới v thì ta hủy bỏ đường đi từ u tới v hiện thời và coi đường đi từ u tới v sẽ là nối của hai đường đi từ u tới k rồi từ k tới v.

Thuật toán Floyd tìm đường đi ngắn nhất giữa các cặp đỉnh trong đồ thị: [14]

o Input : Đồ thị G = (V, E) được biểu diễn dạng ma trận trọng số C, và là một đồ thị có trọng số dương.

o Output: Xác định độ dài đường đi dài nhất giữa mọi cặp đỉnh (i, j), kết quả lưu trong một ma trận A.

o Xác định vết đường đi ngắn nhất giữa mọi cặp đỉnh và vết được lưu trong ma trận P.

o Xác định giá trị đầu cho hai ma trận A và P

+ Ma trận P lưu vết đường đi, chương trình chưa bắt đầu nên giá trị của P được gán tất cả các giá trị P[i, j] = 0;

+ Giá trị của ma trận A chính là độ dài ngắn nhất giữa mọi cặp đỉnh , khởi đầu, giá trị của A chính là độ dài thực trên đồ thị G = (V, E). Chính là độ dài thực tế trên đồ thị của 2 đỉnh i và j. A[i, j] = C[i, j]. For i=1 to n do For j=1 to n do If C[i][j] <=0 then Begin A[i, j] = C[i, j]; P[i, j] = j; Else A[i][j] = VOCUC; P[i][j] = -1; End

+ Xác định độ dài đường đi ngắn nhất từ đỉnh i tới j trên đồ thị, và lưu vết đường đi qua n lần lặp

o Thực hiện N lần lặp, sau lần lặp thứ k ma trận A sẽ chứa độ dài đường đi ngắn nhất giữa mọi cặp đỉnh đi qua các đỉnh thuộc tập {1, 2, 3, …, k). Cứ như vậy, sau n lần lặp,

ma trận A sẽ chứa độ dài đường đi ngắn nhất giữa mọi cặp đỉnh trong đồ thị.

o Giá trị A[i, j] chính là độ dài đường đi ngắn nhất giữa mọi cặp đỉnh trong đồ thị.

o Ký hiệu Ak là ma trận độ dài đường đi ngắn nhất ở lần lặp thứ k, tức là Ak[i, j] chính là độ dài đường đi ngắn nhất từ i đến j chỉ đi qua các đỉnh thuộc {1, 2, 3, …, k}. Ak[i, j] được tính theo công thức sau:

Ak[i, j]=min(Ak-1[i, j], Ak-1[i, k] + Ak-1[j, k]} o Ở bước lặp thứ k thì giá trị A[k, k] không thay đổi

For k=1 to n do For i=1 to n do For j=1 to n do

If A[i, j] > A[i, k] + A [k, j] then Begin

A[i, j]= A[i, k] + A[k, j]; P[i, j]=P[i][k];

End

Thuật toán Floyd tìm đường đi dài nhất giữa các cặp đỉnh trong đồ thị:

+ Giá trị ban đầu của ma trận A For i=1 to n do For j=1 to n do If -C[i][j] <=0 then Begin A[i, j] = -C[i, j]; P[i, j] = j; Else A[i][j] = VOCUC; P[i][j] = -1; End

+ Xác định độ dài đường đi dài nhất từ đỉnh i tới j trên đồ thị, và lưu vết đường đi qua n lần lặp

For k=1 to n do For i=1 to n do For j=1 to n do

If A[i, j] > A[i, k] + A [k, j] then Begin

A[i, j]= A[i, k] + A[k, j]; P[i, j]=P[i][k];

End

Nhận xét: Từ ba thuật toán tìm đường đi ngắn nhất: dijkstra, Ford Bellman và Floyd. Tác giả tiến hành nghiên cứu để cải tiến thuật toán tìm đường đi dài nhất giữa các nút trong đồ thị. Để tìm được đường đi dài nhất giữa hai hay nhiều nút trong đồ thị thì hướng giải quyết sử dụng đơn giản chỉ cần đảo dấu trọng số của đồ thị sau đó tiến hành tìm kiếm đường đi ngắn nhất thì ta sẽ có được đường đi dài nhất giữa hai hay nhiều nút. Với 3 thuật toán Dijkstra, Ford Bellman và Floy thì thuật toán Dijkstra không phù hợp với cách tìm đường đi dài nhất bằng cách đảo dấu trọng số. Vì thuật toán Dijkstra chỉ có thể áp dụng trong trường hợp đồ thị có trọng số dương mà không áp dụng cho trường hợp đồ thị trọng số âm. Với thuật toán Ford Bellman, thuật toán có thể áp dụng trên một đồ thị có trọng số âm vì vậy việc tìm đường đi dài nhất trong đồ thị với Ford Bellman là hoàn toàn khả thi. Nhưng vấn đề không tiện lợi ở đây nếu áp dụng thuật toán Ford Bellman vào để tính toán cận thì thuật toán tính cận sẽ trở nên phức tạp hơn vì mỗi một lần chạy thuật toán Ford Bellman chỉ có thể tìm kiếm được đường đi dài nhất của hai nút. Trong khi đó để có thể tính được cận sẽ cần phải tìm đường đi dài nhất trên nhiều cặp nút trong đồ thị. Thuật toán thứ ba là Floy, thuật toán Floyd triển khai được với đồ thị có trọng số âm do đó có thể áp dụng tìm đường đi dài nhất bằng phương pháp đảo dấu trọng số. Ngoài ra Floy còn có thể tìm được đường đi dài nhất giữa mọi cặp đỉnh trong đồ thị chỉ với 1 lần chạy. Vì nguyên nhân đó tác giả đã áp dụng thuật toán Floy để triển khai trong thuật toán tính cận.

Một phần của tài liệu Song song hoá bài toán JSP trên một số môi trường tính toán song song và phân tán luận văn thạc sĩ (Trang 88 - 96)