Thuật toán tìm kiếm

 


Thuật toán tìm kiếm

Hôm nay là một ngày đẹp trời, bạn thức dậy và không muốn đi làm. Bạn tới thư viện, nơi có 200.000 quyển sách khác nhau và cần phải tìm ra quyển sách mà mình xác định đọc từ đầu. Đây là một ví dụ về việc áp dụng thuật toán tìm kiếm trong đời sống. Vậy thuật toán tìm kiếm là gì?

Khái niệm về thuật toán tìm kiếm


Và chính xác như định nghĩa của từ "tìm kiếm", mình xin định nghĩa về thuật toán này như sau:

Thuật toán tìm kiếm là một tập hợp các hành động giúp chúng ta tìm ra kết quả của một bài toán, tuân theo một quy luật chung. Thuật toán này được thực hiện trên ý tưởng thâu tóm vấn đề chung của các dữ liệu trong trường hợp bài toán không thể rút ra đáp án ngay lập tức.


Khi bạn tới thư viện lần đầu, bạn không thể biết ngay cuốn sách mình cần ở đâu. Bỏ qua trường hợp bạn hỏi người quản lí, trong trường hợp xấu nhất bạn sẽ phải nhìn qua bìa sách của từng cuốn một cho đến khi tìm được nó. Đây chính xác là một thuật toán tìm kiếm.


Một vài thuật toán tìm kiếm cơ bản

(trên cơ sở dữ liệu một chiều)

Tìm kiếm tuyến tính




Nội dung của thuật toán này như sau:

Duyệt từ đầu đến cuối, bao giờ gặp thì dừng lại. 

 

Độ phức tạp: $O(N)$


Đây là thuật toán tìm kiếm đơn giản nhất bạn có thể nghĩ ra mà không cần kĩ năng hay kế hoạch phức tạp gì cả. Trong trường hợp thư viện kể trên sắp xếp kệ sách 1 cách lộn xộn, bạn vẫn còn một cách để tìm đó là tìm kiếm tuyến tính. Trong thực tế, nó là phương pháp được sử dụng nhiều nhất, và cũng là phương pháp cuối cùng mà bạn có thể dùng.


Implementation của thuật toán này khá đơn giản:

int linear_search()
{
	int n; // n is the right_most value
	for (int i = 1; i <= n; i++) // suggest that the left_most value is 1
	{
		if (correct(i)) return i;
	}
}


Tìm kiếm nhị phân





Nội dung của thuật toán này là:

Chia đôi không gian sau mỗi lần chia, loại bỏ phần không thể chứa kết quả. 


 Độ phức tạp: $O(logN)$


Giờ hãy giả sử thư viện chúng ta đang nhắc tới sắp xếp những quyển sách theo số trang mà chúng có, và quyển sách của bạn chứa 250 trang. Bạn đoán đúng rồi, chúng ta không cần bắt đầu từ đầu và kết thúc ở cuối dãy sách, sẽ có một khoảng nào đó của kệ sách chỉ chứa những quyển có chính xác 250 trang, ta chỉ cần quan tâm tới nó mà thôi.


Vậy cụ thể ta sẽ tìm khoảng này như thế nào? Sau đây là cách làm bằng tìm kiếm nhị phân.


  • Đầu tiên, hãy chắc chắn là dữ liệu đã được sắp xếp tăng dần.
  • Gọi vị trí đầu là cuối là $left$ và $right$. Tại mỗi bước, ta lấy vị trí $mid = \left \lfloor \frac{left + right}{2} \right \rfloor$ và xác định dữ liệu tại vị trí đó.
  • Gọi $x$ là dữ liệu tại vị trí $mid$, $t$ là giá trị chúng ta đang tìm kiếm. Nếu $x > t$, đặt $right = t - 1$. Nếu $x < t$, đặt $left = t + 1$. Còn nếu $x = t$, ta có luôn đáp án.
  • Tiếp tục lặp lại quy trình đến khi chỉ còn $1$ đơn vị trong phạm vi tìm kiếm.

Áp dụng vào ví dụ trên, giả sử kệ sách có số trang sách như sau:$$[100, 150, 150, 180, 200, 200, 230, 230, 250, 250, 300, 320, 370, 400, 480, 500]$$ Đây là các bước bạn cần thực hiện:

  • Đặt vị trí $left = 1, right = 16$, từ đó ta có $mid = \left \lfloor \frac{left + right}{2} \right \rfloor = \left \lfloor \frac{1 + 16}{2} \right \rfloor = 8$. Cuốn sách tại vị trí này có số trang là $230 < 250$, ta đặt $left = 8 + 1 = 9$.
  • $left = 9, right = 16$, từ đó ta có $mid = \left \lfloor \frac{left + right}{2} \right \rfloor = \left \lfloor \frac{9 + 16}{2} \right \rfloor = 12$. Cuốn sách tại vị trí này có số trang là $320 > 250$, ta đặt $right = 12 - 1 = 11$.
  • $left = 9, right = 11$, từ đó ta có $mid = \left \lfloor \frac{left + right}{2} \right \rfloor = \left \lfloor \frac{9 + 11}{2} \right \rfloor = 10$. Cuốn sách tại vị trí này có số trang là $250> 250$, dừng tìm kiếm. Tại đây ta chỉ cần tìm từ vị trí thứ $10$ sang hai bên, dừng khi số trang của $1$ nhỏ hơn hoặc lớn hơn $250$.
Implementation của thuật toán:
int binary_search()
{
	int n; // n is the right_most value
	int left = 1, right = n; // assign the left_most and right_most values
	while (left < right)
	{
		int mid = (left + right) / 2;
		if (check(mid) > answer)
		{
			right = mid - 1;
		}
		else if (check(mid) < answer)
		{
			left = mid - 1;
		}
		else
		{
			return mid;
		}
	}
	if (check(l) == answer) return l; // at this time l = r, if we haven't found the answer yet, check the final case
	return -1; // or report that there are no answers.
}



Đây là các thuật toán tìm kiếm cơ bản trên không gian một chiều, ngoài ra còn các thuật toán tìm kiếm trìu tượng và tìm kiếm trên không gian đa chiều (ví dụ như tìm đường trong mê cung), mình sẽ đề cập trong các bài viết sau.

Chúc bạn thành công.

Nhận xét