Top Banner
Trong môn phân tích thiết kế giải thuật một trong những phương pháp gây đau đầu nhất đó là phương pháp quy hoạch động hôm nay mình muốn trình bày lại cách cài đặt bài bài toán ba lô 1, một trong những bài toán chọn món hàng nổi tiếng, mọi người góp ý nha! Bài toán ba lô 1 Đề bài: Cho n món hàng (n ≤ 50). Món thứ i có khối lượng là A[i] (số nguyên). Cần chọn những món hàng nào để bỏ vào một ba lô sao tổng khối lượng của các món hàng đã chọn là lớn nhất nhưng không vượt quá khối lượng W cho trước. (W ≤ 100). Mỗi món chỉ chọn 1 hoặc không chọn. Bài toán ba lo 1 Input: n W A[1] A[2] … A[n] Ví dụ: 4 10 5 2 4 3
32

balo1

Jul 29, 2015

Download

Documents

Tri Thanh
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: balo1

Trong môn phân tích thiết kế giải thuật một trong những phương pháp gây đau đầu nhất đó là phương pháp quy hoạch động hôm nay mình muốn trình bày lại cách cài đặt bài bài toán ba lô 1, một trong những bài toán chọn món hàng nổi tiếng, mọi người góp ý nha!

Bài toán ba lô 1

Đề bài: Cho n món hàng (n ≤ 50). Món thứ i có khối lượng là A[i] (số nguyên). Cần chọn những món hàng nào để bỏ vào một ba lô sao tổng khối lượng của các món hàng đã chọn là lớn nhất nhưng không vượt quá khối lượng W cho trước. (W ≤ 100). Mỗi món chỉ chọn 1 hoặc không chọn.

Bài toán ba lo 1

Input: n W

A[1] A[2] … A[n]

Ví dụ:

4 105 2 4 3

OutPut:

Tổng khối lượng của các món hàng bỏ vào ba lô.Khối lượng của các món hàng đã chọn.

Page 2: balo1

Trong ví dụ trên:

Tổng khối lượng của các món hàng bỏ vào ba lô là 10Khối lượng các món hàng được chọn: 5 2 3

Hướng giải bài toán ba lô 1

1.Tổ chức dữ liệu

Fx[k, v] là tổng khối lượng của các món hàng bỏ vào ba lô khi có k món hàng đầu tiên để chọn và khối lượng tối đa của ba lô là v. Với k ∈ [1, n], v ∈ [1, W].

Nói cách khác: Khi có k món để chọn, Fx[k, v] là khối lượng tối ưu khi khối lượng tối đa của ba lô là v.Khối lượng tối ưu luôn nhỏ hơn hoặc bằng khối lượng tối đa: Fx[k, v] ≤ v

Ví dụ: Fx[4, 10] = 8 Nghĩa là trong trường hợp tối ưu, tổng khối lượng của các món hàng được chọn là 8, khi có 4 món đầu tiên để chọn (từ món thứ 1 đến món thứ 4) và khối lượng tối đa của ba lô là 10. Không nhất thiết cả 4 món đều được chọn.

2. Thuật toán tạo bảng

Trường hợp đơn giản chỉ có 1 món để chọn: Ta tính Fx[1, v] với mọi v: Nếu có thể chọn (nghĩa là khối lượng tối đa của ba lô >= khối lượng của các món hàng thứ 1), thì chọn: Fx[1, v] = A[1];

Ngược lại ( v < A[1] ), không thể chọn, nghĩa là Fx[1, v] = 0; Giả sử ta đã tính được Fx[k–1 , v ] đến dòng k–1, với mọi v ∈ [1, W]. Khi có thêm món thứ k để chọn, ta cần tính Fx[k , v] ở dòng k, với mọi v∈[1,W] Nếu có thể chọn món hàng thứ k (v >= A[k]), thì có 2 trường hợp:

Trường hợp 1: Nếu chọn thêm món thứ k bỏ vào ba lô, thì Fx[k, v] = Fx[ k–1 , u ] + A[k]; Với u là khối lượng còn lại sau khi chọn món thứ k. u = v – A[k]

Trường hợp 2: Ngược lại, không chọn món thứ k, thì Fx[k, v] = Fx[k–1, v ];

Trong 2 trường hợp trên ta chọn trường hợp nào có Fx[k, v] lớn hơn.

Ngược lại (v < A[k]), thì không thể chọn, nghĩa là Fx[k, v] = Fx[k–1, v]; Tóm lại: công thức đệ quy là: if (v >= A[k])Fx[k,v] = Max(Fx[k-1, v - A[k]] + A[k] , Fx[k-1,v])elseFx[k,v] = Fx[k-1, v];

Dưới đây là bảng Fx[k,v] tính được trong ví dụ trên:

Page 3: balo1

Thuật toán tạo bảng trong ba lo 1

3. Thuật toán tra bảng để tìm các món hàng được chọn

Chú ý: Nếu Fx[k, v] = Fx[k–1, v] thì món thứ k không được chọn. Fx[n, W] là tổng khối lượng tối ưu của các món hàng bỏ vào ba lô.Bước 1: Bắt đầu từ k = n, v = W.Bước 2: Tìm trong cột v, ngược từ dưới lên, ta tìm dòng k sao cho Fx[k,v] > Fx[k–1, v]. Đánh dấu món thứ k được chọn: Chọn[k] = true;Bước 3: v = Fx[k, v] – A[k]. Nếu v > 0 thì thực hiện bước 2, ngược lại thực hiện bước 4Bước 4: Dựa vào mảng Chọn để in ra các món hàng được chọn.

Cài đặt bài toán ba lô 1

Đây là một cách cài đặt demo cơ bản mà thg bạn nó cài bằng C, các bạn xem tham khảo nha! Tuy không tối ưu nhưng đã demo được bài toán ba lô 1, các bạn có thể tự phát triển thêm.

12345678910111213141516171819

<!--#include<iostream.h>#include<stdio.h>#include<conio.h>

 void docfile(int a[],int &n,int &w){    FILE *f;    f=fopen("balo1.txt","rt");    fscanf(f,"%d%d",&n,&w);    for(int i=1;i<=n;i++)        fscanf(f,"%d",&a[i]);        fclose(f);}

 void ghifile(int Chon[],int A[],int n){    FILE *f;    f=fopen("kqbalo1.text","wt");    for(int i=1;i<=n;i++)        if(Chon[i]==1)    fprintf(f,"%3d",A[i]);

Page 4: balo1

2021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869

 }

 int Max(int a, int b){    if(a>b)        return a;    return b;}

 void main(){    int Fx[101][101];//bang    int A[101];//A mang trong luong    int Chon[101];    int n;    int W;

     docfile(A,n,W);

     //Chua chon mon hang nao gan bang 0.    for (int k=1;k<=W;k++)    Fx[0][k]=0;

     // Tao bang    for (int k=1;k<=n;k++)//k so mon hang, v khoi luong toi da    {        for (int v=1;v<=W;v++)        {

             if (v>=A[k])            {                Fx[k][v]=Max(Fx[k-1][v-A[k]]+A[k],Fx[k-1][v]);            }            else            {                Fx[k][v]=Fx[k-1][v];            }        }    }

     // Kiem tra bang tim ra cac mon hang duoc chon    int v=W;    int k=n;

     while( v>0)    {        for(int k=n;k>=0;k--)        {            if(Fx[k][v]>Fx[k-1][v])            {                Chon[k]=1;                v=Fx[k][v]-A[k];            }        }    }

     //In cac mon duoc chon    ghifile(Chon,A,n);

Page 5: balo1

7071727374757677787980818283

}-->

==

Bất biến vòng lặp Sử dụng phương pháp bất biến vòng lặp để chứng minh một thuật toán giải bài toán là đúng đắn Ta phải chỉ ra ba điều về một bất biến của vòng lặp:Khởi tạo: bất biến của vòng lặp phải đúng trước lần lặp đầu tiên.Duy trì: Nếu nó đúng trước một vòng lặp, nó vẫn còn đúng trước vòng lặp tiếp theo.Kết thúc: Khi vòng lặp kết thúc, bất biến này cho chúng ta một tính chất hữu ích giúp chứng minh được thuật toán là đúng đắn.

Khi hai tính chất đầu tiên xảy ra, bất biến của vòng lặp là đúng trước mỗi vòng lặp.Chú ý sự tương tự đối với phương pháp quy nạp toán học, khi chứng minh một tính chất là đúng đắn, bạn chứng minh trường hợp cơ sở và thực hiện bước quy nạp. Việc chứng minh bất biến của vòng lặp trước lần lặp đầu tiên giống như là trường hợp cơ sở, và việc chứng minh tính bất bién không thay đổi từ lần lặp này qua lần lặp khác giống như một bước quy nạp.

Tính chất thứ ba có lẽ là tính chất quan trọng nhất, vì chúng ta sử dụng bất biến của vòng lặp để chứng minh tính đúng đắn của nó. Nó cũng khác với cách sử dụng thông thường của quy nạp, trong đó bước quy nạp được sử dụng vô hạn lần; ở đây, chúng ta dừng “quy nạp” khi vòng lặp kết thúc. Trả lời kèm trích dẫn 26-02-2012, 12:47 AM #3

fod© Administrator

Ngày tham gia05 - 2011

Bài viết

Page 6: balo1

255Cảm ơn

4Được cảm ơn 11 lần trong 9 bài viết

Giải thuật đệ quy Trong toán học và khoa học máy tính, các tính chất (hoặc cấu trúc) được gọi là đệ quy nếu trong đó một lớp các đối tượng hoặc phương pháp được xác định bằng việc xác định một số rất ít các trường hợp hoặc phương pháp đơn giản (thông thường chỉ một) và sau đó xác định quy tắc đưa các trường hợp phức tạp về các trường hợp đơn giản. Chẳng hạn, định nghĩa sau là định nghĩa đệ quy của tổ tiên:

Bố mẹ của một người là tổ tiên của người ấy (trường hợp cơ bản);Bố mẹ của tổ tiên một người bất kỳ là tổ tiên của người ấy (bước đệ quy).

Các định nghĩa kiểu như vậy cũng thường thấy trong toán học (chính là quy nạp toán học)

Yêu cầu để giải thuật đệ qui thoả mãn tính dừng:

- T’ phải “đơn giản hơn” T ;- T’ giải được (không cần đệ qui) trong một số trường hợp nào đó (trường hợp suy biến.Ví dụ bài toán tính n! thông qua (n-1)! đơn giản dần đến lúc tính 1!=1

Đặc trưng của các bài toán có thể giải bằng đệ quy:

- Các bài toán phụ thuộc tham số;- Ứng với các giá trị đặc biệt nào đó của tham số thì bài toán có giải thuật để giải (trường hợp suy biến)Trong trường hợp tổng quát bài toán có thể qui về dạng tương tự với một bộ giá trị mới của tham số và sau một số hữu hạn lần thì có thể dẫn tới trường hợp suy biến.

Lược đồ giải thuật đệ quy

Code:  (Chọn tất cả)

Recursive_Algorithm(pn) =

If (pn = p0) //TH suy biến

Page 7: balo1

//Thực hiện giải thuật trường hợp suy biến

else //TH tổng quát

Recursive_Algorithm(pn-1);

endif

End.

Trả lời kèm trích dẫn 26-02-2012, 12:51 AM #4

fod© Administrator

Ngày tham gia05 - 2011

Bài viết255

Cảm ơn4Được cảm ơn 11 lần trong 9 bài viết

Phương pháp vét cạn Có lẽ thuật toán được sử dụng nhiều nhất, quan trọng nhất là kỹ thuật ″Chia để Trị″. Kỹ thuật này sẽ chia bài toán hiện thời thành N bài toán nhỏ hơn, thực hiện lời giải cho từng bài toán nhỏ này và từ đó xây dựng thuật toán cho bài toán lớn tổng hợp.

1. Ý tưởng giải thuật chia để trị

Là phương pháp thiết kế thuật toán dựa trên 2 thao tác chính:- Chia: phân rã bài toán ban đầu thành các bài toán con có kích thước nhỏ hơn, có cùng cách giải.- Trị: giải từng bài toán con (theo cách tương tự bài toán đầu - đệ qui) rồi tổng hợp các lời giải để nhận kết quả của bài toán ban đầu.

Việc “Phân rã”: thực hiện trên miền dữ liệu (chia miền dữ liệu thành các miền nhỏ hơn tương đương 1 bài toán con) đến lúc miền dữ liệu đủ nhỏ để bài toán có thể giải xác định (trường hợp suy biến trong đệ qui)

2. Mô hình

Page 8: balo1

Code:  (Chọn tất cả)

D&C(R) ≡

if(R=R0)

;

else

Chia miền R thành R1,R2,…,Rn

for(i=1…n)

D&C(Ri)

Tổng hợp để nhận lời giải.

End.

3. Tính đúng của giải thuật chia để trị

- Chia để trị sử dụng kĩ thuật đệ qui, thông thường là đệ qui nhiều nhánh- Tính đúng của thuật toán D&C có thể được chứng minh như đối với giải thuật đệ quy (sử dụng quy nạp) hoặc có thể sử dụng bất biến vòng lặp.

4. Một số chú ý khi thiết kế giải thuật chia để trị

khử đệ quy cho thuật toán D- Lời gọi đệ qui trên nhiều bài toán, nhiều lần có thể dẫn tới hiện tượng tràn vùng nhớ đệm &C.- Cần có chiến lược phân rã hợp lý miền dữ liệu để đảm bảo thuật giải tốt nhất.- Hạn chế thường gặp của thuật toán D&C: Việc phân rã có thể dẫn phương pháp quy hoạch độngtới một số các bài toán con trùng nhau (Bottom-Up).

5. Một số ví dụ

Bài toán ″Tháp Hà Nội″: Giả sử có 3 cọc A, B, C. Ban đầu tại A đặt một số đĩa với thứ tự trên nhỏ dưới to như hình vẽ.

Yêu cầu của bài toán là chuyển toàn bộ số đĩa trên sang cọc B, trong quá trình chuyển được phép sử dụng đĩa C, mỗi lần chuyển đúng 01 đĩa và luôn bảo đảm nguyên tắc đĩa nhỏ nằm trên đĩa to trong suốt quá trình chuyển.

Bài toán

Tháp Hà Nội trên có thể giải với thuật toán ″thông minh″ sau: Giả sử ta đặt 3 cọc trên tại các đỉnh của một tam giác đều. Tại bước với số lượt là lẻ, ta chuyển

Page 9: balo1

đĩa nhỏ nhất sang cọc bên cạnh theo chiều kim đồng hồ, tại bước đi với số lượt là chẵn, ta thực hiện một thao tác bất kỳ nhưng không đụng đến cái đĩa nhỏ nhất. Các bạn dễ dàng kiểm tra rằng đó là một thuật toán đúng, tuy nhiên nó rất khó hiểu, khó tổng quát sang các trường hợp khác.

Ta hãy thử vận dụng tư duy của thuật toán ″Chia để Trị″ đối với bài toán Tháp Hà Nội này. Bài toán chuyển N đĩa từ A sang B có thể chia thành 2 bài toán nhỏ hơn với kích thước N-1 như sau: (a) Chuyển N-1 đĩa đầu tiên từ A sang C (giữ nguyên trạng thái của đĩa thứ N tại A). (b) Chuyển đĩa thứ N từ A sang B và chuyển N-1 đĩa từ C sang B. Chú ý rằng khi thực hiện bài toán (b) phần chuyển N-1 đĩa từ C sang B ta có thể dùng lại hoàn toàn thuật toán của bài (a) nhưng với vị trí thay đổi giữa A và C và tất nhiên bỏ qua sự có mặt của đĩa thứ N trong A hay B. Với cách tư duy như vậy, việc mô phỏng thuật toán sẽ tương đối khó do nó phải gọi đệ qui đến chính nó nhưng cách làm trên thật là dễ hiểu và cho phép chúng ta áp dụng cho nhiều lớp bài toán khác. Chúng ta hãy xét một vài ví dụ.

Bài toán nhân các số tự nhiên lớn

Xét bài toán nhân 2 số tự nhiên n-bit X và Y. Bài toán nhân 2 số tự nhiên n-bit (n chữ số) đã được dạy trong nhà trường phổ thông với độ phức tạp O(n2)(3). Bây giờ chúng ta sẽ xét lại bài toán này với kỹ thuật Chia để Trị. Ta phân tách mỗi số X, Y thành 2 phần, mỗi phần n/2 bit. Để cho đơn giản ta sẽ luôn xét n là lũy thừa của 2. X, Y sẽ được phân tích thành 2 phần n/2-bit như sau:

X = A | B (X = A2n/2 + B)

Y = C | D

(Y = C2n/2 + D)

Khi đó tích XY sẽ có dạng:

XY = AC2n + (AD+BC)2n/2 + BD (1)

Dựa trên công thức (1) ta có thể suy luận đơn giản như sau cho việc tính tích XY: chúng ta sẽ tính 4 phép nhân với các số n/2-bit là AC, AD, BC và BD, sau đó thực hiện 3 phép cộng với các số 2n-bit, cuối cùng là 2 phép chuyển chữ số (2 phép nhân với lũy thừa của 2) Các phép cộng và phép chuyển chữ số đều được thực hiện với thời gian O(n), do đó ta thu được công thức tính độ phức tạp của phép toán trên T(n) là:

T(1) = 1

T(n) = 4T(n/2) + C.n (C-const)

Page 10: balo1

(2) Công thức (2) cho ta T(n) = O(n2) và như vậy ta chưa thu được kết quả gì mới so với phương pháp tính từ nhà trường phổ thông.

Bây giờ ta biến đổi công thức (1) dưới dạng:

XY = AC2n

+ ((A-B)(D-C) + AC + BD)2n/2 + BD

(2) Công thức (2) mặc dù phức tạp hơn (1) nhưng chúng có thể được tính bởi:

- 3 phép nhân n/2-bit: AC, BD và (A-B)(D-C).

- 6 phép +,- các số n/2-bit.

- 2 phép chuyển chữ số (nhân với lũy thừa của 2).

Do vậy với cách tính trên ta có công thức sau tính độ phức tạp của thuật toán này:

T(1) = 1

T(n) = 3T(n/2) + C.n (C-const)

(3) Công thức (3) cho ta

Như vậy ta đã thu được một kết quả mới cho việc thực hiện phép nhân 2 số tự nhiên n-bit với thuật toán mạnh hơn phép nhân bình thường đã học trong nhà trường (4).

Bài toán tạo lịch thi đấu Tennis

Giả sử cần lập một lịch thi đấu Tennis cho n = 2k vận động viên (VĐV). Mỗi vận động viên phải thi đấu với lần lượt n-1 vận động viên khác, mỗi ngày thi đấu 1 trận. Như vậy n-1 là số ngày thi đấu tối thiểu phải có. Chúng ta cần lập lịch thi đấu bằng cách thiết lập ma trận có n hàng, n-1 cột. Giá trị số tại vị trí (i,j) (hàng i, cột j) chỉ ra vận động viên cần thi đấu với vận động viên i trong ngày thứ j.

Sử dụng kỹ thuật Chia để Trị, chúng ta hãy lập lịch thi đấu cho nửa (n/2) số vận động viên đầu tiên. Bằng việc sử dụng lời gọi đệ qui chúng ta đưa bài toán về trường hợp chỉ có 2 VĐV.

Chúng ta minh họa bằng trường hợp n=8. Lịch thi đấu cho 4 người đầu tiên của

Page 11: balo1

danh sách chiếm nửa trái trên của ma trận (4 hàng, 3 cột). Phần nửa trái dưới (4 hàng, 3 cột) của ma trận là lịch thi đấu của 4 VĐV còn lại (từ 5 đến 8). Phần này thu được từ nửa trái trên bằng cách cộng 4 vào mỗi phần tử tương ứng của ma trận. Để điền nốt các phần còn lại của ma trận chúng ta chỉ cần xác định lịch thi đấu giữa các VĐV với số thấp (≤n/2) với các VĐV với số cao (≥n/2). Để làm việc này chúng ta xếp các VĐV từ 1 đến n/2 đấu lần lượt với các VĐV số cao vào ngày 4. Các ngày còn lại thu được từ ngày 4 bằng cách hoán vị vòng quanh các VĐV với số thứ tự cao. Quá trình điền số được mô tả trong hình 2. Các bạn có thể tổng quát quá trình này cho trường hợp tổng quát n=2k bất kỳ. Trả lời kèm trích dẫn 26-02-2012, 12:53 AM #5

fod© Administrator

Ngày tham gia05 - 2011

Bài viết255

Cảm ơn4Được cảm ơn 11 lần trong 9 bài viết

Phương pháp quay lui Phương pháp (hay giải thuật) đệ quy là phương pháp hữu hiệu để giải bài toán liệt kê cấu hình dựa trên nguyên tắc vét cạn 1. Ý tưởng- Quay lui (Back tracking): Theo nguyên tắc vét cạn, nhưng chỉ xét những trường hợp khả quan.

- Dùng để giải bài toán liệt kê các cấu hình:+ Mỗi cấu hình được xây dựng bằng cách xác định từng phần tử+ Mỗi phần tử được chọn bằng cách thử các khả năng có thể.- Tại mỗi bước, nếu có một lựa chọn được chấp thuận thì ghi nhận lại lựa chọn này và tiến hành các bước thử tiếp theo. Còn ngược lại không có lựa chọn nào thích hợp thì quay lại bước trước -> quay lui.

2. Mô hìnhKhông gian nghiệm của bài toán (tập khả năng) D={(x1,x2,…,xn)} gồm các cấu hình liệt kê có dạng (x1, x2, ……, xn ) cần được xây dựng:- Cho x1 nhận lần lượt các giá trị có thể. Với mỗi giá trị thử gán cho x1 thì:- Cho x2 nhận lần lượt các giá trị có thể. Với mỗi giá trị thử gán cho x2 thì xét khả năng chọn x3,…, xn, => cấu hình tìm được (x1, x2, ……, xn ).- Tại mỗi bước i: Xây dựng thành phần xi+ Xác định xi theo khả năng v.

Page 12: balo1

+ Nếu i = n thì ta có được một lời giải, ngược lại thì tiến hành bước i+1 để xác định xi+1.+ Nếu không có một khả năng nào chấp nhận được cho xi thì lại lùi lại bước trước để xác định lại thành phần xi-1.

3. Lược đồ phương pháp

Code:  (Chọn tất cả)

Try(i) ≡ //Sinh thành phần thứ i của cấu hình

for (v thuộc tập khả năng thành phần nghiệm xi)

if ( xi chấp nhận được giá trị v)

xi = v;

;

if(i = n) //đủ n thành phần của cấu hình đã xác định

;

else //lời gọi sinh thành phần tiếp theo của cấu hình

Try (i + 1)

;

endif;

endfor

End.

Ví dụ: Bài toán liệt kê dãy nhị phân độ dài nPhân tích:- Dãy nhị phân (x1x2… xn) trong đó xi =0,1- Dùng giải thuật Try(i) để sinh giá trị xi- Nếu i=n thì in giá trị nghiệm, ngược lại sinh tiếp xi+1 bằng Try(i+1)Lược đồ:

Code:  (Chọn tất cả)

Try(i) ≡

for (v=0..1) //v nhận giá trị 0 hoặc 1

xi = v;

if (i= n) printResult (x);

Page 13: balo1

else Try(i+1) ;

endfor;

End.

Lời gọi ban đầu Try(1)4. Một số bài toán- Tìm chu trình Hamilton của đồ thị- Bài toán người đưa hàng- Bài toán xếp balo- Bài toán tô màu bản đồ- Bài toán xếp quân cờ: Xếp n quân hậu/mã trên bàn cờ n x n sao cho không quân nào khống chế quân nào.- Bài toán tìm đường đi trong mê cung Trả lời kèm trích dẫn 26-02-2012, 12:55 AM #6

fod© Administrator

Ngày tham gia05 - 2011

Bài viết255

Cảm ơn4Được cảm ơn 11 lần trong 9 bài viết

Phương pháp nhánh cận Phương pháp (hay giải thuật) nhánh cận là một trong những phương pháp giải các bài toán liệt kê cấu hình có điều kiện tối ưu. 1. Bài toán tối ưu- Bài toán yêu cầu tìm ra một phương án tốt nhất thỏa mãn một số yêu cầu ràng buộc nào đó – nghiệm của bài toán đạt giá trị max/min trong không gian nghiệm.- Thuộc lĩnh vực Tối ưu toán học hoặc Quy hoạch toán học. Lời giải toán có thể khó => Sự vào cuộc của Tin học- Hai hướng tiếp cận tìm lời giải tối ưu cho bài toán:+ Tìm từng lời giải, khi hoàn tất một lời giải thì so sánh của nó với chi phí tốt nhất hiện có. Nếu tốt hơn thì cập nhật chi phí tốt nhất mới.+ Với mỗi lời giải, khi xây dựng các thành phần nghiệm luôn kiểm tra điều kiện nếu đi tiếp theo hướng này thì có khả năng nhận được lời giải tốt hơn lời giải hiện có không? Nếu không thì thôi không đi theo hướng này nữa. => Nguyên lí nhánh cận (Branch and Bound)

Page 14: balo1

2. Ý tưởng- Nhánh cận (Branch and Bound): Thuật toán tìm lời giải cho các bài toán tối ưu dạng liệt kê cấu hình dựa trên nguyên lí đánh giá nhánh cận.- Nguyên lí đánh giá nhánh cận: Sử dụng các thông tin đã tìm được trong lời giải của bài toán để loại bỏ sớm phương án không dẫn tới lời giải tối ưu- Bản chất:+ Sử dụng phương pháp quay lui nhưng tại mỗi bước đưa thêm thao tác đánh giá giá trị phương án hiện có.+ Nếu đó là phương án tối ưu hoặc có hy vọng trở thành phương án tối ưu (tức là tốt hơn phương án hiện có) thì cập nhật lại phương án tối ưu hoặc đi tiếp theo hướng đó.+ Trong trường hợp ngược lại thì bỏ qua hướng đang xét.3. Mô hình- Không gian của bài toán (tập khả năng) D={(x1,x2,…,xn)} gồm các cấu hình liệt kê có dạng (x1, x2, ……, xn ).- Mỗi cấu hình x sẽ xác định một giá trị hàm chi phí f(x)Nghiệm của bài toán: x= (x1,x2,…,xn) sao cho f(x)= giá trị tối ưu (max/min)- Cho xi nhận lần lượt các giá trị có thể. Với mỗi giá trị thử gán cho xi xét khả năng chọn xi+1, xi+2…- Tại mỗi bước i: Xây dựng thành phần xi- Xác định xi theo khả năng v.+ Tính chi phí lời giải nhận được. Nếu “tốt hơn” lời giải hiện thời thì chấp nhận xi theo khả năng v. Tiếp tục xác định xi+1,…đến khi gặp nghiệm+ Nếu không có một khả năng nào chấp nhận được cho xi hoặc lời giải xấu hơn thì lùi lại bước trước để xác định lại thành phần xi-1.4. Lược đồ

Code:  (Chọn tất cả)

Try(i,S )≡ //Sinh thành phần thứ i của cấu hình với chi phí hiện thời S

for (v thuộc tập khả năng thành phần nghiệm xi)

if( v là chấp nhận được)

T = S + Chi phí nghiệm khi có thêm thành phần v;

if (T tốt hơn Toptimize) //Tốt hơn chi phí tốt nhất hiện có

xi = v ;

;

if( xi là trạng thái kết thúc);

Toptimize = T; //Cập nhật chi phí tốt nhất

else

Page 15: balo1

Try(i+1,T); //sinh thành phần tiếp theo với chi phí hiện thời T

endif;

;

endif;

Ví dụ:Bài toán người bán hàng (Traveling Saleman): Một người bán hàng trên hệ thống n thành phố. Giữa các thành phố có thể có hoặc không các đường nối, mỗi đường nối có chi phí xác định từ trước. Người bán hàng xuất phát từ một thành phố, đi tới tất cả các thành phố khác và mỗi thành phố đi qua một lần và quay trở lại thành phố ban đầu. Hãy xác định một hành trình sao cho tổng chi phí trên đường đi là nhỏ nhất.

Phân tích: Biểu diễn mạng lưới giao thông giữa các thành phố như một đồ thị có trọng số G=(V,E)- Mỗi thành phố là một nút của đồ thị (đánh số 1,..,n)- Mỗi đường đi giữa các thành phố là một cạnh nối giữa các nút của đồ thị, có thể có hướng hoặc vô hướng, trên đó có ghi trọng số là chi phí đường điCác cặp cạnh không có đường đi trọng số là ∞- Đỉnh xuất phát trùng với đỉnh kết thúc- Đường đi tìm được là dãy S=x1,x2,…,xn,x1=S với xi < V, (xi,xi+1) < E, có tổng chi phí nhỏ nhất- Sinh các dãy hoán vị 1..n và tính dãy có chi phí nhỏ nhất:+ Quay lui+ Nhánh cậnTiếp cận theo phương pháp nhánh cận:- Chi phí tốt nhất đã tìm được (BestCost). Ban đầu BestCost= +∞- Tại mỗi bước chọn xi : Chi phí đường đi từ x1 đến xi-1 là C+ Với mỗi khả năng v, tính chi phí C1 = C + chi phí từ xi-1 tới v+ Nếu C1 xấu hơn (lớn hơn) BestCost hoặc không có khả năng nào chấp nhận được cho xi thì lùi lại bước trước để xác định lại thành phần xi-1.+ Nếu C1 tốt hơn (nhỏ hơn) BestCost thì chấp nhận xi theo khả năng v. Tiếp tục xác định xi+1,…+ Đến khi gặp nghiệm (i=n+1 & xi=S):. Cập nhật đường đi tốt nhất hiện tại đã tìm được. Cập nhật giá trị BestCost mới: BestCost = C1- Kết thúc tìm kiếm nếu BestCost= +∞ => không có đường điMô tả dữ liệu- Đánh chỉ số các đỉnh của đồ thị từ 1..n- Biểu diễn đồ thị G bằng ma trận kề M = (cij) cỡ n x n+ cij = cost nếu có cạnh nối đỉnh i với đỉnh j với chi phí cost= ∞ nếu không có đường đi- Mảng: Daqua[1..n] đánh dấu đỉnh i đã được đi qua trên đường đi hay chưa?

Page 16: balo1

+ Daqua[i]= true nếu đỉnh i đã có trên đường đi= false nếu ngược lại, đỉnh i chưa có trên đường đi+ Khởi tạo: Daqua[1..n] = falseThuật toán

Code:  (Chọn tất cả)

Try(i,C)≡ //Sinh thành phần thứ i của cấu hình với chi phí hiện thời C

for (v = 1..n)

if (c[xi-1,v]< ∞)&(not Daqua[v])) )

C1 = C + c[xi-1,v];

if (C1 < BestCost) //Tốt hơn chi phí tốt nhất hiện có

xi = v ;

Daqua[v] = true;

if (i = n+1)&(xi = S)

;

BestCost = C1; //Cập nhật chi phí tốt nhất

else if (i<=n)

Try(i+1,C1); //sinh thành phần tiếp theo với chi phí hiện thời C1

endif;

Daqua[v] = false;

endif;

endif;

End.

5. Các bài toán khác- Xếp balo (Knapsack problem)- Quy hoạch nguyên (Integer programming)- Quy hoạch phi tuyến (Nonlinear programming)- Người bán hàng (Traveling salesman problem - TSP)- Quadratic assignment problem (QAP)- Bài toán thỏa được cực đại (Maximum satisfiability problem - MAX-SAT)- Tìm kiếm láng giềng gần nhất (Nearest neighbor search - NNS)

Page 17: balo1

- Cutting stock problem- False noise analysis (FNA) Trả lời kèm trích dẫn 26-02-2012, 12:58 AM #7

fod© Administrator

Ngày tham gia05 - 2011

Bài viết255

Cảm ơn4Được cảm ơn 11 lần trong 9 bài viết

Phương pháp tham lam Một thuật toán tham ăn luôn luôn lựa chọn giải pháp được đánh giá là là tốt nhất tại thời điểm xét. Tức là lựa chọn giải pháp tối ưu cục bộ và mong muốn rằng lựa chọn này sẽ dẫn đến được giải pháp tối ưu toàn bộ. 1. Ý tưởng- Xây dựng lời giải của bài toán với việc chấp nhận lựa chọn phương án thành phần có vẻ tốt nhất.- Nhận được bài toán gần tối ưu2. Mô hình- Nghiệm của bài toán:x = (x1... xn)- Phân tích: Tại bước thứ i:+ Đã xây dựng được các thành phần nghiệm x1... xi-1

+ Xây dựng thành phần nghiệm thứ xi bằng cách chọn phương án tốt nhất trong các khả năng có thể3. Lược đồ

Code:  (Chọn tất cả)

void Greedy()

{

for(i=1; i<=n; i++)

{

x[i] = select[i] //Luu chon thanh phan tot nhat trong S(i)

}

Page 18: balo1

}

4. Ví dụBài toán BaloCode:  (Chọn tất cả)

#include

#include

#include

/*

BaiToanBaLo

Tim do vat

Laptrinh.vn

Phuong phap tham lam

*/

/*Doc du lieu tu file Balo.txt

Dong thu nhat ghi so do vat (n) va trong luong toi da cua ba lo (m)

n dong tiep theo ghi trong luong va gia tri cua vat

*/

#define max 100

int v[max], w[max];

int daxet[max];

int vi, wi, vmax;

int n, W;

Page 19: balo1

int x[max];

int pa[max];

void DocDL()

{

FILE *f;

f = fopen("BaLo.txt", "r");

if(!f)

{

puts("Loi mo tep");

getch();

exit(0);

}

fscanf(f, "%d", &n);

fscanf(f, "%d", &W);

for(int i=1; i<=n; i++)

{

fscanf(f, "%d", &w[i]);

fscanf(f, "%d", &v[i]);

}

fclose(f);

}

int dovatmax()

{

int chiso = -1;

int i;

for(i=1; i<=n; i++)

Page 20: balo1

if(!daxet[i])

break;

chiso = i;

if(chiso==-1)

return -1;

else

{

for(i=chiso+1; i<=n; i++)

if(v[chiso] chiso = i;

return chiso;

}

}

void Greed_BaLo()

{

int chiso;

int k=1;

int w1 = W;

for(int i=1; i<=n; i++)

if(w1>0)

{

chiso = dovatmax();

if(chiso==-1)

break;

else

if(w1>w[chiso])

{

pa[k] = chiso;

Page 21: balo1

daxet[chiso] = 1;

w1 -= w[chiso];

k++;

}

}

for(int i=1; i printf("%3d", pa[i]);

}

int main()

{

DocDL();

Greed_BaLo();

getch();

}

Trả lời kèm trích dẫn 26-02-2012, 12:59 AM #8

fod© Administrator

Ngày tham gia05 - 2011

Bài viết255

Cảm ơn4Được cảm ơn 11 lần trong 9 bài viết

Phương pháp quy hoạch động Quy hoạch động - giống như phương pháp chia để trị - giải quyết các bài toán bằng cách kết hợp các giải pháp của các bài toán con. Điểm khác biệt là một thuật toán quy hoạch động giải quyết tất cả các bài toán cháu đúng một lần và sau đó ghi kết quả của chúng trong một bảng, như vậy tránh được việc phải tính lại các kết quả khi bài toán cháu được gặp lại.

Page 22: balo1

1. Nguyên lý tối ưu Bellman

- Quy hoạch động là quá trình điểu khiển tối ưu với trạng thái bắt đầu. Mọi trạng thái A bất kỳ thuộc quá trình này cũng tối ưu.- Tư tưởng chính: Thực hiện các bài toán con trước, sử dụng các kết quả này để giải bài toán lớn- Hướng tiếp cận:+ Giải bài toán theo công thức truy hồi+ Giải các bài toán con trước+ Dựa vào bài toán con để giải bài toán lớn hơn cho đến khi gặp bài toán cần giải- Tổng quát:+ Giải bài toán qua N bước+ Giải bài toán sao cho tổng chi phí 1 -> N-1 và từ N-1 -> N là tối ưu

2. Thuật toán quy hoạch động

Bước 1: Xây dựng công thức truy hồiGiả sử: Chia bài toán thành N giai đoạn 1->NXác định F(1) - cơ sở quy nạp- Gọi (i) là hàm số xác định giá trị tối ưu, tính đến giá trị thứ i-> F(n) là nghiệm của bài toán- Đối với bài toán+ Tìm Min: F(N) = Min{F(i), F(j)}i = 1, N-1j = N-1, N+ Tìm Max: F(N) = Max{F(i), F(j)}i = 1, N-1j = N-1, N

Bước 2: Tìm cơ sở quy hoạch độngĐể chia bài toán lớn thành các bài toán con có cùng một cách giải thì phải yêu cầu phải biến đổi một số bài toán con ở trường hợp cơ sở quy nạp bằng cách thêm biến phụ, chính là cơ sở quy hoạch động

Bước 3: Lấp đầy bảng phương án, dựa vào cơ sở quy hoạch động, công thức quy hoạch độngGiải bài toán

Bước 4: Truy vếtTìm nghiệm của bài toán

3. Ví dụ

Bài toán: Dãy con không giảm dài nhấtPhân tích:

Page 23: balo1

- Gọi L[i] là độ dài lớn nhất của dãy con không giảm từ x[i] -> x[n]-> L[1] là nghiệm của bài toán- Để đưa các trường hợp này về cùng một cách giải, ta thêm 2 phần tử+ a[0] = -œ+ a[n+1] = +œThuật toán:Bước 1: Xây dựng công thức truy hồi- Xét tại phần tử thứ i:+ L[i] = 1 nếu a[i] > ... > a[n]+ L[i] = L[jmin] + 1 với jmin = min{j | a[i] < a[j]}- Tổng quát:+ L[i] = max {L[j] với j = i+1, n và a[i] < a[j]}

Bước 2: Cơ sở quy hoạch độngL[n+1] = 1

Bước 3: Tính bảng phương ánCode:  (Chọn tất cả)

void QHD()

{

int i, j;

int jmax, temp;

a[0] = -32766;

a[n+1] = 32767;

L[n+1] = 1;

for(i=n;i>=0; i--)

{

jmax = n+1;

temp = L[n+1];

for(j=i+1; j<=n; j++)

if(a[i] < a[j] && L[j] > temp)

{

temp = L[j];

jmax = j;

Page 24: balo1

}

L[i] = L[jmax]+1;

T[i] = jmax;

}

}

Bước 4: Truy vết- Theo các vết sử dụng mảng T[i] mà ta đã đánh dấu- Bắt đầu từ T[0] ta lần lượt lưu lại T[i] cho tới khi T[i] = n+1