Chương 7: Hàm số của véc-tơ

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

Hàm số và tập tin

Đến giờ ta mới chỉ đưa một hàm vào trong mỗi tập tin. Cũng có thể đặt nhiều hàm vào trong một tập tin, nhưng chỉ có hàm đầu tiên, hàm cấp cao nhất mới gọi được từ Command Window. Các hàm phụ trợ khác có thể được gọi từ bất kì đâu trong tập tin, nhưng không thể gọi từ tập tin khác.

Những chương trình lớn thường cần đến nhiều hàm; việc giữ tất cả các hàm trong cùng một tập tin tuy tiện lợi, nhưng làm cho việc gỡ lỗi trở nên khó khăn vì bạn không thể gọi các hàm phụ trợ từ Command Window.

Để giúp giải quyết vấn đề này, tôi thường dùng hàm cấp cao nhất để phat triển và thử nghiệm các hàm phụ trợ. Chẳng hạn, tôi có thể tạo ra một tập tin tên là duck.m và khởi đầu với một hàm cấp cao nhất có tên là duck mà không nhận vào bất kì biến đầu vào cũng như trả lại bất kì biến đầu ra nào.

Sau đó tôi sẽ viết một hàm có tên là error_func để lượng giá hàm sai số cho fzero. Để thử nghiệm error_func tôi sẽ gọi nó từ duck rồi gọi duck từ Command Window.

Bản nháp chương trình đầu tiên của tôi có thể trông như sau:

function res = duck()
    error = error_func(10)
end

function res = error_func(h)
    rho = 0.3;      % density in g / cm^3
    r = 10;         % radius in cm
    res = h;
end

Dòng lệnh res = h chưa xong, nhưng để phục vụ mục đích thử nghiệm thì từng ấy mã lệnh cũng đủ. Một khi đã hoàn thành và thử nghiệm error_func, tôi sẽ sửa duck sao cho nó dùng fzero.

Ở bài toán này tôi có thể chỉ cần đến hai hàm, nhưng nếu có nhiều hàm hơn, tôi có thể lần lượt viết và thử nghiệm từng hàm một, rồi sau đó kết hợp chúng lại thành một chương trình chạy được.

Mô hình hóa vật lý

Các ví dụ mà chúng ta đã đề cập đến giờ đều liên quan đến toán học; Bài tập [“bài toán con vịt”], là ví dụ đầu tiên mà chúng ta tìm hiểu một hiện tượng vật lý. Nếu bạn chưa làm bài này, hãy quay lại và ít nhất là đọc kỹ bài toán.

Cuốn sách này được viết về chủ đề mô hình hóa vật lý, vì vậy tôi nên giải thích ý nghĩa của cụm từ trên. Mô hình hóa vật lý là một công đoạn thiết lập những phỏng đoán về các hệ thống vật lý và giải thích biểu hiện của chúng. Một hệ vật lý là thứ tồn tại khách quan mà ta cần quan tâm, chẳng hạn như con vịt.

Hình vẽ sau minh họa các bước của quá trình này:

Một mô hình là một dạng biểu diễn được đơn giản hóa cho một hệ vật lý. Quá trình lập một mô hình được gọi là trừu tượng hóa. Trong phạm vi bàn đến ở đây, “trừu tượng” ngược nghĩa với “hiện thực;” một mô hình trừu tượng ít có sự tương đồng trực tiếp đến hệ vật lý mà nó mô phỏng, cũng như hội họa trừu tượng không trực tiếp biểu diễn hình ảnh của vạn vật ngoài đời. Một mô hình hiện thực là mô hình bao gồm nhiều chi tiết hơn và tương đồng hơn với thế giới thực.

Sự trừu tượng hóa sẽ dẫn đến việc phải quyết định đúng đắn về việc đưa các yếu tố nào vào mô hình và đơn giản bớt hoặc lược bỏ những yếu tố nào. Chẳng hạn, trong bài toán con vịt, ta xét đến trọng lượng riêng của vịt và lực đẩy nổi của nước, nhưng bỏ qua lực đẩy nổi do không khí và tác động đạp nước của chân vịt. Ta cũng đơn giản hóa hình dạng con vịt bằng cách giả thiết rằng phần con vịt ngập trong nước cũng gần giống một chỏm cầu. Và chúng ta đã sử dụng những ước tính sơ lược về kích thước và khối lượng của con vịt.

Trong số các quyết định giản hóa trên, có cái chấp nhận được. Khối lượng riêng của con vịt cao hơn của không khí rất nhiều nên ảnh hưởng của lực đẩy nổi của không khí có lẽ sẽ rất nhỏ. Những quyết định khác, như hình dạng khối cầu, thì khó chấp nhận hơn, nhưng có ích. Hình dạng thực tế của con vịt rất phức tạp; mô hình khối cầu giúp ta có thể tính được một kết quả gần đúng mà không phải thực hiện đo đạc chi tiết hình dạng của những con vịt thật.

Một mô hình giống thật hơn chưa chắc đã tốt hơn. Mô hình rất có ích vì chúng có thể được phân tích về mặt toán học và mô phỏng về mặt số trị. Các mô hình quá giống thật sẽ khó có thể mô phỏng được và hoàn toàn không thể phân tích được.

Một mô hình được gọi là thành công nếu nó đạt yêu cầu đề ra. Nếu ta chỉ cần một ước đoán rất thô sơ về phần của con vịt ngập trong nước thì mô hình khối cầu cũng là đủ. Nếu ta cần một lời giải chính xác hơn (vì lý do nào đó) thì ta có thể cần đến một mô hình giống thật hơn.

Việc kiểm tra xem một mô hình có đủ tính đúng đắn hay không được gọi là thẩm định. Hình thức chặt chẽ nhất của thẩm định là tiến hành đo trên hệ vật lý và so sánh với kết quả dự đoán của một mô hình.

Nếu điều này không thực hiện được, ta vẫn còn những hình thức thẩm định khác tuy không chặt chẽ bằng. Một hình thức là so sánh nhiều mô hình của cùng một hệ. Nếu chúng không thống nhất thì có biểu hiện là (ít nhất) một trong số các mô hình đó đã sai, và độ lớn của sự khác biệt chính là dấu hiệu về độ tin cậy của những ước tính đó.

Đến giờ chúng ta chỉ thấy được một mô hình vật lý, vì vậy mà một phần của đoạn thảo luận trên vẫn chưa thể làm rõ. Ta sẽ quay trở lại chủ đề này sau, nhưng trước hết cần tìm hiểu thêm về véc-tơ.

Véc-tơ với vai trò là biến đầu vào

Vì nhiều hàm lập sẵn chấp nhận véc-tơ làm đối số, nên sẽ không có gì lạ khi bạn có thể viết các hàm nhận vào véc-tơ. Sau đây là một ví dụ (ngốc nghếch):

function res = display_vector(X)
    X
end

Chẳng có gì đặc biệt về hàm này cả. Điểm khác biệt duy nhất so với hàm vô hướng mà ta gặp từ trước đó là tôi đã dùng một chữ cái viết in để gợi nhớ rằng X là một véc-tơ.

Sau đây là một ví dụ khác về một hàm không trả về giá trị nào; nó chỉ hiển thị giá trị của biến đầu vào:

>> display_vector(1:3)

X = 1     2     3

Tiếp theo là một ví dụ lý thú hơn, trong đó gói đoạn mã lệnh ở Mục [Rút gọn] để tính tổng các phần tử của một véc-tơ:

function res = mysum(X)
    total = 0;
    for i=1:length(X)
        total = total + X(i);
    end
    res = total;
end

Tôi gọi nó là mysum để tránh xung đột tên với hàm lập sẵn sum, nhưng về công dụng thì hai hàm giống nhau.

Sau đây là cách gọi nó từ Command Window:

>> total = mysum(1:3)

total = 6

Vì hàm này có một giá trị trả về nên tôi đã cố ý gán nó cho một biến.

Véc-tơ đóng vai trò là biến đầu ra

Cũng không có gì sai khi gán véc-tơ cho một biến đầu ra. Sau đây là một ví dụ trong đó gói đoạn mã lệnh ở Mục {apply}:

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

Lý tưởng nhất lẽ ra phải thay đổi tên của biến đầu ra thành Res, để nhắc rằng nó nhận một giá trị véc-tơ, nhưng tôi đã không làm vậy.

Sau đây là cách dùng myapply:

>> V = myapply(1:3)

V = 1     4     9

Hãy viết một hàm có tên là find_target để gói đoạn lệnh từ Mục [Tìm kiếm], để tìm vị trí của giá trị mong muốn trong một véc-tơ.

Véc-tơ hóa hàm của bạn

Những hàm tính trên véc-tơ thường cũng dùng được với các số vô hướng, vì MATLAB coi rằng số vô hướng là véc-tơ có chiều dài bằng 1.

>> mysum(17)

ans = 17

>> myapply(9)

ans = 81

Không may là, điều còn lại không phải luôn đúng. Nếu bạn viết một hàm để tính cho đầu vào là số vô hướng, thì nó có thể không dùng được cho véc-tơ.

Nhưng có trường hợp vẫn được! Nếu các toán tử và hàm bạn dùng trong thân hàm có thể tính được với véc-tơ thì hàm của bạn sẽ tính được cho véc-tơ.

Chẳng hạn, sau đây chính là hàm đầu tiên mà ta viết:

function res = myfunc (x)
    s = sin(x)
    c = cos(x)
    res = abs(s) + abs(c)
end

Này! Nó làm việc được với véc-tơ đấy:

>> Y = myfunc(1:3)

Y = 1.3818    1.3254    1.1311

Đến đây, tôi muốn dừng lại một chút để thừa nhận rằng tôi đã khắt khe một chút khi trình bày về MATLAB, vì có một số đặc điểm mà tôi nghĩ đã làm khó người mới học một cách không cần thiết. Nhưng rồi cuối cùng, ở đây ta thấy được những đặc điểm chứng tỏ sức mạnh của MATLAB.

Một số hàm khác ta đã viết lại không dùng được với véc-tơ, nhưng chúng có thể sửa được dễ dàng. Chẳng hạn, có hàm hypotenuse từ Mục {hypotenuse}:

function res = hypotenuse(a, b)
    res = sqrt(a^2 + b^2);
end

Hàm này không tính được với véc-tơ vì toán tử ^ dành cho phép lũy thừa ma trận, và chỉ tính cho các ma trận vuông.

>> hypotenuse(1:3, 1:3)
??? Error using ==> mpower
Matrix must be square.

Nhưng nếu bạn thay thế ^ bởi toán tử tính theo phần tử .^, nó lại chạy được!

>> A = [3,5,8];
>> B = [4,12,15];
>> C = hypotenuse(A, B)

C = 5    13    17

Ở trường hợp này, nó ghép cặp các phần tử tương ứng từ hai véc-tơ đầu vào, nên các phần tử của C là độ dài cạnh huyền lần lượt của các cặp (3, 4), (5, 12) and (8, 15).

Nói chung, nếu bạn muốn viết một hàm trong đó chỉ dùng các toán tử theo phần tử và các hàm làm việc trên véc-tơ, thì hàm mới viết cũng sẽ làm việc được với véc-tơ.

Tổng và hiệu

Một phép toán với véc-tơ quan trọng khác là tổng lũy tích, vốn nhận một véc-tơ đầu vào và tính một véc-tơ mới gồm tất cả các tổng thành phần của véc-tơ ban đầu. Theo kí hiệu toán, nếu V là véc-tơ ban đầu, thì các phần tử của tổng lũy tích, C, là:

Ci = ∑ j = 1iVj

Nói cách khác, phần tử thứ i của C là tổng của i phần tử đầu tiên trong V. MATLAB cung cấp một hàm có tên là cumsum để tính tổng lũy tích:

>> V = 1:5

V = 1     2     3     4     5

>> C = cumsum(V)

C = 1     3     6    10    15

Hãy viết một hàm có tên là cumulative_sum trong đó dùng một vòng lặp để tính tổng lũy tích của véc-tơ đầu vào.

Phép tính ngược với cumsumdiff, vốn tính hiệu giữa các phần tử kế tiếp trong véc-tơ đầu vào.

>> D = diff(C)

D = 2     3     4     5

Lưu ý rằng véc-tơ đầu ra ngắn hơn 1 phần tử so với véc-tơ đầu vào. Vì vậy, phiên bản diff trong MATLAB không thật sự là hàm ngược của cumsum. Nếu là hàm ngược thì ta đã có thể trông đợi cumsum(diff(X)) bằng X:

>> cumsum(diff(V))

ans = 1     2     3     4

Nhưng nó đã không thỏa mãn điều kiện trên.

Hãy viết một hàm mydiff để tính nghịch đảo của cumsum, sao cho cả cumsum(mydiff(X))mydiff(cumsum(X)) đều trả lại X.

Tích và thương

Dạng phép nhân của cumsumcumprod, dùng để tính tích số lũy tích. Theo kí hiệu toán học thì:

Pi = ∏ j=1iVj

Trong MATLAB, hàm đó như sau:

>> V = 1:5

V = 1     2     3     4     5

>> P = cumprod(V)

P = 1     2     6    24   120

Hãy viết một hàm tên là cumulative_prod trong đó dùng vòng lặp để tính tích số lũy tích từ véc-tơ đầu vào.

MATLAB không cung cấp một phiên bản phép nhân tương ứng với diff, mà lẽ ra đã có tên ratio, để tính tỉ số giữa các phần tử kế tiếp nhau trong véc-tơ đầu vào.

Hãy viết một hàm tên là myratio để tính nghịch đảo của cumprod, sao cho cả cumprod(myratio(X))myratio(cumprod(X)) đều trả lại X.

Bạn có thể dùng một vòng lặp, hoặc nếu khéo hơn, có thể lợi dụng hệ thức elna + lnb = ab.

Nếu áp dụng myratio cho một véc-tơ có chứa các số Fibonacci, bạn có thể khẳng định được rằng tỉ số giữa các phần tử kế tiếp nhau hội tụ về tỉ số vàng, $(1+\sqrt{5})/2$ (xem Bài tập {fibratio}).

Kiểm tra sự tồn tại

Đôi khi ta phải kiểm tra các phần tử của một véc-tơ xem nếu có bất kì phần tử nào thỏa mãn một điều kiện cho trước hay không. Chẳng hạn, bạn muốn biết rằng có phần tử số dương trong véc-tơ hay không. Theo logic học, điều kiện này được gọi là kiểm tra sự tồn tại, và nó được kí hiệu bởi dấu ∃ , đọc là “tồn tại.” Chẳng hạn, biểu thức sau

∃ x thuộc S: x > 0

nghĩa là “tồn tại phần tử x nào đó trong tập hợp S sao cho x > 0.” Trong MATLAB, ý này có thể được thể hiện tự nhiên bằng một hàm logic như exists, vốn trả lại 1 nếu có phần tử như vậy và 0 nếu không có.

function res = exists(X)
    for i=1:length(X)
        if X(i) > 0
            res = 1;
            return
        end
    end
    res = 0;
end

Trước đây chưa bắt gặp lệnh return; nó tương tự như break, chỉ khác là nó thoát khỏi cả hàm chứ không chỉ vòng lặp. Điều đó rất cần đến trong trường hợp này vì ngay khi tìm được một phần tử dương, ta biết được câu trả lời (có tồn tại!) và có thể kết thúc hàm lập tức mà không cần xét đến các phần tử còn lại.

Nếu ta thoát ở cuối vòng lặp, điều đó nghĩa là ta không thể tìm thấy giá trị mong muốn (vì nếu có, ta đã gặp lệnh return).

Kiểm tra sự toàn vẹn

Một phép toán véc-tơ thường gặp khác là kiểm tra xem tất cả các phần tử có cùng thỏa mãn một điều kiện không; nó được gọi là kiểm tra sự toàn vẹn và được kí hiệu bằng dấu ∀  và được đọc là “với mọi.” Vì vậy biểu thức này

∀ x thuộc S: x > 0

có nghĩa là “với mọi phần tử x trong tập hợp S, x > 0.”

Một cách khá ngốc nghếch để lượng giá biểu thức này trong MATLAB là đếm số phần tử thỏa mãn điều kiện trên. Một cách tốt hơn là rút gọn bài toán về kiểm tra sự tồn tại; nghĩa là viết lại

∀ x thuộc S: x > 0

theo dạng

 ∼ ∃ x thuộc S: x ≤ 0

Trong đó  ∼ ∃  nghĩa là “không tồn tại.” Nói cách khác, kiểm tra để đảm bảo tất cả các phần tử phải dương thì cũng như kiểm tra điều ngược lại, có tồn tại phần tử không dương.

Hãy viết một hàm có tên forall nhận vào một véc-tơ và trả lại giá trị 1 nếu tất cả các phần tử đều dương và 0 nếu có bất kì phần tử nào không dương.

Véc-tơ logic

Khi áp dụng một toán tử logic cho một véc-tơ, kết quả là một véc-tơ logic; nghĩa là một véc-tơ trong đó các phần tử đều là những giá trị logic 1 và 0.

>> V = -3:3

V = -3    -2    -1     0     1     2     3

>> L = V>0

L =  0     0     0     0     1     1     1

Ở ví dụ này, L là véc-tơ logic có các phần tử tương ứng với các phần tử của V. Với mỗi phần tử dương của V, phần tử tương ứng của L bằng 1.

Véc-tơ logic có thể được dùng như cờ để lưu giữ trạng thái của một điều kiện. Chúng thường được dùng với hàm find, vốn nhận vào một véc-tơ logic và trả lạ một véc-tơ bao gồm các chỉ số của những phần tử “đúng.”

Áp dụng find vào L ta được

>> find(L)

ans = 5     6     7

nghĩa là các phần tử thứ 5, 6 và 7 có giá trị bằng 1.

Nếu không có phần tử “đúng” nào, kết quả sẽ là véc-tơ rỗng.

>> find(V>10)

ans = Empty matrix: 1-by-0

Ví dụ này tính toán véc-tơ logic và truyền nó làm đối số cho find mà không gán nó vào một biến trung gian. Bạn có thể đọc đoạn mã lệnh này theo cách trừu tượng là “tìm tất cả những chỉ số của các phần tử trong V có giá trị lớn hơn 10.”

Bạn cũng có thể dùng find để viết lại exists cho gọn hơn:

function res = exists(X)
    L = find(X>0)
    res = length(L) > 0
end

Hãy viết một phiên bản của forall có dùng find.

Thuật ngữ

hàm cấp cao nhất:
Hàm đầu tiên trong một tập tin M; là hàm duy nhất có thể gọi đến từ Command Window hoặc từ một tập tin khác.

hàm phụ trợ:
Hàm trong tập tin M nhưng không phải hàm cấp cao nhất; nó chỉ có thể được gọi từ một hàm khác trong cùng tập tin.

mô hình hóa vật lý:
Quá trình đưa ra phỏng đoán về các hệ vật lý cũng như giải thích biểu hiện của chúng.

hệ vật lý:
Thứ tồn tại trong thế giới thực mà chúng ta quan tâm nghiên cứu.

mô hình :
Sự mô tả được đơn giản hóa của hệ vật lý, rất thích hợp cho việc phân tích và mô phỏng.

trừu tượng hóa:
Quá trình xây dựng một mô hình bằng cách quyết định những yếu tố nào cần được giản hóa hoặc lược bỏ.

thẩm định:
Kiểm tra xem mô hình có đạt yêu cầu sử dụng hay không.

kiểm tra sự tồn tại:
Một điều kiện logic nhằm diễn đạt ý “có tồn tại” một phần tử thỏa mãn một thuộc tính nhất định trong tập hợp cho trước.

kiểm tra sự toàn vẹn:
Một điều kiện logic nhằm diễn đạt ý tất cả các phần tử trong tập hợp đều có chung một thuộc tính nhất định.

véc-tơ logic:
Một véc-tơ gồm các giá trị logic 1 hoặc 0, thường là kết quả của phép áp dụng một toán tử logic vào một véc-tơ ban đầu.

4 phản hồi

Filed under MatLab, Mô hình hóa, Sách

4 responses to “Chương 7: Hàm số của 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

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