Cong ty Cong Nghe Tin hoc Nha truong http://www.vnschool.net
Thuật toán: Heuristic bằng số thực
04/08/2010

Trong tin học, các bài toán liên quan đến số thực thường là các bài toán khá phức tạp trong việc xử lí và thao tác. Ví dụ: Nhân chia số thực chậm hơn rất nhiều so với số nguyên, rất khó kiểm soát điều kiện bằng nhau, hay lỗi thường gặp là chia cho 0.
Ví dụ bạn sẽ nhận được 1 vòng lặp vô hạn nếu bạn viết :


x:=0;

while x<>1 do x:=x+0.1;

trong khi bạn nghĩ rằng nó sẽ kết thúc sau 10 bước lặp.

Tuy vậy, ta có thể sử dụng chính những tính chất này của số thực để áp dụng vào 1 số bài toán như là 1 cách Heuristic cho hiệu quả rất tốt.

Heuristic số thực sử dụng 1 số tính chất của số thực : Rất khó tạo ra được 2 số thực bằng nhau bằng và 2 tổng các số thực bằng nhau bằng hàm RanDom . Vì các test thực tế hay do ban giám khảo tạo ra hầu hết đều dùng hàm RanDom nên khả năng có 2 số thực ( hay 2 tổng ) bằng nhau trong bộ test là rất khó.


Bài 1 : CIRCLE

Trên mặt phẳng toạ độ cho N điểm, không có 3 điểm nào thẳng hàng. Hãy chọn 3 điểm trong N điểm này sao cho đường tròn qua 3 điểm được chọn sẽ chứa bên trong nó đúng K-3 điểm khác.

Dữ liệu : vào từ file CIRCLE.IN
Dòng đầu chứa 2 số nguyên N và K ( 3<=K<=N<=10000);

N dòng theo tiếp theo, dòng thứ i chứa toạ độ điểm thứ i là cặp số thực xi,yi thể hiện hoành độ và tung độ của điểm thứ i.( giá trị tuyệt đối của các toạ độ không qúa 1000)
Kết quả ghi ra file văn bản CIRCLE.OUT chỉ 1 dòng gồm 3 số thể hiện số hiệu 3 đỉnh được chọn.

IN

5 4

0 0

2 0

0 3

2 1

2 5

OUT

1 2 3

Bài này có thuật toán đơn giản sau :

Lần lượt chọn 2 điểm X,Y. Với các điểm Ai còn lại ta xét góc XAiY, đặt G[i]=XAiY. G[i] ta có thể tính bằng hàm cosin nhưng theo tôi ta nên dùng hàm Theta thì hơn. Hàm Theta tính như sau :

function theta(x1, y1, x2, y2 : real) : real;

{ham nay xep cac diem theo thu tu nhu xep theo arctang cua goc
tao boi 2 diem (x1,y1) va (x2,y2) tao voi duong nam ngang}

var dx, dy, t : real;

begin

dx:=x2-x1; dy:=y2-y1;

if (dx = 0) and (dy = 0) then

t:=0

else

t:=dy/(abs(dx)+abs(dy));

if dx < 0 then

t:=2 - t

else

if dy < 0 then

t:=4 + t;

theta:=t;

end;

Để tìm hiểu kĩ hơn về hàm này, các bạn có thể đọc trong cuốn “Cẩm Năng Thuật Toán”.


Ta sử dụng 1 định lí sau:

với A1,A2,A3 cùng nằm trên 1 nửa mặt phẳng ta luôn luôn có các góc A1
Thế nhưng độ phức tạp của thuật toán trên còn rất lớn. Vì thế, ta sẽ sử dụng tính chất Heuristic số thực ở đây.

Ta biết, các góc G[i] phải là các số thực. Nên việc G[K-3]=G[K-2](hoặc G[K-2]=G[K-1]) là rất khó xảy ra, vì thế thực sự ta cũng chỉ cần đến 1 lần xét điểm X và Y.Mặt khác, ta thấy tính chất trên chỉ đúng khi các điểm còn lại cùng nằm trên 1 nửa mặt phẳng bờ là XY nên ta sẽ lựa chọn XY là 1 cạnh của bao lồi tập các điểm. Để đơn giản, ta chọn X là điểm có tung độ thấp nhất. Điểm Y là điểm tiếp theo trên bao lồi.

Với cách chọn trên, độ phức tạp thuật toán là NlogN chấp nhận được.


Ta xét tiếp ví dụ thứ 2 mà trong đó ứng dụng của Heuristic số thực sẽ hiệu quả hơn rất nhiều.


Bài 2: CANDLES

Liza tròn n tuổi, vì vậy trên bánh ga tô hình tròn bán kính r có cắm n ngọn nến. Mẹ Liza cắt bánh bằng m nhát cắt thẳng dạng một dây trương cung trên đường tròn. Mỗi người khách lấy một miếng tạo ra sau khi cắt.

Các đường cắt không đi qua chổ cắm nến. Liza băn khoăn, không biết có ai trong số khách của mình lấy được miếng có hơn một ngọn nến hay không.


Yêu cầu: Cho n, m, r, toạ độ (xi,y i) của các ngọn nến (0 < n ≤ 10 000, 0 ≤ m ≤ 1 000, 1 ≤ r ≤ 2 000, i = 1  n) và các hệ số aj, bj, cj của phương trình đường thẳng ajx+bjy+cj = 0 các lát cắt. Các hệ số đều có giá trị tuyệt đối không vượt quá 10 000. Tất cả các đại lượng đã cho đều nguyên, không có 2 lát cắt trùng nhau, không có 2 ngọn nến cắm cùng một chổ. Nếu có người lấy được một miếng bánh có hơn một ngọn nến thì đưa ra thông báo YES, trong trường hợp ngược lại – đưa ra thông báo NO.

Dữ liệu: Vào từ file văn bản CANDLES.INP:

• Dòng đầu tiên chứa 3 số nguyên n m r,

• n dòng sau: mỗi dòng chứa 2 số nguyên xi yi,

• m dòng cuối cùng: mỗi dòng chứa 3 số nguyên aj bj cj.


Kết quả: Đưa ra file văn bản CANDLES.OUT thông báo tương ứng.

Ví dụ:


Ta có 1 ý tưởng như sau cho bài toán này :


2 ngọn nến sẽ cùng nằm trên 1 miếng bánh khi và chỉ khi 2 ngọn nến cùng nằm về 1 phía đối với tất cả các đường thẳng. Vì ngọn nến không thuộc đường thẳng nào nên nó có 2 vị trí tương đối đối với 1 đường thẳng. Đặt 2 vị trí đó với 2 giá trị -1 và 1.

Nếu áp dụng ý tưởng này, ta sẽ 1 mảng A có kích thước NxM với A[i,j]=-1 nếu điểm i nằm về phía âm của đường thẳng thứ j, A[i,j]=1 nếu ngược lại. Sau đó ta duyệt lại xem có 2 điểm nào trùng nhau không. Ta sẽ dùng xử lí bít tại đây. Thế nhưng cả độ phức tạp thuật toán lẫn độ phức tạp bộ nhớ đều không thể chấp nhận được.


Bây giờ ta sử dụng Heuristic số thực tại đây như sau :

Gán cho mỗi đường thẳng 1 giá trị RanDom số thực Ri. Sau đó ta thực hiện : Nếu điểm M nằm phía âm của đường thẳng Di thì Gán trọng số của điểm M là Tm:=Tm-Ri, ngược lại Tm:=Tm+Ri.

Dễ thấy, nếu 2 ngọn nến cùng 1 miếng thì 2 giá trị R của chúng phải bằng nhau. Vì mảng R ta dùng hàm RanDom nên khả năng bị trùng nhau là rất thấp. Như thế ta có thể coi mỗi miếng bánh có 1 giá trị là R riêng biệt.

Để tìm xem có 2 giá trị R nào trùng nhau không, ta có thể sắp xếp. Sau đó kiểm tra 2 giá trị liên tiếp nhau có bằng nhau không.

Với cách này, độ phức tạp thuật toán là NxM, độ phức tạp bộ nhớ là N+M có thể chấp nhận được.

Có 1 thuật toán chuẩn có độ phức tạp NxM ( mỗi lần xét 1 đường thẳng lại chia tập các điểm ra thành 2 tập ) nhưng khi bạn cần 1 thuật toán chấp nhận được về độ chính xác, cài đặt đơn giản, nhanh chóng thì thuật toán Heuristic bằng số thực là 1 sự lựa chọn rất thích hợp. Cài đặt 2 bài toán trên rất đơn giản, có lẽ ngay cả với người mới học lập trình cũng mất không quá 20 phút, vì thế tôi không các bạn hãy tự làm lấy.Bạn thấy đấy, số thực đâu có đáng ghét phải không.

Lưu Tuấn Anh SN :27/12/217 phố Lê Đại Hành thành phố Thái Bình
URL của bài viết này::http://www.vnschool.net/modules.php?name=News&file=article&sid=4307
© Cong ty Cong Nghe Tin hoc Nha truong contact: sales@schoolnet.vn