O (Lát) là gì và tôi tính toán như thế nào?


31

Cứu giúp! Tôi có một câu hỏi mà tôi cần phân tích Big-O của một thuật toán hoặc một số mã.

  • Tôi không chắc chính xác Big-O là gì hoặc nó liên quan đến Big-Theta hay các phương tiện khác để phân tích độ phức tạp của thuật toán.

  • Tôi không chắc liệu Big-O đề cập đến thời gian để chạy mã hay dung lượng bộ nhớ cần có (sự đánh đổi không gian / thời gian).

  • Tôi có bài tập về Khoa học Máy tính nơi tôi cần thực hiện một số vòng lặp, có lẽ là thuật toán đệ quy và đưa ra Big-O cho nó.

  • Tôi đang làm việc trên một chương trình mà tôi có lựa chọn giữa hai cấu trúc dữ liệu hoặc thuật toán với Big-O đã biết và không chắc chắn nên chọn cái nào.

Làm cách nào để hiểu cách tính toán và áp dụng Big-O vào chương trình, bài tập về nhà hoặc kiến ​​thức chung về Khoa học Máy tính?

Lưu ý: câu hỏi này là mục tiêu dupe chính tắc cho các câu hỏi Big-O khác được xác định bởi cộng đồng . Nó có chủ ý rộng để có thể chứa một lượng lớn thông tin hữu ích cho nhiều câu hỏi Big-O. Xin vui lòng không sử dụng thực tế rằng nó rộng như một dấu hiệu cho thấy các câu hỏi tương tự có thể được chấp nhận.


1
Chỉ cần một lưu ý, câu hỏi này đang được thảo luận về meta ở đây .
enderland

2
Một nguồn tài nguyên tuyệt vời để bắt đầu sẽ là khóa học của Khan (Thomas Cormen của CLRS là một trong những nhà văn). Đây cũng là một nguồn tài nguyên tuyệt vời đối với tôi khi tốt nghiệp CS. khanacademy.org/computing/computer-science/alacticms
Sudip Bhandari

3
Đối với những người gắn cờ câu hỏi này: vui lòng đọc phần phụ ở cuối câu hỏi và theo liên kết đó trước khi gắn cờ hoặc bỏ phiếu để đóng.

Câu trả lời:


22

Chữ O (...) dùng để chỉ ký hiệu Big-O, đây là một cách đơn giản để mô tả có bao nhiêu thao tác mà thuật toán thực hiện để làm một việc gì đó. Điều này được gọi là phức tạp thời gian .

Trong ký hiệu Big-O, chi phí của một thuật toán được thể hiện bằng hoạt động tốn kém nhất của nó với số lượng lớn. Nếu một thuật toán thực hiện các bước, nó sẽ được biểu diễn O (n 3 ). Một thuật toán đếm từng mục trong danh sách sẽ hoạt động theo thời gian O (n), được gọi là thời gian tuyến tính.n3 + n2 + n

Để biết danh sách các tên và ví dụ kinh điển trên Wikipedia: Đơn hàng các chức năng phổ biến

Tài liệu liên quan:


6
Lưu ý: big O vốn không đo thời gian hoặc không gian hoặc bất kỳ điều cụ thể nào. Nó chỉ đơn giản là giới hạn trên sự tăng trưởng tiệm cận của một chức năng (lên đến một hằng số). Hàm đó có thể là thời gian, không gian, v.v. của thuật toán là một hàm có độ dài đầu vào của nó và phổ biến nhất trong bối cảnh CS là thời gian, nhưng không nhất thiết phải như vậy.
Phục hồi Monica

23

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)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.

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.


Đây là một câu trả lời tốt, nhưng Big-O và Big-ít liên quan đến các trường hợp xấu nhất và tốt nhất. Chúng là giới hạn trên và dưới, nhưng cả hai đều có thể áp dụng cho cả hai trường hợp, ví dụ, loại chèn có, trong trường hợp tốt nhất, độ phức tạp về thời gian ϴ(n)(cả Big-O và Big-) trong trường hợp xấu nhất, trong trường hợp xấu nhất, độ phức tạp thời gian của ϴ(n²)(cả Big-O và Big-).
Paul

Ngoài ra, bất kỳ thuật toán nào, ngoại trừ thuật toán trống, đều Ω(1)thuộc trường hợp tốt nhất, xấu nhất và trung bình, nhưng đó là vì đó là giới hạn thấp hơn và không liên quan gì đến trường hợp tốt nhất thực tế của thuật toán.
Paul

Lớn - bất cứ điều gì không liên quan đến trường hợp tốt nhất hoặc tồi tệ nhất - đây là một quan niệm sai lầm phổ biến. Chúng chỉ đơn thuần là cách để xác định xem một hàm xác định tăng nhanh hơn, chậm hơn hay với cùng tốc độ so với hàm khác. Khái niệm trường hợp tốt nhất / tồi tệ nhất chỉ tồn tại khi bạn bắt đầu nói về độ phức tạp thời gian / không gian của các thuật toán thực, trong trường hợp đó các yếu tố không xác định phát sinh và bạn cần xem xét phân phối xác suất của đầu vào. Ngay cả khi đó, trường hợp tốt nhất / tồi tệ nhất nằm trên một trục trực giao với big-O / Omega.
Rufflewind

@Rufflewind nếu bạn nghĩ rằng bạn có thể mô tả nó tốt hơn, thì hãy tiếp tục. Bạn có nhận ra đây là một câu trả lời wiki cộng đồng, phải không?

"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 thường được coi là từ đồng nghĩa với Big-" -> Không chỉ trong văn học không chính thức. Học kỳ đầu tiên của tôi về Sự phức tạp, trở lại trường đại học, đã dạy chúng tôi định nghĩa về Big-O như thể nó là Big-. (Sách giáo khoa của chúng tôi đã sử dụng định nghĩa đó!) Nó làm tôi bối rối khi tôi đến Khu phức hợp II và tôi phát hiện ra hai người không giống nhau.
T. Sar - Tái lập Monica
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.