Chương 4. Véc-tơ

Trở về Mục lục cuốn sách

1. Kiểm tra điều kiện đầu

Một số vòng lặp ở chương trước sẽ không chạy đúng nếu giá trị của n không được đặt đúng trước khi vòng lặp bắt đầu chạy. Chẳng hạn, vòng lặp sau đây dùng để tính tổng của n phần tử đầu tiên của một dãy hình học:

A1 = 1;
total = 0;
for i=1:n
    a = A1 * 0.5^(i-1);
    total = total + a;
end
ans = total

Nó chạy đúng với bất kì giá trị dương nào của n, nhưng điều gì sẽ xảy ra nếu n âm? Lúc đó, bạn sẽ nhận được:

total = 0

Vì sao? Bởi biểu thức 1:-1 nghĩa là “tất cả các số từ 1 đến -1, đếm theo chiều xuôi.” Điều này không thật hiển nhiên, nhưng MATLAB hiểu rằng chẳng có con số nào trong một khoảng như vậy, vì thế kết quả là

>> 1:-1

ans = Empty matrix: 1-by-0

Nếu ma trận là rỗng, bạn có thể trông đợi nó là “0-by-0,” bạn hiểu rồi đấy. Trong mọi trường hợp, nếu bạn lặp qua một khoảng rỗng, thì vòng lặp không chạy chút nào; đó là lý do tại sao ở ví dụ này giá trị của total bằng không với mọi giá trị âm của n.

Nếu bạn chắc rằng mình chẳng bao giờ mắc lỗi, và các điều kiện đầu cho các hàm viết ra luôn được thỏa mãn, thì bạn chẳng phải kiểm tra. Nhưng với hầu hết chúng ta, thật nguy hiểm khi viết một chương trình như thế này, một chương trình có thể cho kết quả sai (hoặc ít nhất là vô nghĩa) nếu giá trị đầu vào là số âm. Một cách làm tốt hơn là dùng lệnh if.

2. if

Lệnh if cho phép kiểm tra những điều kiện nhất định và thực hiện câu lệnh nếu điều kiện được thỏa mãn. Ở ví dụ trước, ta đã có thể viết:

if n<0
    ans = NaN
end

Cú pháp ở đây cũng tương tự một vòng lặp for. Dòng đầu tiên chỉ định điều kiện mà ta quan tâm đến; trong trường hợp này ta đang hỏi liệu n có âm không. Nếu có, MATLAB thực hiện phần thân của câu lệnh, tức là các lệnh nằm giữa ifend.

MATLAB không yêu cầu bạn viết thụt đầu dòng những lệnh trong phần thân của lệnh if, nhưng nó giúp cho mã lệnh viết ra dễ đọc hơn; và bạn nên làm điều này, có lẽ tôi cũng không phải nhắc nữa.

Ở ví dụ này, điều “đúng đắn” cần làm nếu n âm là đặt ans = NaN, vốn là một cách tiêu chuẩn để cho thấy rằng kết quả không được xác định (không phải một con số).

Nếu điều kiện không được thỏa mãn thì những câu lệnh trong phần thân không được thực hiện. Đôi khi có những câu lệnh khác cần được thực hiện khi điều kiện sai. Trong trường hợp đó bạn có thể mở rộng lệnh if với một vế else.

Dạng hoàn chỉnh của chương trình ví dụ trên có thể trông như sau:

if n<0
    ans = NaN
else
    A1 = 1;
    total = 0;
    for i=1:n
        a = A1 * 0.5^(i-1);
        total = total + a;
    end
    ans = total
end

Các lệnh như iffor trong đó chứa những câu lệnh khác được gọi là lệnh phức hợp. Tất cả các lệnh phức hợp đều kết thúc với end.

Ở ví dụ này, một trong những câu lệnh trong vế else là một vòng lặp for. Việc đặt một câu lệnh phức hợp bên trong một lệnh phức hợp khác là hợp lệ và cũng thường gặp; cách này đôi khi được gọi là lồng ghép.

3. Toán tử quan hệ

Các toán tử để so sánh hai giá trị, như toán tử <>, được gọi là toán tử quan hệ ví chúng kiểm tra mối tương quan giữa hai biến. Kết quả của một toán tử quan hệ là một trong hai giá trị logic: 1 (biểu diễn cho “đúng”), hoặc 0 (biểu diễn cho “sai”).

Các toán tử quan hệ thường xuất hiện trong lệnh if, nhưng bạn cũng có thể lượng giá chúng tại dấu nhắc lệnh:

>> x = 5;
>> x < 10

ans = 1

Bạn cũng có thể gán một giá trị logic vào một biến:

>> flag = x > 10

flag = 0

Một biến có chứa một giá trị logic thường được gọi là cờ vì chúng đánh dấu trạng thái của một điều kiện nào đó.

Các toán tử quan hệ khác gồm có <=>=, vốn rất dễ hiểu, ==, nghĩa là “bằng,” và ~=, nghĩa là “khác”. (Trong kí hiệu logic, dấu “ngã” còn biểu thị cho “không.”)

Đừng quên rằng == là toán tử để kiểm tra độ bằng nhau, còn = là toán tử gán. Nếu bạn thử dùng = trong câu lệnh if, bạn sẽ bị lỗi cú pháp:

if x=5
??? if x=5
        |
Error: The expression to the left of the equals sign is not a valid 
target for an assignment.

MATLAB nghĩ rằng bạn đang gán giá trị cho một biến tên là if x!

4. Toán tử logic

Để kiểm tra rằng một số nào đó có rơi vào khoảng cho trước không, bạn có thể muốn viết 0 < x < 10, nhưng đó là cách làm sai. Thật không may là trong nhiều trường hợp, bạn nhận được kết quả đúng nhưng lại vì một lý do sai. Chẳng hạn:

>> x = 5;
>> 0 < x < 10            % right for the wrong reason

ans = 1

Xin đừng bị đánh lừa!

>> x = 17
>> 0 < x < 10            % just plain wrong

ans = 1

Vấn đề là ở chỗ MATLAB lượng giá các toán tử từ trái qua phải, vì vậy đầu tiên là nó kiểm tra xem có phải 0<x không. Đúng vậy, do đó kết quả bằng 1. Tiếp theo nó so sánh giá trị logic 1 (chứ không phải giá trị của x) với 10. Vì 1<10, nên kết quả là đúng, ngay cả khi x không thuộc khoảng số.

Với những người mới bắt đầu lập trình, đây là một lỗi rất hiểm!

Một cách tránh tình huống này là dùng một cặp lệnh if lồng ghép để kiểm tra riêng hai vế điều kiện:

ans = 0
if 0<x
    if x<10
        ans = 1
    end
end

Nhưng sẽ gọn hơn nếu ta dùng toán tử AND (và), &&, để kết hợp các điều kiện lại.

>> x = 5;
>> 0<x && x<10

ans = 1

>> x = 17;
>> 0<x && x<10

ans = 0

Kết quả của AND là đúng nếu cả hai toán hạng đều đúng. Toán tử OR (hoặc), ||, sẽ đúng nếu một trong hai, hoặc cả hai toán hạng đúng.

5. Véc-tơ

Các giá trị mà ta đã thấy đến giờ đều là những con số đơn lẻ, và được gọi là vô hướng để phân biệt với véc-tơma trận, vốn là các tập hợp số.

Một véc-tơ trong MATLAB cũng giống như một dãy số trong toán học, đó là một tập hợp các số tương ứng với những số nguyên dương. Cái mà ta gọi là “khoảng” ở chương trước thực ra là một véc-tơ.

Nói chung, bất kể phép tính gì thực hiện được với số vô hướng đều cũng có thể thực hiện được với véc-tơ. Bạn có thể gán giá trị véc-tơ cho một biến:

>> X = 1:5

X = 1     2     3     4     5

Các biến chứa véc-tơ thường được kí hiệu bằng chữ in. Đó chỉ là quy ước; MATLAB không yêu cầu điều này, với nhưng người mới lập trình, đây là một cách hữu ích để nhớ ra đâu là số vô hướng và đâu là véc-tơ.

Cũng như với dãy, các số hợp thành một véc-tơ thì được gọi là các phần tử.

6. Phép toán số học với véc-tơ

Bạn có thể thực hiện phép tính số học với véc-tơ. Nếu bạn cộng một số vô hướng với một véc-tơ, MATLAB sẽ cộng số vô hướng đo cho từng phần tử của véc-tơ:

>> Y = X+5

Y = 6     7     8     9    10

Kết quả là một véc-tơ mới; giá trị ban đầu của X vẫn không đổi.

Nếu bạn cộng hai véc-tơ, MATLAB sẽ cộng các phần tử tương ứng của mỗi véc-tơ và tạo ra một véc-tơ mới chứa các tổng:

>> Z = X+Y

Z = 7     9    11    13    15

Nhưng phép cộng véc-tơ chỉ được khi hai véc-tơ số hạng có cùng kích cỡ. Nếu không thì:

>> W = 1:3

W = 1     2     3

>> X+W
??? Error using ==> plus
Matrix dimensions must agree.

Thông báo lỗi trong trường hợp này hơi lạc hướng, vì chúng ta đang nghĩ về các véc-tơ, chứ không phải ma trận. Vấn đề chỉ là một sự không khớp giữa từ vựng toán học và từ vựng trong MATLAB.

7. Mọi thứ đều là ma trận

Trong toán học (đặc biệt là đại số tuyến tính), một véc-tơ là một dãy một chiều các giá trị còn ma trận có hai chiều (và nếu bạn muốn nghĩ theo cách đó thì số vô hướng là đại lượng không chiều). Trong MATLAB, mọi thứ đều là ma trận.

Bạn có thể thấy điều này khi dùng lệnh whos để hiển thị các biến trong không gian làm việc. whos cũng giống như who, chỉ khác ở chỗ nó còn hiển thị cả kích cỡ và kiểu của từng biến.

Trước hết tôi sẽ tạo ra các giá trị có kiểu khác nhau:

>> scalar = 5

scalar = 5

>> vector = 1:5

vector = 1     2     3     4     5

>> matrix = ones(2,3)

matrix =

     1     1     1
     1     1     1

ones là một hàm để lập một ma trận mới với số hàng và số cột đã cho, rồi đặt tất cả các phần tử bằng 1. Bây giờ hãy xem ta có gì.

>> whos
  Name         Size                    Bytes  Class

  scalar       1x1                         8  double array
  vector       1x5                        40  double array
  matrix       2x3                        32  double array

Theo MATLAB, mọi thứ đều là mảng có độ chính xác kép: “double” là tên gọi khác của số có dấu chấm động với độ chính xác kép, còn “array” là tên gọi khác của ma trận.

Khác biệt duy nhất giữa chúng là kích cỡ, vốn được chỉ định bởi số hàng và số cột. Biến số vô hướng, scalar, theo MATLAB, là một ma trận có 1 hàng và 1 cột. Còn vector là một ma trận thực sự với 1 hàng và 5 cột. Và tất nhiên, matrix là một ma trận.

Mục đích của tât cả những điều trên là bạn có thể coi các giá trị trong chương trình như là số vô hướng, véc-tơ và ma trận, và tôi nghĩ rằng bạn nên như vậy, chỉ cần nhớ rằng MATLAB coi mọi thứ như ma trận.

Sau đây là một ví dụ khác trong đó thông báo lỗi chỉ có nghĩa nếu bạn hiểu được điều gì đang xảy ra bên trong:

>> X = 1:5

X = 1     2     3     4     5

>> Y = 1:5

Y = 1     2     3     4     5

>> Z = X*Y
??? Error using ==> mtimes
Inner matrix dimensions must agree.

Trước hết, mtimes là hàm của MATLAB để tính nhân hai ma trận. Lí do: các kích thước cạnh nhau của hai ma trận thừa số thì phải tương đồng (“inner matrix dimensions must agree”). Theo nguyên tắc định nghĩa phép nhân ma trận trong đại số tuyến tính, cột của X phải bằng số hàng của Y.

Nếu bạn không biết đại số tuyến tính thì cũng không sao. Khi nhìn thấy X*Y có lẽ bạn mong đợi rằng từng phần tử của X phải được nhân với từng phần tử của Y và kết quả được đưa vào một véc-tơ mới. Phép toán đó được gọi là phép nhân phần tử, và toán tử thực hiện công việc này là .*:

>> X .* Y

ans = 1     4     9    16    25

Ta sẽ trở lại các toán tử thao tác trên phần tử vào một dịp khác; bạn có thể tạm quên chúng.

8. Chỉ số

Bạn có thể chọn phần tử trong một véc-tơ bằng cách dùng cặp ngoặc tròn:

>> Y = 6:10

Y = 6     7     8     9    10

>> Y(1)

ans = 6

>> Y(5)

ans = 10

Điều này nghĩa là phần tử thứ nhất của Y bằng 6 và phần tử thứ năm bằng 10. Con số trong ngoặc tròn được gọi là chỉ số vì nó biểu thị phần tử mà bạn muốn của véc-tơ.

Chỉ số có thể là một biểu thức bất kì.

>> i = 1;

>> Y(i+1)

ans = 7

Vòng lặp đi cùng véc-tơ cũng tựa như hổ mọc thêm cánh. Chẳng hạn, vòng lặp này hiển thị các phần tử của Y.

for i=1:5
     Y(i)
end

Mỗi lượt lặp ta dùng một giá trị khác của i làm chỉ số cho Y.

Một hạn chế trong ví dụ này là nếu ta phải biết trước số phần tử của Y. Ta có thể khái quát hóa bằng cách dùng hàm length, vốn để trả lại số phần tử trong một véc-tơ.

for i=1:length(Y)
     Y(i)
end

Vậy đấy. Bây giờ nó sẽ chạy được với một véc-tơ có độ dài tùy ý.

9. Lỗi chỉ số

Một chỉ số có thể là biểu thức bất kì, nhưng giá trị của biểu thức này phải là số nguyên dương, và nó phải nhỏ hơn hoặc bằng chiều dài của véc-tơ. Nếu nó bằng không hoặc âm, bạn sẽ nhận được thông báo sau:

>> Y(0)
??? Subscript indices must either be real positive integers or
logicals.

“Subscript indices” chính là “indices”, chỉ số trong MATLAB. “Real positive integers” (số nguyên dương) có nghĩa loại trừ các số phức. Và bạn cũng có thể tạm quên đi “logicals” (kiểu giá trị logic).

Nếu chỉ số quá lớn, bạn sẽ thu được:

>> Y(6)
??? Index exceeds matrix dimensions.

Và lại liên quan đến chữ “m” (ma trận), nhưng ngoài đó ra thì thông báo lỗi này tương đối rõ ràng.

Sau cùng, đừng quên rằng chỉ số phải là một số nguyên:

>> Y(1.5)
??? Subscript indices must either be real positive integers or
logicals.

10. Véc-tơ và dãy số

Véc-tơ và dãy số thường gắn bó mật thiết với nhau. Chẳng hạn, một cách khác để tính dãy Fibonacci là bằng cách lưu các giá trị liên tiếp vào một véc-tơ. Một lần nữa, định nghĩa của dãy Fibonacci là F1=1, F2=1, và Fi=Fi-1+Fi-2 với i ≥ 3. Trong MATLAB, câu lệnh sẽ có dạng

F(1) = 1
F(2) = 1
for i=3:n
    F(i) = F(i-1) + F(i-2)
end
ans = F(n)

Lưu ý rằng tôi dùng chữ cái viết in cho véc-tơ F và chữ cái thường cho các số vô hướng in. Cuối cùng, đoạn chương trình lấy phần tử chót của F và lưu nó vào ans, vì kết quả của chương trình phải là số Fibonacci thứ n, chứ không phải cả dãy số.

Nếu từng gặp vướng mắc ở Bài tập số Fibonacci, bạn phải trân trọng sự giản đơn của phiên bản chương trình trên. Cú pháp của MATLAB cũng tương đồng với kí hiệu toán học; nó làm ta dễ kiểm tra tính đúng đắn của chương trình. Song có những nhược điểm cần lưu ý gồm

  • Bạn phải cẩn thận với khoảng số của vòng lặp. Ở đoạn chương trình trên, vòng lặp chạy từ 3 đến n, và mỗi lần ta gán một giá trị vào phần tử thứ i. Nó cũng chạy khi ta “đẩy” chỉ số đi hai đơn vị, cho vòng lặp chạy từ 1 đến n-2:
    F(1) = 1
    F(2) = 1
    for i=1:n-2
        F(i+2) = F(i+1) + F(i)
    end
    ans = F(n)
    

    Bạn dùng phiên bản nào cũng được, nhưng cần chọn một cách làm và phải thống nhất với nó. Nếu bạn kết hợp các phần tử của cả hai cách, bạn sẽ bị lẫn. Tôi ưa dùng dạng có F(i) bên vế trái lệnh gán, vì vậy mỗi lượt lặp nó sẽ gán cho phần tử thứ i.

  • Nếu bạn thực sự chỉ muốn số Fibonacci thứ n, việc lưu trữ cả dãy số sẽ làm tốn dung lượng bộ nhớ của máy. Nhưng nếu mất dung lượng mà đoạn mã trở nên dễ viết và gỡ lỗi hơn thì có lẽ vẫn tốt.

Hãy viết một vòng lặp để tính n phần tử đầu tiên của dãy hình học Ai+1 Ai/2 với A= 1. Lưu ý rằng kí hiệu toán học đặt Ai+1 ở vế trái của đẳng thức. Khi chuyển sang mã lệnh MATLAB, có thể bạn sẽ phải đẩy dịch chỉ số.

11. Vẽ đồ thị các véc-tơ

Tính năng vẽ đồ thị rất hữu ích đối với biểu diễn véc-tơ. Nếu bạn gọi plot với đối số là một véc-tơ, MATLAB sẽ lấy các chỉ số tọa độ theo phương x và giá trị các phần tử làm tọa độ theo phương y. Để biểu diễn giá trị các số Fibonacci đã tính được ở mục trước:

plot(F)

Hình biểu diễn này thường có ích cho việc gỡ lỗi, đặc biệt là khi véc-tơ của bạn lớn đến nỗi việc in các giá trị số lên màn hình trở nên vô hiệu.

Nếu bạn gọi plot với đối số là hai véc-tơ, MATLAB sẽ vẽ véc-tơ thứ hai như một hàm số của véc-tơ đầu; nghĩa là giá trị của véc-tơ đầu làm các tọa độ x còn các giá trị tương ứng của véc-tơ sau làm tọa độ y rồi vẽ các cặp điểm (x,y).

X = 1:5
Y = 6:10
plot(X, Y)

MATLAB mặc định màu nét vẽ là xanh lam, nhưng bạn có thể thay đổi bằng một chuỗi kí tự có dạng giống như ta đã thấy ở Mục plotting. Chẳng hạn, chuỗi 'ro-' ra lệnh cho MATLAB vẽ những vòng tròn nhỏ ở mỗi điểm dữ liệu; dấu gạch ngang ngụ ý rằng các điểm cần được nối bởi đường thẳng.

Trong ví dụ này, tôi gắn chặt với quy ước đặt tên đối số thứ nhất là X (vì nó ứng với tọa độ x) và đối số thứ hai Y. Không có gì đặc biệt với những cái tên này; bạn hoàn toàn có thể vẽ X như là hàm theo Y. MATLAB luôn coi véc-tơ thứ nhất là biến độc lập, và véc-tơ thứ hai là biến phụ thuộc.

12. Phép rút gọn

Một cách dùng hay thấy ở vòng lặp là chạy qua các phần tử của một mảng vầ cộng chúng lại, hoặc nhân với nhau, hoặc tính tổng các bình phương, v.v. Kiểu tính toán này được gọi là phép rút gọn, vì nó rút gọn một véc-tơ với nhiều phần tử về một con số vô hướng.

Chẳng hạn, vòng lặp này cộng lại những phần tử của một véc-tơ có tên X (mà ta coi rằng nó đã được định nghĩa trước).

total = 0
for i=1:length(X)
    total = total + X(i)
end
ans = total

Các dùng total như một biến thu gom cũng giống như điều ta đã thấy ở Mục series. Một lần nữa, ta dùng hàm length để xác định giới hạn trên của khoảng, vì vậy vòng lặp này sẽ chạy được bất kể độ dài của X bằng bao nhiêu. Mỗi lượt lặp, ta cộng total với phần tử thứ i của X, vì vậy khi hết vòng lặp total sẽ mang giá trị tổng của tất cả phần tử.

Hãy viết một vòng lặp để nhân tất cả các phần tử của một véc-tơ với nhau. Có thể bạn sẽ cần đặt một biến thu gom product, và phải nghĩ xem đặt giá trị ban đầu bằng bao nhiêu trước khi vòng lặp được thực thi.

13. Áp dụng

Một công dụng khác của hàm là chạy qua các phần tử của một véc-tơ, thực hiện một phép toán nào đó lên từng phần tử, rồi tạo ra véc-tơ mới chứa các kết quả. Dạng tính toán này được gọi là áp dụng, bởi vì khi đó bạn áp dụng phép toán với từng phần tử của véc-tơ.

Chẳng hạn, vòng lặp sau đây tính một véc-tơ Y trong đó chứa các bình phương của từng phần tử thuộc X (một lần nữa, ta giả sử rằng X đã được định nghĩa).

for i=1:length(X)
    Y(i) = X(i)^2
end

Hãy viết một vòng lặp để tính một véc-tơ Y trong đó chứa các giá trị sin của từng phần tử thuộc X. Để kiểm tra vòng lặp của bạn, hãy viết một chương trình

  1. sử dụng linspace (xem thông tin cách dùng) để gán X là véc-tơ gồm 100 phần tử từ 0 đến .
  2. dùng vòng lặp vừa viết để lưu các giá trị sin vào Y.
  3. Vẽ đồ thị các phần tử của Y như là hàm của từng phần tử thuộc X.

14. Tìm kiếm

Một cách dùng khác của vòng lặp là tìm kiếm những phần tử trong một véc-tơ rồi trả lại chỉ số của giá trị mà bạn cần tìm (hoặc giá trị đầu tiên thỏa mãn đặc tính nào đó). Chẳng hạn, nếu một véc-tơ bao gồm các giá trị độ cao tính được của một vật thể rơi, bạn có lẽ cũng muốn biết chỉ số nào của véc-tơ đó ứng với lúc vật thể chạm đất (coi rằng mặt đất ở cao độ bằng 0).

Để tạo ra dữ liệu giả định, ta sẽ dùng một dạng đầy đủ của toán tử hai chấm:

X = 10:-1:-10

Các giá trị trong khoảng này chạy từ 10 đến –10, với độ dài bước là –1. Độ dài bước là khoảng cách giữa các phần tử cạnh nhau trong khoảng.

Vòng lặp sau tìm ra chỉ số của phần tử bằng 0 trong X:

for i=1:length(X)
    if X(i) == 0
        ans = i
    end
end

Một điều buồn cười ở vòng lặp này là nó tiếp tục chạy ngay cả khi đã tìm được giá trị mong muốn. Đây có lẽ không phải là điều bạn cần. Song cũng có thể bạn muốn làm cách này, nếu giá trị cần tìm xuất hiện nhiều lần; khi đó vòng lặp sẽ cho chỉ số của phần tử cuối cùng thỏa mãn điều kiện đề ra.

Nhưng nếu bạn muốn chỉ số của phần tử đầu tiên (hay biết rằng chỉ có một phần tử như vậy), bạn có thể tiết kiệm một số vòng lặp không cần thiết bằng cách dùng câu lệnh break.

for i=1:length(X)
    if X(i) == 0
        ans = i
        break
    end
end

break có tác dụng giống như tên gọi của nó: thoát khỏi vòng lặp. Nó dừng vòng lặp và thực hiện câu lệnh ngay sau vòng lặp (trong trường hợp này, không còn câu lệnh nào nữa, và chương trình kết thúc).

Ví dụ trên minh hoạ cho ý tưởng cơ bản của công việc tìm kiếm, nhưng cũng cho thấy một cách dùng nguy hiểm của lệnh if. Hãy nhớ rằng các giá trị dấu phẩy động thường chỉ gần đúng. Điều đó có nghĩa là nếu bạn tìm kiếm dựa trên sự bằng nhau tuyệt đối, có thể bạn sẽ không tìm ra. Chẳng hạn, hãy thử đoạn lệnh sau:

X = linspace(1,2)
for i=1:length(X)
    Y(i) = sin(X(i))
end
plot(X, Y)

Bạn có thể thấy rằng trên đồ thị, giá trị của \sinx đi qua 0.9 trong khoảng này, nhưng nếu tìm một chỉ số sao cho Y(i) == 0.9, bạn sẽ trắng tay.

for i=1:length(Y)
    if Y(i) == 0.9
        ans = i
        break
    end
end

Điều kiện này không bao giờ đúng, vì thế phần thân của lệnh if không bao giờ được thực hiện.

Ngay cả khi đồ thị biểu diễn một đường liên tục, bạn đừng quên rằng cả XY đều là các chuỗi rời rạc, và thường chỉ là giá trị xấp xỉ. Như một quy tắc, bạn nên tránh việc dùng toán tử == để so sánh hai giá trị số có dấu phẩy động. Có một số cách khắc phục điều này mà ta sẽ trở lại sau.

Hãy viết một vòng lặp để tìm chỉ số của số nguyên đầu tiên xuất hiện trong một véc-tơ và lưu nó vào trong ans. Nếu không có giá trị số âm nào, bạn nên luôn đặt ans bằng –1 (vốn không phải là một chỉ số hợp lệ) vì đó là cách biểu thị một trường hợp bất thường.

15. Sự thật có thể gây mất hứng

Những người lập trình MATLAB có kinh nghiệm sẽ không bao giờ viết những vòng lặp như trong chương này, vì MATLAB cung cấp những cách làm đơn giản và nhanh hơn cho việc rút gọn, áp dụng và tìm kiếm.

Chẳng hạn, hàm sum có thể dùng để tính tổng của các phần tử trong véc-tơ và prod để tính tích.

Nhiều thao tác áp dụng có thể được thực hiện bằng các toán tử đối với từng phần tử. Câu lệnh sau đây gọn hơn là vòng lặp ở Mục Áp dụng

Y = X .^ 2

Ngoài ra, đa số các hàm MATLAB lập sẵn đều tính được với véc-tơ:

X = linspace(0, 2*pi)
Y = sin(X)
plot(X, Y)

Sau cùng, hàm find có thể đảm nhiệm thao tác tìm kiếm, nhưng để hiểu được nó ta cần biết một số khái niệm khác nữa, vì vậy tạm thời bạn cần bằng lòng với cách làm của mình.

Tôi bắt đầu bằng việc đề cập các vòng lặp đơn giản vì muốn cho thấy những khái niệm cơ bản và tạo điều kiện cho bạn thực hành. Đến lúc nào đó bạn có thể phải viết một vòng lặp mà MATLAB không có sẵn một cách làm tắt, mà bạn phải tự lập nên.

Nếu bạn hiểu được các vòng lặp và thấy thoải mái với cách làm tắt thì hãy dùng chúng! Cò nếu không bạn luôn có thể viết hẳn vòng lặp ra.

Hãy viết một biểu thức để tính tổng của các bình phương từng phần tử trong một véc-tơ.

16. Thuật ngữ

lệnh phức hợp:
Một lệnh như iffor, trong đó chứa các câu lệnh khác ở phần thân được viết thụt đầu dòng.

lồng ghép:
Đặt một lệnh phức hợp vào trong phần thân của một lệnh phức hợp khác.

toán tử quan hệ:
Toán tử để so sánh hia giá trị và trả lại kết quả là một giá trị logic.

giá trị logic:
Giá trị biểu thị cho “đúng” hoặc “sai”. MATLAB dùng các giá trị tương ứng là 1 và 0.

cờ:
Biến bao gồm một giá trị logic, thường được dùng để lưu trữ trạng thái của một điều kiện nào đó.

vô hướng:
Một giá trị đơn lẻ.

véc-tơ:
Dãy các giá trị.

ma trận:
Tập hợp các giá trị xếp theo hai chiều (cũng gọi là “array” (mảng) trong một số tài liệu về MATLAB).

chỉ số:
Số nguyên dùng để chỉ thị một trong các giá trị trong một véc-tơ hay ma trận (cũng gọi là “subscript” (chỉ số dưới) trong một số tài liệu về MATLAB).

phần tử:
Một trong các giá trị của véc-tơ hay ma trận.

theo phần tử:
Phép tính thực hiện trên từng phần tử của một véc-tơ hay ma trận (khác với các phép toán trong môn đại số tuyến tính).

rút gọn:
Cách xử lý các phần tử của một véc-tơ để trả về một giá trị đơn lẻ, như tổng các phần tử.

áp dụng:
Cách xử lý một véc-tơ bằng việc thực hiện phép toán đối với mỗi phần tử, để cho ra một véc-tơ chứa các kết quả.

tìm kiếm:
Cách xử lý một véc-tơ bằng việc kiểm tra từng phần tử một theo thứ tự đến khi tìm thấy một phần tử thỏa mãn điều kiện mong muốn.

17. Bài tập

Tỉ số giữa hai số Fibonacci liên tiếp, Fn+1/Fn, sẽ hội tụ về một hằng số khi n tăng lên. Hãy viết một chương trình tính ra một véc-tơ gồm n phần tử đầu tiên của dãy Fibonacci sequence (giả thiết rằng biến n đã được định nghĩa), rồi tính một véc-tơ mới chứa các tỉ số của cac số Fibonacci liên tiếp. Hãy vẽ đồ thị véc-tơ này xem nó có xu hướng hội tụ không. Nếu có thì nó hội tụ về giá trị nào?

Có một hệ phương trình vi phân nổi tiếng được xấp xỉ bởi hệ phương trình như sau:

\left\{\begin{matrix} x_{i+1} & = & x_i + \sigma \left( y_i - x_i \right) dt \\ y_{i+1} & = & y_i + \left[ x_i (r - z_i) - y_i \right] dt \\ z_{i+1} & = & z_i + \left( x_i y_i - b z_i \right) dt \end{matrix}\right.

  • Hãy viết một chương trình tính 10 phần tử đầu tiên của các dãy X, YZ lưu chúng vào các véc-tơ có tên X, YZ.

    Hãy dùng các giá trị khởi đầu X= 1 , Y= 2Z= 3, cùng σ = 10, = 8/3= 28, bước thời gian dt = 0,01.

  • Hãy đọc các giải thích cách dùng plot3comet3 rồi vẽ kết quả trong không gian 3 chiều.
  • Một khi mã lệnh đã chạy được, hãy dùng các dấu chấm phẩy để ngăn các kết quả in ra rồi sau đó chạy chương trình với các dãy có độ dài lần lượt là 100, 1000 và 10000.
  • Chạy lại chương trình với các điều kiện khởi đầu khác nhau. Điều này có ảnh hưởng gì đến kết quả?
  • Hãy chạy chương trình với các giá trị khác nhau của σ, br rồi xem liệu bạn có thể hiểu được từng biến có ảnh hưởng gì đến hệ.

Phép khớp logistic thường được đem ra làm ví dụ cho việc một biểu hiện phức tạp, hỗn loạn có thể nảy sinh từ các phương trình động lực đơn giản [một số nội dung trong bài này được lấy từ trang Wikipedia]. Nó trở nên phổ biến từ khi xuất hiện bài báo năm 1976 của nhà sinh vật học Robert May.

Phép khớp logistic được dùng để mô phỏng sinh khối của một loài khi có mặt các yếu tố hạn chế như nguồn thức ăn và dịch bệnh. Trong trường hợp này, có hai yếu tố cần xét đến: (1) Quá trình sinh sản làm tăng sinh khối của các loài tỉ lệ với số cá thể hiện tại. (2) Quá trình chết đói khiến cho sinh khối giảm với tốc độ tỉ lệ với hiệu số giữa sức mang của môi trường với số cá thể hiện tại.

Điều này có thể viết bằng biểu thức sau

Xi+1 rXi(1 – Xi)

trong đó Xi là một số giữa 0 và 1 để biểu thị sinh khối vào năm thứ i, còn r là một số dương biểu thị tốc độ tổng hợp của sinh sản và chết đói.

  • Hãy viết một tập tin lệnh có tên logmap để tính 50 phần tử đầu tiên của X với r=3.9X1=0.5, trong đó r là tham số của phép khớp logistic còn X1 là số cá thể ban đầu.
  • Hãy vẽ đồ thị kết quả với một khoảng các giá trị của r từ 2.4 đến 4.0. Biểu hiện của hệ thống sẽ thay đổi ra sao khi bạn thay đổi ra sao khi bạn thay đổi r?
  • Một cách đặc trưng cho ảnh hưởng của r là vẽ một đồ thị với r là tọa độ x và sinh khối là tọa độ y, để cho thấy ứng với mỗi giá trị của r, giá trị sinh khối ở trạng thái ổn định là bao nhiêu. Thử xem bạn có hình dung được cách vẽ biểu đồ này không?

11 phản hồi

Filed under Mô hình hóa

11 responses to “Chương 4. Véc-tơ

  1. Pingback: Chương 1. Các biến và giá trị | Blog của Chiến

  2. Pingback: Chương 11: Tối ưu hóa và nội suy | Blog của Chiến

  3. Pingback: Chương 6: Tìm nghiệm | Blog của Chiến

  4. Pingback: Mô hình hóa hiện tượng vật lý bằng MATLAB | Blog của Chiến

  5. Văn Toản

    Anh giúp em bài này với được không, liên quan đến toán mà lâu lắm không động nên bí quá:
    nhập 2 ma trận cùng số cột A,B. kết hợp lệnh null ,tìm cơ sở và số chiều của giao 2 không gian nghiệm của hệ thuần nhất Ax=0,Bx=0

  6. Khách

    anh ơi nhờ anh xem hô em cái code này với dk ko ạ nó báo lỗi là:
    Error using ==> times
    Matrix dimensions must agree.
    Error in ==> oto0 at 53
    zc=A*y+f;
    nhờ anh xem chỗ này em phải sửa sao với ạ.
    % file oto.m
    global wn m3 lt m1 m4 Ju K1 K4 C1 C4 Kl1 Kl4
    tf=[0 10];
    z0=[0 0 0 0]’;
    [t,z]=ode45(‘oto0’,tf,z0);
    disp(‘tan so dao dong tu do cua he’)
    wn=diag(wn);

    %file oto0.m
    function [zc ]= oto0(t,y)
    global wn m3 lt m1 m4 Ju K1 K4 C1 C4 Kl1 Kl4
    lt=0.72;a =1.36;b =1.36;
    m1=57;m3=57;m4=57;
    Ju=750;% mo mem quan tinh doi voi truc ngang;
    C1=1000;C4=1100;
    K1=30000;K4=32500;
    Cl1=0;Cl4=0;
    Kl1=160000;Kl4=160000;
    %phuong trinh nhap nho mat dung
    y1=0.01*sin(11.64*t);
    y4=0.01*sin(11.64*t-2.85);
    %van toc nhap nho mat dung
    M=[m3 0 0 0;0 m1 0 0;0 0 m4 0;0 0 0 Ju];
    Mn=[0.0175 0 0 0;
    0 0.0175 0 0;
    0 0 0.0175 0;
    0 0 0 0.0013];

    K = [K1+K4 -K1 -K4 -K1*a-K4*b;
    -K1 K1+Kl1 0 K1*a;
    -K4 0 K4+Kl4 -K4*lt;
    -K1*a-K4*b K1*a -K4*b K1*a^2+K4*b^2];
    C=[C1+C4 -C1 -C4 -C1*a-C4*b;
    -C1 C1+Cl1 0 C1*a;
    -C4 0 C1+Cl4 -C4*b;
    -C1*a-C4*b C1*a -C4*b C1*a^2+C4*b^2];
    %Luc tac dung do kich thich mat duong
    R=[0 y1 y4 0]’;
    [X,W]=eig(K,M);
    wn=sqrt(W);
    %ma tran 0
    O1=zeros(4,4);
    %ma tran don vi4X4
    I=eye(4);
    %vecto 0 co 4X1
    O2=zeros(4,1);
    %xac dinh vec to rieng va tri rieng
    %tan so dao dong tu do cua hec
    %ma tran A
    A=[O1 I;-Mn*K -Mn*C];
    %vecto f
    f=[O2;Mn*R];
    %phuong trinh trang thai
    zc=A*y+f;

    • Bạn dùng hàm size() để kiểm tra kích thước của A và y xem có tương hợp không? Đây là nhân ma trận? y được nhập vào giá trị nào, kích thước bao nhiêu?

      • Khách

        Dạ em sửa dược lỗi này rồi ạ.
        Em cám ơn anh.

      • Khách

        Anh ơi cho em hỏi thêm một chút được không ạ.
        Em có chương trình này và muốn lấy ra đồ thị của gia tốc vậy có thể làm cách nào ạ.

        %File ham otod0.m
        function [zc ]= otod0(t,y)
        global wn M m1 m4 Jv K1 K4 C1 C4 X F1 F2 y1c y4c
        a =1.36;b =1.36;
        m1=57;M=1779;m4=57;
        Jv=1840; % Mo mem quan tinh;
        C1=1000;C4=1100;
        K1=30000;K4=32500;
        Cl1=0;Cl4=0;
        Kl1=160000;Kl4=160000;
        % Phuong trinh nhap nho mat dung
        y1=0.01*sin(11.64*t);
        y4=0.01*sin(11.64*t-2.85);
        y1c=0.116*cos(11.64*t);
        y4c=0.116*cos(11.64*t-2.85);
        % Van toc nhap nho mat dung
        M=[M 0 0 0;0 m1 0 0;0 0 m4 0;0 0 0 Jv];
        Mn=[0.0006 0 0 0;%Mn ma tran nghich dao cua M
        0 0.0175 0 0;
        0 0 0.0175 0;
        0 0 0 0.0005];
        % Ma tran do cung
        K = [K1+K4 -K1 -K4 -K1*a+K4*b;
        -K1 K1+Kl1 0 K1*a;
        -K4 0 K4+Kl4 -K4*b;
        -K1*a+K4*b K1*a -K4*b K1*a^2+K4*b^2];
        % Ma tran can nhot
        C=[C1+C4 -C1 -C4 -C1*a+C4*b;
        -C1 C1+Cl1 0 C1*a;
        -C4 0 C1+Cl4 -C4*b;
        -C1*a+C4*b C1*a -C4*b C1*a^2+C4*b^2];
        % Luc tac dung do kich thich mat duong
        F1=Kl1*y1+Cl1*y1c;
        F2=Kl4*y4+Cl4*y4c;
        R=[0 F1 F2 0]’;
        % Xac dinh vec to rieng va tan so rieng cua he
        [X,W]=eig(K,M);
        wn=sqrt(W);
        % Ma tran 0
        O1=zeros(4,4);
        % Ma tran don vi4X4
        I=eye(4);
        % Vecto 0 co 4X1
        O2=zeros(4,1);
        % Ma tran A
        A=[O1 I;-Mn*K -Mn*C];
        % Vecto f
        f=[O2;Mn*R];
        % Phuong trinh trang thai
        zc=A*y+f;

        %File chinh otod.m

        global wn M m1 m4 Jv K1 K4 C1 C4
        a =1.36;b =1.36;
        m1=57;M=1779;m4=57; % Khoi luong cua vat
        Jv=1840; % mo mem quan tinh doi voi truc ngang;
        C1=1000;C4=1100; % He so can giam chan Ns/m
        K1=30000; % Do cung lo xo N/m
        K4=32500;
        Cl1=0; % He so can cua lop truoc Ns/m
        Cl4=0; % He so can cua lop truoc Ns/m
        Kl1=160000;Kl4=160000; % Do cung cua lop truoc va sau N/m
        tf=[0 10]; % Thoi gian dao dong
        z0=[0 0 0 0 0 0 0 0]’; % Dieu kien ban dau
        [t,z]=ode45(‘otod0’,tf,z0); % Ham ode45 giai phuong trinh vi phan
        disp(‘tan so dao dong tu do cua he’)
        disp(diag(wn)’)
        subplot(1,1,1)
        plot(t,z(:,1))
        title(‘Do thi chuyen vi than xe trong mat phang doc’)
        ylabel(‘y0(m)’)
        grid
        figure
        subplot(1,1,1)
        plot(t,z(:,2))
        title(‘Do thi chuyen vi cua phan khoi luong truc va lop banh truoc’)
        xlabel(‘t(s)’)
        ylabel(‘y0(m)’)
        grid
        figure
        subplot(1,1,1)
        plot(t,z(:,3))
        title(‘Do thi chuyen vi cua phan khoi luong truc va lop banh sau’)
        ylabel(‘y0(m)’)
        grid
        figure
        subplot(1,1,1)
        plot(t,z(:,4))
        title(‘Do thi chuyen vi goc than xe trong mat phang doc’)
        xlabel(‘t(s)’)
        ylabel(‘y0(rad)’)
        grid

      • Bạn tham khảo Chương 10: Các hệ bậc hai, https://quangchien.wordpress.com/2011/11/26/ch10/ , mục Hiện tượng rơi tự do.

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s