I. Lời nói đầu
Như các bạn đã biết, trong chương trình Toán phổ thông, chúng ta đã được nghiên cứu khá nhiều về các bài toán liên quan tới chủ đề Hình học. Tuy nhiên, không chỉ trong môn Toán, mà trong môn Tin học, các bài toán Hình học cũng là một chủ đề khá quen thuộc, thậm chí còn "khó nhai" trong các kì thi lập trình. Hình học tính toán (Computational Geometry) là một nhánh của ngành Khoa học máy tính, chuyên nghiên cứu về thuật toán giải quyết các bài toán liên quan tới đối tượng hình học. Trong Toán học và Công nghệ hiện đại, hình học tính toán có ứng dụng rất rộng rãi trong nhiều lĩnh vực như đồ họa máy tính, thiết kế, mô phỏng,...
Chính vì tầm quan trọng của chủ đề này, mà trong series bài viết sau đây, tôi sẽ giới thiệu tới các bạn những khái niệm từ cơ bản tới nâng cao về hình học tính toán thường xuất hiện trong các kì thi lập trình, sau đó khảo sát một số bài toán và thuật toán căn bản. Những công thức hình học thường được thừa nhận không chứng minh, để giúp cho người đọc nhanh chóng nắm bắt ý tưởng (và thực ra việc chứng minh cũng không quá cần thiết trong lập trình :3).
II. Các khái niệm cơ bản thường dùng
1. Vector
Vector là một đối tượng có cả độ lớn và hướng. Hướng của vector là hướng từ điểm đầu đến điểm cuối của nó.
Một vector thường được biểu diễn bằng một tia (một đoạn thẳng có hướng), hoặc bằng đồ thị dưới dạng một mũi tên nối từ điểm đầu tới điểm cuối và được ký hiệu là .
2. Hệ tọa độ Descartes
Đây là khái niệm rất căn bản, thường được biết đến với tên gọi quen thuộc là hệ trục tọa độ hệ tọa độ hai chiều,...
Trong mặt phẳng, ta chọn một điểm và hai vector đơn vị (vector có độ dài) là và vuông góc với nhau. Khi đó, bộ ba được gọi là Hệ tọa độ Descartes vuông góc, hay còn gọi là một mục tiêu Euclid hai chiều, một mục tiêu trực chuẩn.
Ta kí hiệu mục tiêu đó là với và là hai tia gốc có vector chỉ phương lần lượt là và .
Mục tiêu Euclid
Điểm được gọi là gốc tọa độ, đường thẳng được gọi là trục hoành và được gọi là trục tung.
Ngoài ra, hệ tọa độ Descartes cũng có thể được phát triển trên không gian ba chiều, bằng cách sử dụng thêm một vector đơn vị có chiều hướng từ dưới lên trên, sau đó dùng một tia có vector chỉ phương chính là .
Hệ trục tọa độ Descartes trong không gian 3 chiều
3. Tọa độ
Tọa độ của vector
Xét mặt phẳng trực chuẩn với một vector bất kì của mặt phẳng, thì sẽ tồn tại duy nhất một cặp số thực sao cho . Cặp số khi đó được gọi là tọa độ của vector trong mặt phẳng trực chuẩn, kí hiệu là hoặc .
Tọa độ của điểm
Xét mặt phẳng trực chuẩn ta lấy một điểm . Khi đó tọa độ của vector được gọi là tọa độ của điểm kí hiệu hoặc .
Để tìm tọa độ của điểm trong hệ trục tọa độ không gian ba chiều, ta cũng làm theo cách hoàn toàn tương tự, chỉ cần bổ sung thêm cao độ của điểm theo trục .
Liên hệ giữa tọa độ của vector và tọa độ của điểm
Trong hình học phẳng, vector có thể được biểu diễn bằng một cặp số cho biết tọa độ của vector, được xác định bằng hiệu các tọa độ tương ứng của điểm cuối với điểm đầu :
Lấy ví dụ, một vector từ đến có thể được biểu diễn bởi .
4. Một số phép toán với vector
3.1. Độ lớn của vector
Độ lớn của một vector được xác định bằng khoảng cách giữa điểm đầu và điểm cuối của nó. Giả sử ta có vector trong mặt phẳng tọa độ hai chiều, thì độ lớn của nó, kí hiệu là sẽ được xác định dựa trên định lý Pythagoras như sau:
3.2. Phép cộng hai vector
Với hai vector và tổng của và được tính bằng công thức:
Phép cộng vector cũng có tính chất giao hoán. Nếu như biểu diễn hình học, thì vector tổng của và chính là đường chéo của hình bình hành dựng từ hai vector ban đầu trên mặt phẳng tọa độ. Do đó quy tắc tính tổng hai vector còn gọi là quy tắc hình bình hành.
3.3. Phép trừ hai vector
Vector đối
Trước tiên, ta cùng tìm hiểu khái niệm về vector đối.
Cho vector một vector có cùng độ lớn nhưng ngược hướng với được gọi là vector đối của kí hiệu là . Mỗi vector đều có vector đối, chẳng hạn vector đối của là nghĩa là .
Tọa độ của vector đối sẽ bằng số đối của tọa độ vector ban đầu.
Hiệu hai vector
Sử dụng khái niệm về vector đối, ta có hiệu của hai vector và chính là tổng của và .
Nếu 2 vector có chung điểm đầu thì vector hiệu có hướng từ điểm cuối của đến điểm cuối của . Ví dụ: . Nếu hai vector có chung điểm cuối thì vector hiệu có hướng từ điểm đầu của đến điểm đầu của . Ví dụ: .
3.4. Tích vô hướng (dot product)
Hai phép toán khá đặc biệt đối với vector là tích vô hướng và tích có hướng. Không giống như cộng và trừ, hai phép toán này hơi khó để tưởng tượng.
Tích vô hướng (còn gọi là tích chấm) có thể biểu diễn bằng cả đại số lẫn hình học. Hai cách biểu diễn này đều tương đương trong hệ tọa độ Descartes.
Theo đại số, tích vô hướng của hai vector là tổng các tích tọa độ tương ứng giữa chúng. Ví dụ, hai vector và có tích vô hướng là:
Còn theo hình học, tích vô hướng là tích giữa độ lớn của hai vector và của góc hợp giữa chúng. hai vector và có tích vô hướng là:
với là góc hợp bởi và
Ví dụ: Tính tích vô hướng giữa hai vector và :
Tính theo đại số:
Tính theo hình học:
Từ các định nghĩa về tích vô hướng, ta cũng có thể tính được góc hợp bởi và theo công thức:
Ngoài ra, công thức trên không chỉ đúng trong hình học phẳng, mà ta có thể áp dụng tích vô hướng cho các vector có số chiều tùy ý, đẳng thức trên vẫn hoàn toàn chính xác!
3.5. Tích có hướng (cross product)
Trong không gian ba chiều
Tích có hướng là một phép nhân vector trong không gian ba chiều. Nó khác với tích vô hướng ở chỗ, kết quả thu được sẽ là một vector, nghĩa là đại lượng có hướng. Vector này sẽ vuông góc với mặt phẳng chứa hai vector đầu vào của phép nhân.
Định nghĩa tích có hướng như sau:
Trong đó:
- là góc giữa và .
- là vector đơn vị vuông góc với và . Thực tế, có tới hai vector thỏa mãn điều kiện vuông góc là và do đó hướng của phụ thuộc vào quy tắc bàn tay phải, được mô tả như hình vẽ dưới đây:
Trong không gian hai chiều
Nếu xét trong hình học phẳng thì vector kết quả lúc này vuông góc và có hướng đi vào/ra mặt phẳng đang xét, do đó ta có thể bỏ qua đặc điểm về hướng, và sử dụng tích có hướng như là một đại lượng vô hướng.
Tương tự tích vô hướng, tích có hướng trong không gian hai chiều cũng có thể được định nghĩa bằng hai cách:
-
Theo đại số, tích có hướng giữa hai vector và được định nghĩa bằng công thức:
-
Theo hình học, tích có hướng giữa hai vector và được định nghĩa bằng công thức: với là góc hợp bởi hai vector tính từ tới và ngược chiều kim đồng hồ (góc này được gọi là góc định hướng). Ta lại biết rằng, với một góc thỏa mãn thì nên nếu thì tích có hướng dương, ngược lại tích có hướng âm (hoặc có thể xét theo quy tắc: Nếu góc định hướng có chiều ngược kim đồng hồ thì nó mang dấu dương, ngược lại mang dấu âm).
Ta cũng suy ra được công thức tính của góc định hướng giữa hai vector và như sau:
Ví dụ: Tính tích có hướng của hai vector và :
Tính bằng đại số:
Tính bằng hình học:
Ứng dụng quan trọng thứ nhất của tích có hướng trong hình học phẳng là bằng với diện tích của hình bình hành có hai cạnh bên là và :
Do đó, diện tích của một tam giác còn bằng một nửa giá trị tuyệt đối của tích có hướng với hai vector thành phần là hai cạnh của tam giác (*).
Một ứng dụng khác của tích có hướng là khảo sát chiều: Giả sử ta đi từ điểm sang điểm theo đường thằng và đi tiếp sang điểm theo đường thẳng, khi đó:
<center>
- Tích có hướng sẽ là số dương nếu như chỗ rẽ tại là "rẽ trái" (bẻ góc ngược chiều kim đồng hồ). Đây gọi là công thức CCW.
- Tích có hướng sẽ là số âm nếu như chỗ rẽ tại là "rẽ phải" (bẻ góc ngược thuận kim đồng hồ). Đây gọi là công thức CW.
- Tích có hướng sẽ bằng nếu như ba điểm thẳng hàng.
Rẽ trái và rẽ phải
</center>Các công thức nói trên tưởng chừng như vô dụng, nhưng chúng sẽ rất có ý nghĩa trong một số thuật toán cụ thể, chẳng hạn như thuật toán tìm Bao lồi (sẽ nói tới ở một chuyên đề khác).
5. Khoảng cách giữa các điểm và đường thẳng
Tìm khoảng cách giữa hai điểm trên mặt phẳng hai chiều
Đây là công thức rất quan trọng, thường xuyên xuất hiện trong các bài toán về hình học. Để tính khoảng cách giữa hai điểm và trên mặt phẳng tọa độ, ta sử dụng công thức sau:
Công thức trên còn được gọi là công thức khoảng cách Euclid, để phân biệt với công thức khoảng cách Mahattan.
Tìm khoảng cách giữa một điểm và một đường thẳng trên mặt phẳng hai chiều
Tìm khoảng cách giữa điểm và đường thẳng cũng là một vấn đề rất thường gặp trong các bài toán hình học.
Hình 1: Khoảng cách giữa điểm và đường thẳng
Lấy ví dụ, ta có ba điểm và cần tìm khoảng cách từ đến đường thẳng đi qua và . Bước đầu tiên, các bạn hãy tính hai vector và . Sau đó, ta tính tích có hướng lấy giá trị tuyệt đối của kết quả rồi chia cho . Kết quả thu được chính là khoảng cách cần tìm.
Chứng minh: Xét đặt là đường cao kẻ từ (khoảng cách từ tới ) và đáy tương ứng là . Ta có công thức:
Mà theo nhận định (*) ở mục 3.5, thì diện tích một tam giác bằng một nửa giá trị tuyệt đối của tích có hướng với hai vector thành phần là hai cạnh của tam giác, tức là:
Từ và suy ra:
Nhưng nếu ta chỉ muốn tìm khoảng cách từ đoạn thẳng tới điểm thay vì cả đường thẳng thì sao? Trong trường hợp này, ta cần tìm khoảng cách từ tới điểm gần nó nhất trên đoạn thẳng mà điểm gần nhất có thể là một trong hai đầu mút của đoạn thẳng thay vì là một điểm nào đó trên đường thẳng. Trong hình 1 ở trên, điểm gần nhất trên đường thẳng không nằm giữa và mà là nằm tại .
Có vài cách khác nhau để xử lý trường hợp này, một trong số đó là tích vô hướng. Đầu tiên, kiểm tra xem điểm gần nhất trên đường thẳng có ra khỏi hay không bằng cách tính . Nếu tích này âm, nghĩa là góc giữa và là góc tù (do với góc thỏa mãn thì ), do đó điểm gần nhất trên đoạn sẽ là .
Tương tự, nếu , điểm gần nhất là . Nếu cả hai tích vô hướng đều lớn hơn hoặc bằng thì điểm gần nhất sẽ nằm giữa và .
Cài đặt
// Struct lưu vector và định nghĩa một số phép toán: Tích chấm, tích chéo, độ dài.
struct Vector
{ double x, y; Vector(double _x, double _y) { x = _x; y = _y; } double dot_product(const Vector &other) { return x * other.x + y * other.y; } double cross_product(const Vector &other) { return x * other.y - y * other.x; } double length() const { return sqrt(x * x + y * y); }
} typedef Vector Point; // Tính vector AB = B - A.
Vector operator - (const Point &B, const Point &A) { return Vector(B.x - A.x, B.y - A.y);
} // Nếu is_segment = true tức là AB là một đoạn thẳng thay vì đường thẳng.
double line_point_dist(const Point &A, const Point &B, const Point &C, bool is_segment) { double dist = abs((B - A).cross_product(C - A)) / (A - B).length(); if (is_segment) { double dot1 = (A - B).dot_product(C - B); if (dot1 < 0) return (B - C).length(); double dot2 = (B - A).dot_product(C - A); if (dot2 < 0) return (A - C).length(); } return dist;
}
III. Luyện tập
Các bạn có thể tham khảo contest Codeforces Gym 100168, một contest gồm toàn các bài tập liên quan tới hình học (mặc dù tiếng Nga nhưng rất ngắn gọn nên có thể dùng Google Dịch được ngay).
Một số bài tập có liên quan đến nội dung bài viết này có thể tóm tắt như sau:
- Codeforces Gym - 100168L: Tính độ dài (độ lớn) vector.
- Codeforces Gym - 100168D: Tính diện tích tam giác.
- CSES - Polygon Area: tính diện tích đa giác Codeforces Gym - 100168F: Tính khoảng cách từ 1 điểm đến một đường thẳng có dạng .
- Codeforces Gym - 100168G: Tính khoảng cách từ một điểm đến một đường thẳng đi qua hai điểm
- Codeforces Gym - 100168H: Tính khoảng cách từ một điểm đến một tia.
- Codeforces Gym - 100168I: Tính khoảng cách từ một điểm đến một đoạn thẳng.
IV. Tài liệu tham khảo
- https://vnoi.info/wiki/algo/geometry/basic-geometry-1.md#trong-không-gian-2-chiều-mặt-phẳng
- Tài liệu giáo khoa chuyên Tin quyển 2 - Thầy Hồ Sĩ Đàm.
- https://en.wikipedia.org/wiki/Computational_geometry