Các chức năng tiệm cận là gì? Một tiệm cận là gì?
Với một hàm f (n) mô tả lượng tài nguyên (thời gian CPU, RAM, dung lượng ổ đĩa, v.v.) được sử dụng bởi thuật toán khi áp dụng cho đầu vào có kích thước n , chúng tôi xác định tối đa ba ký hiệu tiệm cận để mô tả hiệu suất của nó n lớn .
Một tiệm cận (hoặc chức năng tiệm cận) chỉ đơn giản là một số chức năng (hoặc quan hệ) khác g (n) mà f (n) ngày càng gần với khi n ngày càng lớn hơn, nhưng không bao giờ đạt được. Ưu điểm của việc nói về các hàm tiệm cận là chúng thường đơn giản hơn nhiều để nói về ngay cả khi biểu thức cho f (n) cực kỳ phức tạp. Các hàm tiệm cận được sử dụng như một phần của các ký hiệu giới hạn giới hạn f (n) ở trên hoặc bên dưới.
.
Ba ký hiệu giới hạn tiệm cận là gì và chúng khác nhau như thế nào?
Tất cả ba ký hiệu được sử dụng như thế này:
f (n) = O (g (n))
Trong đó f (n) ở đây là hàm quan tâm và g (n) là một số hàm tiệm cận khác mà bạn đang cố gắng xấp xỉ f (n) với. Điều này không nên được coi là một sự bình đẳng theo nghĩa chặt chẽ, mà là một tuyên bố chính thức giữa tốc độ f (n) tăng trưởng so với n so với g (n) , khi n trở nên lớn. Những người theo chủ nghĩa thuần túy sẽ thường sử dụng ký hiệu thay thế f (n) O (g (n)) để nhấn mạnh rằng ký hiệu O (g (n)) thực sự là một nhóm các hàm có chung tốc độ tăng trưởng.
Ký hiệu Big-(Theta) nêu sự bình đẳng về sự tăng trưởng của f (n) lên đến một yếu tố không đổi (nhiều hơn về điều này sau). Nó hành xử tương tự như một =
nhà điều hành cho tốc độ tăng trưởng.
Ký hiệu Big-O mô tả một hướng trên về sự tăng trưởng của f (n) . Nó hành xử tương tự như một ≤
nhà điều hành cho tốc độ tăng trưởng.
Ký hiệu Big- (Omega) mô tả mức thấp hơn về tốc độ tăng trưởng của f (n) . Nó hành xử tương tự như một ≥
nhà điều hành cho tốc độ tăng trưởng.
Có nhiều ký hiệu tiệm cận khác , nhưng chúng không xảy ra gần như thường xuyên trong tài liệu khoa học máy tính.
Các ký hiệu Big-O và ilk của nó thường là một cách để so sánh độ phức tạp của thời gian .
Thời gian phức tạp là gì?
Độ phức tạp thời gian là một thuật ngữ ưa thích cho lượng thời gian T (n) mà thuật toán cần để thực thi như là một hàm có kích thước đầu vào n . Điều này có thể được đo bằng lượng thời gian thực (ví dụ giây), số lượng lệnh CPU, v.v. Thông thường người ta cho rằng thuật toán sẽ chạy trên máy tính kiến trúc von Neumann hàng ngày của bạn . Nhưng tất nhiên bạn có thể sử dụng độ phức tạp thời gian để nói về các hệ thống máy tính kỳ lạ hơn, nơi mọi thứ có thể khác!
Người ta cũng thường nói về sự phức tạp của không gian bằng cách sử dụng ký hiệu Big-O. Độ phức tạp không gian là dung lượng bộ nhớ (lưu trữ) cần thiết để hoàn thành thuật toán, có thể là RAM, đĩa, v.v.
Nó có thể là trường hợp một thuật toán chậm hơn nhưng sử dụng ít bộ nhớ hơn, trong khi thuật toán khác nhanh hơn nhưng sử dụng nhiều bộ nhớ hơn. Mỗi có thể phù hợp hơn trong các trường hợp khác nhau, nếu tài nguyên bị hạn chế khác nhau. Ví dụ, bộ xử lý nhúng có thể có bộ nhớ hạn chế và ưu tiên thuật toán chậm hơn, trong khi máy chủ trong trung tâm dữ liệu có thể có dung lượng bộ nhớ lớn và ưu tiên thuật toán nhanh hơn.
Tính toán
Tính toán của thuật toán là một chủ đề có thể điền vào một cuốn sách giáo khoa nhỏ hoặc khoảng nửa học kỳ của lớp đại học: phần này sẽ bao gồm những điều cơ bản.
Cho hàm f (n) trong mã giả:
int f(n) {
int x = 0;
for (int i = 1 to n) {
for (int j = 1 to n) {
++x;
}
}
return x;
}
Sự phức tạp thời gian là gì?
Vòng lặp bên ngoài chạy n lần. Đối với mỗi lần vòng lặp bên ngoài chạy, vòng lặp bên trong chạy n lần. Điều này đặt thời gian chạy tại T (n) = n 2 .
Hãy xem xét một chức năng thứ hai:
int g(n) {
int x = 0;
for (int k = 1 to 2) {
for (int i = 1 to n) {
for (int j = 1 to n) {
++x;
}
}
}
return x;
}
Vòng lặp bên ngoài chạy hai lần. Vòng lặp giữa chạy n lần. Đối với mỗi lần vòng lặp giữa chạy, vòng lặp bên trong chạy n lần. Điều này đặt thời gian chạy ở T (n) = 2n 2 .
Bây giờ câu hỏi là, thời gian chạy tiệm cận của cả hai chức năng là gì?
Để tính toán điều này, chúng tôi thực hiện hai bước:
- Loại bỏ hằng số. Khi các thuật toán tăng thời gian do đầu vào, các thuật ngữ khác chi phối thời gian chạy, làm cho chúng không quan trọng.
- Loại bỏ tất cả nhưng thuật ngữ lớn nhất. Khi n đi đến vô cùng, n 2 nhanh chóng vượt qua n .
Chìa khóa ở đây là tập trung vào các thuật ngữ chi phối và đơn giản hóa các thuật ngữ đó .
T (n) = n 2 ∈ ϴ (n 2 )
T (n) = 2n 2 ∈ ϴ (n 2 )
Nếu chúng ta có một thuật toán khác có nhiều thuật ngữ, chúng ta sẽ đơn giản hóa nó bằng các quy tắc tương tự:
T (n) = 2n 2 + 4n + 7 ∈ (n 2 )
Chìa khóa với tất cả các thuật toán này là chúng tôi tập trung vào các thuật ngữ lớn nhất và loại bỏ hằng số . Chúng tôi không nhìn vào thời gian chạy thực tế, nhưng sự phức tạp tương đối .
Tính toán Big-và Big-O
Trước hết, được cảnh báo rằng trong văn học không chính thức , Hồi Big-O thường được coi là từ đồng nghĩa với Big-, có lẽ vì các chữ cái Hy Lạp rất khó gõ. Vì vậy, nếu ai đó bất ngờ hỏi bạn về Big-O của thuật toán, họ có thể muốn Big-.
Bây giờ nếu bạn thực sự muốn tính toán Big-Ω và Big-O theo các nghĩa chính thức được xác định trước đó, bạn có một vấn đề lớn: có vô số mô tả Big-Ω và Big-O cho bất kỳ chức năng nào! Nó giống như hỏi những con số nhỏ hơn hoặc bằng 42 là gì. Có nhiều khả năng.
Đối với thuật toán có T (n) (n 2 ) , bất kỳ điều nào sau đây là các câu lệnh hợp lệ chính thức để thực hiện:
- T (n) O (n 2 )
- T (n) O (n 3 )
- T (n) O (n 5 )
- T (n) ∈ O (n 12345 × e n )
- T (n) ∈ Ω (n 2 )
- T (n) ∈ Ω (n)
- T (n) ∈ (log (n))
- T (n) (log (log (n)))
- T (n) ∈ (1)
Nhưng không đúng khi nói T (n) O (n) hoặc T (n) ∈ (n 3 ) .
Độ phức tạp tương đối là gì? Có những lớp thuật toán nào?
Nếu chúng ta so sánh hai thuật toán khác nhau, độ phức tạp của chúng khi đầu vào chuyển sang vô cùng thường sẽ tăng lên. Nếu chúng ta xem xét các loại thuật toán khác nhau, chúng có thể giữ nguyên tương đối giống nhau (giả sử, khác nhau bởi một yếu tố không đổi) hoặc có thể phân kỳ rất lớn. Đây là lý do để thực hiện phân tích Big-O: để xác định xem thuật toán có thực hiện hợp lý với đầu vào lớn hay không.
Các lớp thuật toán được phân tích như sau:
(1) - không đổi. Ví dụ: chọn số đầu tiên trong danh sách sẽ luôn mất cùng một lượng thời gian.
(N) - tuyến tính. Ví dụ, lặp lại một danh sách sẽ luôn mất thời gian tỷ lệ thuận với kích thước danh sách, n .
(Log (n)) - logarit (cơ sở thường không quan trọng). Các thuật toán phân chia không gian đầu vào ở mỗi bước, chẳng hạn như tìm kiếm nhị phân, là ví dụ.
(N × log (n)) - logarit lần tuyến tính (Hồi tuyến tính tuyến tính). Các thuật toán này thường phân chia và chinh phục ( log (n) ) trong khi vẫn lặp ( n ) tất cả các đầu vào. Nhiều thuật toán sắp xếp phổ biến (hợp nhất sắp xếp, Timsort) thuộc loại này.
(N m ) - đa thức ( n nâng lên bất kỳ m không đổi ). Đây là một lớp phức tạp rất phổ biến, thường được tìm thấy trong các vòng lặp lồng nhau.
(M n ) - hàm mũ (bất kỳ m hằng số nào được nâng lên n ). Nhiều thuật toán đệ quy và đồ thị thuộc loại này.
(N!) - giai thừa. Một số đồ thị và thuật toán tổ hợp là độ phức tạp giai thừa.
Điều này có liên quan gì đến trường hợp tốt nhất / trung bình / tồi tệ nhất không?
Không. Big-O và gia đình ký hiệu của nó nói về một hàm toán học cụ thể . Chúng là các công cụ toán học được sử dụng để giúp đặc trưng hóa hiệu quả của các thuật toán, nhưng khái niệm tốt nhất / trung bình / trường hợp xấu nhất không liên quan đến lý thuyết về tốc độ tăng trưởng được mô tả ở đây.
Để nói về Big-O của một thuật toán, người ta phải cam kết với một mô hình toán học cụ thể của một thuật toán với chính xác một tham số n
, được cho là mô tả kích thước kích thước của đầu vào, theo nghĩa nào đó là hữu ích. Nhưng trong thế giới thực, đầu vào có cấu trúc nhiều hơn so với chỉ độ dài của chúng. Nếu đây là một thuật toán sắp xếp, tôi có thể ăn trong chuỗi "abcdef"
, "fedcba"
hoặc "dbafce"
. Tất cả chúng đều có chiều dài 6, nhưng một trong số chúng đã được sắp xếp, một cái bị đảo ngược và cái cuối cùng chỉ là một mớ bòng bong ngẫu nhiên. Một số thuật toán sắp xếp (như Timsort) hoạt động tốt hơn nếu đầu vào đã được sắp xếp. Nhưng làm thế nào để kết hợp tính không đồng nhất này vào một mô hình toán học?
Cách tiếp cận điển hình là chỉ đơn giản giả định đầu vào đến từ một số phân phối xác suất ngẫu nhiên. Sau đó, bạn tính trung bình độ phức tạp của thuật toán trên tất cả các đầu vào có độ dài n
. Điều này cung cấp cho bạn một mô hình phức tạp trường hợp trung bình của thuật toán. Từ đây, bạn có thể sử dụng các ký hiệu Big-O / / as như bình thường để mô tả hành vi trường hợp trung bình.
Nhưng nếu bạn lo ngại về các cuộc tấn công từ chối dịch vụ, thì bạn có thể phải bi quan hơn. Trong trường hợp này, sẽ an toàn hơn khi giả định rằng các đầu vào duy nhất là những đầu vào gây ra nhiều đau buồn nhất cho thuật toán của bạn. Điều này cung cấp cho bạn một mô hình phức tạp trong trường hợp xấu nhất của thuật toán. Sau đó, bạn có thể nói về Big-O / / Ω vv của mô hình trường hợp xấu nhất .
Tương tự, bạn cũng có thể tập trung sự quan tâm của mình vào các yếu tố đầu vào mà thuật toán của bạn gặp ít rắc rối nhất để đến với mô hình trường hợp tốt nhất , sau đó xem Big-O / /, v.v.