PDA

View Full Version : Vẽ đồ thị hàm số


pacific_boy571
21-05-2005, 01:34 PM
Pascal không cung cấp một công cụ nào để vẽ đồ thị hàm số nên chúng ta sẽ phải xây dựng một chương trình vẽ đồ thị hàm số dựa vào các thủ tục và hàm đồ hoạ có sẵn của Pascal.

Đầu tiên là phải xác định một hệ toạ độ chuẩn để biểu diễn hàm số: Gốc toạ độ (0.0) trong chế độ đồ hoạ thường là góc trái trên của màn hình, do vậy ta phải dùng thủ tục SetViewPort(x1,y1,x2,y2: Integer; Clip: Boolean)
với x1,y1 là toạ độ tâm màn hình để chuyển gốc toạ độ (0,0) về tâm màn hình (x1:=GetmaxX div 2, y1 := GetmaxY div 2);
x2,y2 là toạ độ góc phi dưới của màn hình ( x2 := GetmaxX; y2 := GetmaxY);
tham số Clip là ClipOff để cho phép vẽ ra ngoài viewport. Như vậy ta thấy trục Oy của hệ toạ độ mới trong chế độ đồ hoạ ngược với hệ toạ độ Đêcac bình thường, do đó khi vẽ đồ thị ta sẽ đảo ngược lại toạ độ y để có được đồ thị đúng. Ta phải quy đổi giá trị x, y của hàm số y = f(x) thành toạ độ các điểm trên màn hình bằng cách nhân x, y với tỉ lệ trục Ox và Oy, ví dụ : x*20, y*20 tức là 20 điểm ảnh (pixel) trên màn hình tương ứng với 1 đơn vị trên đồ thị hàm số. Để vẽ đồ thị hàm số ta dùng thủ tục vẽ một điểm ảnh:
PutPixel(x,y: Integer; Color: Word)
trong đó y là hàm số của x: y = f(x) và đặt thủ tục này trong một vòng lặp While do để lần lượt vẽ từng điểm của đồ thị ứng với mỗi giá trị của x, nhưng vì tốc độ tính toán của máy rất nhanh nên ta có cảm giác đồ thị được vẽ ngay lập tức. Thuật toán cụ thể như sau:
Procedure VeDoThiHamSo(các tham số của hàm số:Real);
Var các biến trung gian: Real;
Begin
cho x giá trị ban đầu; (giá trị nhỏ nhất của x trên trục toạ độ trên màn hình)
While do (giá trị lớn nhất của x trên trục toạ độ trên màn hình)
Begin
tăng giá trị của x; (x := x + k, với k càng nhỏ thì đồ thị càng mịn, tối thiểu cho k := 0.001)
định nghĩa hàm y theo x; (y = f(x))
If then (giới hạn toạ độ y nhìn thấy được trên màn hình)
PutPixel(Round(x*),Round(y*),màu);
(Vì toạ độ của một điểm bất kì trong chế độ đồ hoạ mang giá trị nguyên nên phải dùng hàm Round để làm tròn)
lưu giá trị của x qua mỗi vòng lặp bằng biến trung gian;
End;
End;

pacific_boy571
21-05-2005, 01:36 PM
Dựa vào thuật toán trên, trong bài viết này tôi sẽ trình bày cách vẽ đồ thị của các hàm số quen thuộc trong chương trình phổ thông:
+ Đường thẳng, đường tròn, elip (dùng các thủ tục Line, Circle, Ellipse).
+ Hypebol, parabol, các hàm đa thức (bậc hai, bậc ba, bậc bốn), hàm phân thức, hàm lượng giác (dùng thủ tục PutPixel).
Sau đây là toàn bộ chương trình:
Program Ve_Do_Thi_Ham_So;
Uses Crt, Graph;
Var gd,gm,x,y,i:Integer;
tlX,tlY:Byte;
(* Khởi động chế độ đồ hoạ *)
Procedure KhoiTao;
Begin
gd := Detect;
initGraph(gd,gm,'D:BPbgí); (* Đường dẫn cho trình điều khiển đồ hoạ của bạn *)
If GraphResult <> grOK then Halt(1);
x := GetMaxX div 2; y := GetMaxY div 2;
End;
(* Đặt tỉ lệ giữa trục Ox và Oy *)
Procedure Tile(tileX,tileY:Byte);
Var i:integer;
Begin
SetColor(LightBlue);
For i := 1 to x div tileX do
Begin
Line(-TileX*i,2-y,-TileX*i,y-2);
Line(TileX*i,2-y,TileX*i,y-2);
End;
For i := 1 to y div tileY do
Begin
Line(2-x,-TileY*i,x-2,-TileY*i);
Line(2-x,TileY*i,x-2,TileY*i);
End;
End;
(* Vẽ hệ trục tọa độ *)
Procedure HeToaDo(tileX,tileY:Byte);
Begin
SetFillStyle(SolidFill,Blue);
SetViewPort(x, y, GetmaxX, GetMaxY,ClipOff);
Bar3d(-x,-y,x,y,0,TopOff);
tlX := tileX; tlY := tileY;
Tile(tileX,tileY);
SetColor(Yellow);
Line(0,10-y, 0,y-1);
Line(1-x,0,x-10,0);
SetTextStyle(DefaultFont,HorizDir,1);
SetTextJustify(CenterText,CenterText);
OutTextXY(0,15-y,#24);
OutTextXY(x-10,0,#26);
OutTextXY(10,10,'O');
OutTextXY(10,-y+20,'Y');
OutTextXY(x-20,10,'X');
End;
(* 1. Vẽ đường thẳng: ax + by + c = 0 *)
Procedure VeDuongThang(a,b,c:Real);
var x1,x2,y1,y2:real;
Begin
If a = 0 then
begin
y1 := c/b;
Line(-x,Round(y1*tlY),x,Round(y1*tlY));
end
else
if b = 0 then
begin
x1 := -c/a;
Line(Round(x1*tlX),-y,Round(x1*tlX),y);
end
else
begin
x1 := (-b*(5-y)-c)/a;
x2 := (-b*(y-5)-c)/a;
y1 := (-a*x1-c)/(-B);
y2 := (-a*x2-c)/(-B);
Line(Round(x1*tlX),Round(y1*tlY),Round(x2*tlX),Rou nd(y2*tlY));
end;
end;
(* 2. Vẽ đường tròn: x2 + y2 + ax + by + c = 0 *)
Procedure VeDuongTron(a,b,c:Real);
Var i1,i2,r:real;
begin
if tlX <> tlY then tlY := tlX;
(* Tính bán kính *)
r := sqrt((a/2)*(a/2) +(b/2)*(b/2) -c);
(* Tìm tâm đường tròn *)
i1 := -a/2;
i2 := b/2;
(* Vẽ đường tròn *)
Circle(Round(i1*tlX),Round(i2*tlX),Round(r*tlX));
(* Vẽ tâm đường tròn *)
PutPixel(Round(i1*tlX),Round(i2*tlX),Yellow);
end;
(* 3. Vẽ Elip: x2/a2 + y2/b2 = 1 *)
Procedure VeElip(a,b:real);
var c:real;
begin
(* Vẽ Elip *)
Ellipse(0,0,0,360,Round(a*tlX),Round(b*tlY));
(* Vẽ 2 tiêu điểm F1 và F2*)
if a > b then
begin
c := sqrt(a*a-b*B);
Line(Round(c*tlX),-2,Round(c*tlX),2);
OutTextXY(Round&copy;*tlX,10,'F1');
Line(-Round(c*tlX),-2,-Round(c*tlX),2);
OutTextXY(-Round(c*tlX),10,'F2');
end;
if a < b then
begin
c := sqrt(b*b-a*a);
Line(-2,Round(c*tlY),2,Round(c*tlY));
OutTextXY(15,Round(c*tlY),'F1');
Line(-2,-Round(c*tlY),2,-Round(c*tlY));
OutTextXY(15,-Round(c*tlY),'F2');
end;
end;
(* 4. Vẽ Hypebol: x2/a2 - y2/b2 = 1 *)
Procedure VeHypebol(a,b:integer);
var x1,y1,z:real;
begin
y1 := -y div tlX;
(* Vẽ 2 tiệm cận xiên của hypebol *)
SetColor(White);
VeDuongThang(b/a,-1,0);
VeDuongThang(-b/a,-1,0);
{Ve hai tieu diem F1 va F2}
z := sqrt(a*a+b*B);
Line(Round(z*tlX),2,Round(z*tlX),-2);
OutTextXY(Round(z*tlX), 15,'F1');
Line(-Round(z*tlX),2,-Round(z*tlX),-2);
OutTextXY(-Round(z*tlX), 15,'F2');
(* Vẽ hai nhánh của hypebol *)
while y1 < y div tlY do
begin
y1 := y1 + 0.05;
x1 := (a*sqrt(y1*y1+b*B))/b;
PutPixel(Round(x1*tlX),-Round(y1*tlX),Yellow);
x1 := (-a*sqrt(y1*y1+b*B))/b;
PutPixel(Round(x1*tlX),-Round(y1*tlX),Yellow);
z := y1; y1 := z;
end;
end;
(* 5. Vẽ Parabol: y2 = ax (a = 2p), còn parabol x2 = ay (a = 2p) là một trường hợp riêng của hàm bậc hai y = ax2 + bx + c *)
Procedure VeParabol(a:real);
var x1,y1,z:real;
begin
y1 := -y div tlY;
while y1 < y div tlY do
begin
y1 := y1 + 0.01;
x1 := (y1*y1)/a;
PutPixel(Round(x1*tlX),-Round(y1*tlY),Yellow);
z := y1; y1 := z;
end;
(* Vẽ đường chuẩn *)
SetColor(white);
VeDuongThang(1,0,a/4);
(* Vẽ tiêu điểm *)
Line(Round((a/4)*tlX),2,Round((a/4)*tlX),-2);
OutTextXY(Round((a/4)*tlX),15,'F')
end;
(* 6. Vẽ hàm đa thức, ở đây ta chỉ xét đến bậc cao nhất là bậc bốn: y =ax4 + bx3 + cx2 + dx +e , các bạn có thể thêm các hệ số để vẽ đồ thị hàm bậc cao hn*)
Procedure VeHamDaThuc(a,b,c,d,e:Real);
var x1,y1,z:Real;
begin
x1 := -x div tlX;
While x1 < (x div tlX) do
begin
x1 := x1 + 0.001;
y1 := a*x1*x1*x1*x1 + b*x1*x1*x1 + c*x1*x1 + d*x1 + e;
if (-y1*tlY > -y) and (-y1*tlY < y) then
PutPixel(Round(x1*tlX),-Round(y1*tlY),Yellow);
z := x1; x1 := z;
end;
end;
(* 7. Vẽ hàm phân thức dạng1 (bậc nhất trên bậc nhất) y = (ax +B)/ (cx + d) *)
Procedure VeHamPhanThuc1(a,b,c,d:Real);
var x1, y1, z:Real;
begin
x1 := -x div tlX;
if c <> 0 then
begin
SetColor(white);
(* Vẽ tiệm cận đứng màu trắng *)
VeDuongThang(1,0,d/c);
(* Vẽ tiệm cận ngang màu trắng *)
VeDuongThang(0,1,-a/c);
while x1 < -d/c do
begin
x1 := x1 + 0.001;
y1 := (a*x1 + B)/(c*x1 + d);
if (-y1*tlY > -y) and (-y1*tlY < y) then
PutPixel(Round(x1*tlX),-Round(y1*tlY),Yellow);
z := x1; x1 := z;
end;
while (x1 > -d/c) and (x1 < (x div tlX)) do
begin
x1 := x1 + 0.001;
y1 := (a*x1 + B)/(c*x1 + d);
if (-y1*tlY > -y) and (-y1*tlY < y) then
PutPixel(Round(x1*tlX),-Round(y1*tlY),Yellow);
z := x1; x1 := z;
end;
end;
end;
(* 8. Vẽ hàm phân thức dạng 2 (bậc hai trên bậc nhất) y = (ax2 + bx + c)/(dx + e) *)
Procedure VeHamPhanThuc2(a,b,c,d,e:Real);
var x1, y1, z:Real;
begin
if d <> 0 then
begin
SetColor(white);
(* Vẽ tiệm cận đứng màu trắng *)
VeDuongThang(1,0,e/d);
(* Vẽ tiệm cận xiên màu trắng *)
VeDuongThang(a*d,-d*d,b*d-a*e);
x1 := -x div tlX;
while x1 < -e/d do
begin
x1 := x1 + 0.001;
y1 := (a*x1*x1 + b*x1 + c)/(d*x1 + e);
if (-y1*tlY > -y) and (-y1*tlY < y) then
PutPixel(Round(x1*tlX),-Round(y1*tlY),Yellow);
z := x1; x1 := z;
end;
while (x1 > -e/d) and (x1 < (x div tlX)) do
begin
x1 := x1 + 0.001;
y1 := (a*x1*x1 + b*x1 + c)/(d*x1 + e);
if (-y1*tlY > -y) and (-y1*tlY < y) then
PutPixel(Round(x1*tlX),-Round(y1*tlY),Yellow);
z := x1; x1 := z;
end;
end;
end;
(* 9. Vẽ hàm lượng giác y = sin(ax + B) *)
Procedure VeHamSin(a,b:real);
var x1,y1,z:Real;
begin
x1 := -x div tlX;
while x1 < x div tlX do
begin
x1 := x1 + 0.01;
y1 := sin(a*x1+B);
PutPixel(Round(x1*tlX),-Round(y1*tlY),Yellow);
z := x1; x1 := z;
end;
end;
(* 10. Vẽ hàm lượng giác y = cos(ax + B) *)
Procedure VeHamCos(a,b:real);
var x1,y1,z:Real;
begin
x1 := -x div tlX;
while x1 < x div tlX do
begin
x1 := x1 + 0.01;
y1 := cos(a*x1+B);
PutPixel(Round(x1*tlX),-Round(y1*tlY),Yellow);
z := x1; x1 := z;
end;
end;
(* Chương trình chính: Lần lượt vẽ đồ thị của 11 hàm số, lặp lại cho đến khi ấn một phím bất kì *)
BEGIN
Repeat
KhoiTao;
for i := 1 to 11 do
begin
HeToaDo(20,20);
case i of
1: VeHamDaThuc(0,1,3,0,-2); (* y = x3 + 3x2 - 2 *)
2: VeHamDaThuc(0,0,1,2,-1); (* y = x2 + 2x -1 *)
3: VeHamDaThuc(1,0,-1,0,1); (* y = x4 - x2 + 1 *)
4: VeHamPhanThuc1(1,-1,1,1); (* y = (x -1)/(x + 1) *)
5: VeHamPhanThuc2(-1,0,-3,1,1); (* y = (-x2 -3)/(x + 1) *)
6: VeElip(7,5); (* x2/72 + y2/52 = 1 *)
7: VeHypebol(4,3); (* x2/42 - y2/32 = 1 *)
8: VeParabol(-6); (* y2 = -6x *)
9: VeHamSin(2,5); (* y = sin(2x + 5);
10: VeHamCos(2,0); (* y = cos2x *)
11: VeDuongTron(10,10,4); (* x2 + y2 + 10x + 10y + 4 = 0 *)
end;
Delay(1000); ClearDevice;
end;
Until KeyPressed;
CloseGraph;
END.


Ghi chú:
+ Thủ tục VeHypebol và Parabol tôi biểu diễn x = f(y) để tránh xy ra lỗi xử lý dấu chấm động (Error 207: Invalid floating point operation).
+ Bạn dễ dàng chứng minh được tiệm cận xiên của hàm phân thức bậc hai trên bậc nhất có công thức: adx - d2y + bd - ae = 0
+ Có thể rút gọn hai thủ tục VeHamPhanThuc1 và VeHamPhanThuc2 thành một thủ tục tổng quát là hàm phân thức bậc n trên bậc n, nhưng khi đó việc xử lý sẽ rất phức tạp.