phương pháp nhánh cận,bài toán cái tui,bài toán người đóng thùng,bài toán người du lịch
Trang 1CHƯƠNG 5
BÀI TOÁN TỐI ƯU
1 Giới thiệu bài toán
Chọn cấu hình tối ưu trong số các cấu hình thoả mãn các điều kiện cho trước gọi là bài toán tối ưu Dạng tổng quát của bài toán như sau:
Hàm f(x) gọi là hàm mục tiêu, mỗi phần tử x D gọi là một phương án (phương án chính là cấu hình thoả mãn các điều kiện cho trước), tập D gọi là tập các phương án Phương án x*
D sao cho f(x*) nhỏ nhất (hay lớn nhất) gọi là phương án tối ưu, khi đó giá trị f*=f(x*) gọi là giá trị tối ưu
2 Một số bài toán tối ưu
2.1 Bài toán người du lịch
Có n thành phố, một người du lịch xuất phát từ một thành phố, muốn đi qua n-1 thành phố còn lại, mỗi tp một lần, rồi quay về tp xuất phát Biết chi phí từ tp i đến tp j là Ci,j , hãy tìm hành trình có tổng chi phí là nhỏ nhất
HD:
Gọi x1 là tp xp, mỗi hành trình có dạng: x1-> x2->… ->xn Đặt f(x)=Cx1,x2 + Cx2,x3+…+ Cxn,x1
và gọi D={(x2, ,xn)| (x2, ,xn) là hoán vị của tập {1,2,…,n}\{x1}} Bài toán người du lịch có thể phát biểu ở dạng bài toán tối ưu như sau: Tìm min { f(x) | x=(x2, ,xn) D}
NX: ta có (n-1)! hành trình cần phải xét để tìm ra hành trình tối ưu!
2.2 Bài toán cái túi
Có n vật, vật thứ i có trọng lượng là ai và giá trị sử dụng là ci Một nhà thám hiểm có cái túi với khả năng chứa trọng lượng tối đa là b Hỏi nhà thám hiểm cần đem theo các vật nào để tổng giá trị sử dụng là lớn nhất?
HD:
Một phương án có dạng x=(x1, x2,…, xn) với xi=1 là đồ vật thứ i được đem theo và xi=0 là đồ vật thứ i không được đem theo
Với phương án x giá trị đồ vật mang theo là f(x)=
1
n
i i i
c x
và tổng trọng lượng đồ vật mang
theo là g(x)=
1
n
i i i
a x
Gọi D={(x1, x2,…, xn)| xi=0 hoặc xi=1}, bài toán cái túi có thể phát biểu
ở dạng bài toán tối ưu như sau: Tìm max { f(x) |g(x)b, x D}
NX: ta có 2n phương án cần xét để tìm ra phương án tối ưu
2.3 Bài toán phân công
Có n công việc và n thợ, mỗi thợ thực hiện 1 công việc và 1 công việc chỉ do 1 thợ thực hiện Gọi Cij là chi phí cần trả để thợ i hoàn thành cv j Hãy tìm cách thuê sao cho tổng chi phí thuê là nhỏ nhất
Tìm giá trị nhỏ nhất (hay lớn nhất) của hàm f(x); với x D (D là tập hữu hạn)
Trang 2HD:
Gọi P={1,2,…,m} là tập các chỉ số của thợ Tập các phương án là: D={x=(x1,…,xn) | x là một hoán vị của P} Chi phí với phương án x D là: F(x)=Cx1,1+…+Cxn,n Bài toán phân công có thể phát biểu ở dạng bài toán tối ưu như sau: Tìm min { F(x) | x D}
2.4 Bài toán cho thuê máy
Một ông chủ có một cái máy để cho thuê Đầu tháng ông ta nhận được yêu cầu thuê máy của
m khách hàng Mỗi khách hàng i sẽ cho biết tập Ni các ngày trong tháng cần sử dụng máy Ông chủ có quyền từ chối hoặc nếu nhận thì phải bố trí máy phục vụ khách hàng i đúng những ngày mà khách hàng này yêu cầu Hỏi rằng ông chủ phải tiếp nhận các yêu cầu của khách như thế nào để cho tổng số ngày sử dụng máy là lớn nhất? Biết rằng mỗi ngày máy chỉ
có thể cho 1 người thuê
HD:
Gọi P={1,2,…,m} là tập chỉ số khách hàng yêu cầu thuê máy Khi đó tập các phương án cho thuê máy là: D={KP : Ni Nj = , i,,j K, ij } và tổng số ngày sử dụng theo phương
án KD là
k K
N
Bài toán cho thuê máy có thể phát biểu ở dạng bài toán tối ưu như sau: Tìm max {F(K)|
KD}
2.5 Bài toán đóng thùng
Tìm cách xếp n vật có trọng lượng là w1,…,wn (wi b) vào các cái thùng sao cho số thùng
sử dụng là ít nhất Biết rằng mỗi thùng có khả năng chứa một trọng lượng là b
HD:
Gọi P={1,2,…,n} là chỉ số các vật và Ni P là tập các chỉ số của vật được cất vào hộp i D={x=(N1,…,Nm) | Ni P; w b
i
N j
j
m
i
i
1
|
| ; Ni Nj =, ij , } là tập các phương án Bài toán đóng thùng có thể phát biểu ở dạng bài toán tối ưu như sau: Tìm min {f(x)=m| xD }
3 Các thuật toán duyệt
3.1 Thuật toán duyệt toàn bộ
Dùng các thuật toán liệt kê, duyệt tất cả các phương án có thể xảy ra, tính giá trị hàm mục tiêu của mỗi phương án So sánh các giá trị hàm mục tiêu để tìm ra phương án tối ưu Nếu số phương án qúa nhiều thì thuật toán sẽ thực thi rất chậm đến nỗi không thể chấp nhận được
Ta cần tận dụng những thông tin đã tìm được để loại bỏ bớt những phương án không tối ưu
và dẫn đến thuật toán nhánh cận
3.2 Thuật toán nhánh cận
3.2.1 Ý tưởng:
Sử dụng thuật toán quay lui và dùng hàm “cận dưới” g để loại bớt các phương án không thể
là phương án tối ưu
Trang 3- Xét phương án hoàn chỉnh x =(x1,…,xk,…xn); a=(a1,…,ak) với ai=xi, gọi là phương án bộ phận cấp k (kn)
- G/s đã xây dựng được hàm g (hàm tính cận dưới) như sau: với mọi phương án bộ phận (a1,…,ak) và với mọi k=1,…,n
g(a 1 , …,a k ) min {f(x): x D, x 1 = a 1 , …,x k = a k }
Gọi x là phương án hoàn chỉnh tốt nhất hiện có và f = f(x) gọi là kỷ lục hiện tại Nếu một
phương án bộ phận a=(a1,…,ak) nào đó mà có g(a 1 , …,a k ) > f
f< g(a1, …,ak) min {f(x): x D, x1 = a1, …,xk = ak}
các phương án hoàn chỉnh x=(x1,…,xn) phát triển từ phương án bộ phận a=(a1, …,ak) đều
có hàm mục tiêu f(x) > kỷ lục hiện tại f, nên ta loại tất cả các phương án hoàn chỉnh x phát triển từ phương án bộ phận a
* Ghi chú:
Nếu bài toán tối ưu là tìm max {f(x): x D} thì cần xây dựng được hàm g (hàm tính cận trên) sao cho:
g(a 1 , …,a k ) max {f(x): x D, x 1 = a 1 , …,x k = a k }
và nếu một phương án bộ phận a=(a1,…,ak) nào đó mà có g(a 1 , …,a k ) < f
f> g(a1, …,ak) max {f(x): x D, x1 = a1, …,xk = ak}
các phương án hoàn chỉnh x=(x1,…,xn) phát triển từ phương án bộ phận a=(a1, …,ak) đều
có hàm mục tiêu f(x) < kỷ lục f, nên ta loại tất cả các phương án hoàn chỉnh x phát triển từ phương án bộ phận a
3.2.2 Cài đặt: (tìm min)
void Try(int i) //xac dinh xi
{
for (xét tất cả các knj của xi)
{
if (chấp nhận knj ) //nếu chấp nhận khả năng j {
ghi nhận việc đã chọn knj // (*) :lệnh này có thể không có
xi = knj ; //lưu kn j vào xi
if (i==n) //nếu đã xác định đủ n thành phần cập nhật kỷ lục nếu kỷ lục mới nhỏ hơn kỷ lục hiện tại;
else
if (g(a 1 , …,a k ) f) //tim max thi g(a 1 , …,a k ) f
Try(i+1); // xác định xi+1
ghi nhận việc bỏ chọn knj // lệnh này không có nếu (*) không có }
}
}
void nhanhcan()
{
Trang 4f= +; //nếu biết một phương án x nào đó thì có thể đặt f = f(x).Nếu tìm max thì
f= -
Try(1); //tim x1
}
* Nhận xét:
- Nếu không kiểm tra điều kiện g(a1, …,ak) f thì thuật toán trở thành thuật toán duyệt toàn bộ các phương án
- Hàm g(x) cần xây dựng đơn giản để việc tính g được nhanh chóng
3.2.3 Giải một số bài toán bằng thuật toán nhánh cận:
a) Bài toán cái túi:
Có n loại đồ vật, loại đồ vật thứ i có trọng lượng ai và giá trị sử dụng ci Cần chất các đồ vật này vào một cái túi có trọng lượng là b, sao cho tổng giá trị sử dụng của các đồ vật chất trong túi là lớn nhất
HD:
Gọi xi là số vật loại i được cất vào túi D={x =(x1,…,xn):
1
n
i i i
a x
b, xi Z+, i=1,…,n} là tập
các phương án Hàm mục tiêu f(x) =
1
n
i i i
c x
Bài toán cái túi có dạng: Tìm max {f(x) :x D} (1)
Không giảm tính tổng quát, ta giả thiết các loại đồ vật được đánh số sao cho thoả mãn:
c1/a1 c2/a2 … cn/an
Mệnh đề:
Phương án tối ưu của (1) với biến liên tục (xiR+) là x =(x1,x2,…,xn)=(b/a1,0,…,0) và giá trị tối ưu là (c1/a1)b
Chứng minh:
ci/ai c1/a1 , i=1,…,n cixi (c1/a1)aixi f(x) =
1
n
i i i
c x
1
( / )
n
i i i
c a a x
1
n
i i i
a x
(c1/a1)b
Bây giờ giả sử đã có phương án bộ phận cấp k: (u1, …,uk) Khi đó gía trị sử dụng trong túi là:
k
=c1u1+…+ckuk
và trọng lượng còn lại của cái túi là
bk= b – a1u1 – a2u2 – … – akuk
Ta có:
max {f(x)=
1
n
i i i
c x
: x D, xi = ui, i = 1,…,k} = max { k+
1
n
i i
i k
c x
1
n
i i
i k
a x
bk, xi Z+}
k+ max {
1
n
i i
i k
c x
1
n
i i
i k
a x
bk, xi 0}
Trang 5= k+ (ck+1/ak+1)bk
chọn hàm tính cận trên là g(x)= k+ (ck+1/ak+1)bk
* Nhận xét:
ak+1 xk+1 +….+ an xn bk xk+1 +(ak+2 xk+2….+ an xn)/ak+1 bk / ak+1 xk+1 bk / ak+1
các khả năng có thể chọn cho xk+1 là 0,…, bk / ak+1
các khả năng có thể chọn cho xk là 0,…, bk-1 / ak
Do mệnh đề đã cm, ta chọn các khả năng theo thứ tự giảm dần
Ví dụ:
Giải bài toán cái túi sau:
Tìm max của : f(x)=10x1+5x2+3x3+6x4
Thoả điều kiện: 5x1+3x2+2x3+4x4 8
xi Z+
HD: gọi giá trị của các đồ vật trong túi, w là trọng lượng còn lại, g= + (ck+1/ak+1)w 10/55/33/26/4
f= -
*k=1: x1 b0 / a1 = 8/5 x1=0,1
x1=1 : =10, w=8-5=3, g=10+5/3 x 3=15
x1=0 : =0, w=8, g=0+5/3 x 8=40/3 < f=15 (*) nên loại
*k=2: x2 b1 / a2 = 3/3=1 x2=0,1
x2=1 : =15, w=3-3=0, g=15+3/2 x 0=15
x2=0 : =10, w=3, g=10+3/2 x 3=29/2=14.5< f=15 (*) nên loại
*k=3: x3 b2 / a3 = 0/2=0 x3=0
x3=0 : =15, w=0, g=15+6/4 x 0=15
*k=4: x4 b3 / a4 = 0/4=0 x4=0
x4=0 : =15, w=0
pán x=(1,1,0,0) f=15 (*)
* Cài đặt
/* Bai toan cai tui */
#include <stdio.h>
#include <conio.h>
//c[i]: gia tri su dung cua vat loai i, a[i]: trong luong vat loai i
float c[100],a[100];
//x: chua phuong an trung gian, xopt: phuong an toi uu
int x[100],xopt[100];
Trang 6//d: so thu tu cua loai i la d[i]
int d[100],n;//n la so loai vat
/*w la trong luong toi da tui co the chua,
weight: trong luong hien tai cua tui
fopt: gia tri hien tai lon nhat cua tui
cost: gia tri hien tai
*/
float w,weight,fopt,cost;
/* file caitui.dat co dang sau:
4 8 //n=4, b=8
5 3 2 4 //a: trong luong moi loai vat
10 5 3 6 //c: gia tri moi loai vat
*/
void ReadFile()
{
FILE *fin;
int i;
if ((fin = fopen("caitui.dat", "rt"))== NULL) {
printf("Khong tim thay file caitui.dat.\n"); return ;
}
fscanf(fin,"%d%f",&n,&w);
for (i=0;i<n;i++) fscanf(fin,"%f",&a[i]);
for (i=0;i<n;i++) fscanf(fin,"%f",&c[i]);
fclose(fin);
printf("\nSo loai vat:%d",n);
printf("\nTrong luong cai tui:%0.0f",w);
printf("\nTrong luong cac loai vat:");
for (i=0;i<n;i++) printf("%0.0f ",a[i]);
printf("\nGia tri cac loai vat:");
for (i=0;i<n;i++) printf("%0.0f ",c[i]);
}
void Init()
{
int i,j,k,t;
fopt=0;// gia tri lon nhat =0
weight=0;//trong luong ban dau cua tui=0; for (i=0; i<n; i++)
d[i]=i+1;
for (i=0; i<n-1; i++)
{
k=i;
for (j=i+1; j<n; j++)
if (c[j]/a[j]>c[k]/a[k]) k=j;
Trang 7if (k!=i) {
t=a[i];a[i]=a[k];a[k]=t;
t=c[i];c[i]=c[k];c[k]=t;
t=d[i];d[i]=d[k];d[k]=t;
} }
}
void KyLuc()
{
if (cost>fopt)
{
for (int i=0; i<n; i++) {
xopt[i]=x[i]; fopt=cost;
} }
}
void Try(int i)
{
int j,t;
t=(w-weight)/a[i];//w: suc chua cua tui
for (j=t; j>=0; j )
{
x[i]=j;
weight=weight+a[i]*x[i];//weight:trong luong tui cost=cost+c[i]*x[i];//cost: gia tri tui
if (i==n-1) KyLuc();
else
if (cost+c[i+1]*(w-weight)/a[i+1]>fopt) Try(i+1); weight=weight-a[i]*x[i];
cost=cost-c[i]*x[i];
}
}
void InKq()
{
printf("\nTong gia tri do vat:%0.0f",fopt);
for (int i=0; i<n; i++)
printf("\nso luong do vat loai %d la %d",d[i],xopt[i]);
}
void main()
{
clrscr();
ReadFile();
Trang 8Init();
Try(0);
InKq();
getch();
}
b) Bài toán người du lịch:
Cố định thành phố xuất phát là T1 Bài toán người du lịch trở thành bài toán :
Tìm min { f(x2,…,xn)=c[1,x2]+c[x2,x3]+…+c[xn-1,xn]+c[xn,1] | (x2,…,xn) là hoán vị của {2,…,n} }
G/s đã đi qua k thành phố: T1 > T(a1) >… >T(ak) ứng với phương án bộ phận (a1, a2,…,
ak)
và chi phí cho hành trình bộ phận này là =c[1,a2]+c[a2,a3]+…+c[ak-1,ak] Để phát triển thành hành trình đầy đủ, còn phải đi qua n-k+1 đoạn đường nữa
Ta có:
+(n-k+1)cmin min {f(x): x1 = a1, …,xk = ak}
nên có thể chọn g(a 1 ,…,a k )= +(n-k+1)c min
Ví dụ: giải bài toán người du lịch với ma trận chi phí sau:
C=
HD: cmin=3 Gọi chi phí hành trình bộ phận, cận dưới g= + 3(n-k+1)
f= +
*k=2: x2=2,3,4,5
x2=2 : =3, g=3+(5-2+1) x 3=15
x2=3 : =14, g=14+(5-2+1) x 3=26
x2=4 : =18, g=18+(5-2+1) x 3=30
x2=5 : =15, g=15+(5-2+1) x 3=27
vv…
* Cài đặt
/*
bai toan nguoi du lich
*/
#include <stdio.h>
#include <conio.h>
#include <values.h>
Trang 9int n;//so thanh pho
int c[100][100];//ma tran chi phi
int cmin;//chi phi nho nhat trong ma tran chi phi
int tp[100];//tp[i]=0:tp i da di qua, =1 chua di qua
int cost;//chi phi bo phan
int fopt; //gia tri ky luc, gia tri cuoi cung la gia tri toi uu int x[100];//la mot phuong an
int xopt[100];//la phuong an toi uu
void ReadFile()
{
FILE *fin;
int i,j;
if ((fin = fopen("dulich.dat", "rt"))== NULL)
{
printf("Khong tim thay file dulich.dat.\n"); return ;
}
//doc so thanh pho
fscanf(fin,"%d",&n);
//doc ma tran chi phi
for (i=0;i<n;i++)
for (j=0;j<n;j++)
fscanf(fin,"%d",&c[i][j]);
fclose(fin);
//xuat thong tin de kiem tra
printf("\nSo thanh pho:%d",n);
printf("\nMa tran chi phi:");
for (i=0;i<n;i++)
{
printf("\n");
for (j=0;j<n;j++)
printf("%3d",c[i][j]);
}
}
void Init()
{
int i,j;
cmin=MAXINT;//so kieu int lon nhat
for (i=0;i<n;i++)
{
tp[i]=1;//tat ca tp i deu chua di qua for (j=0;j<n;j++)//tim cmin
if ( (i!=j)&&(c[i][j]<cmin)) cmin=c[i][j]; }
Trang 10fopt=MAXINT;//ky luc ban dau
cost=0;//chi phi bo phan
x[0]=0;//tp bat dau di la tp 0
}
void CNKL() //cap nhat ky luc
{
int i,s;
s=cost+c[x[n-1]][x[0]];//cong them chi phi tro ve tp dau
if (s<fopt)//neu chi phi cua hanh trinh nay < ky luc
{
fopt=s;//ghi nhan ky luc moi for (i=0;i<n;i++) xopt[i]=x[i]; //ghi nhan hanh trinh moi }
}
void Try(int i)
{
int j;
for (j=1;j<n;j++)
if (tp[j]) {
x[i]=j;
tp[j]=0;//tp j da duoc chon cost=cost+c[x[i-1]][x[i]];//chi phi bo phan
if (i==n-1) CNKL();
else
if (cost+(n-i+1)*cmin<=fopt) Try(i+1); tp[j]=1;
cost=cost-c[x[i-1]][x[i]];
} }
void Result()
{
int i;
printf("\n\nChi phi nho nhat:%d",fopt);
printf("\nHanh trinh co chi phi nho nhat:\n");
for (i=0;i<n;i++)
printf ("%d -> ",xopt[i]+1);
printf ("%d",xopt[0]+1);
}
void main()
{
clrscr();
ReadFile();
Init();
Try(1);
Trang 11Result();
getch();
}
/*
vi du ve file duclich.dat
6
Co ds la:
Chi phi nho nhat:104
Hanh trinh co chi phi nho nhat:
1->4->6->3->2->5->1
*/