Làm thế nào để tìm độ phức tạp thời gian của một thuật toán


889

Câu hỏi

Làm thế nào để tìm độ phức tạp thời gian của một thuật toán?

Tôi đã làm gì trước khi đăng câu hỏi lên SO?

Tôi đã trải qua điều này , điều này và nhiều liên kết khác

Nhưng không có nơi nào tôi có thể tìm thấy một lời giải thích rõ ràng và thẳng về cách tính độ phức tạp thời gian.

Tôi biết gì?

Nói cho một mã đơn giản như mã dưới đây:

char h = 'y'; // This will be executed 1 time
int abc = 0; // This will be executed 1 time

Nói cho một vòng lặp như vòng dưới đây:

for (int i = 0; i < N; i++) {        
    Console.Write('Hello World !');
}

int i = 0; Điều này sẽ được thực hiện chỉ một lần . Thời gian thực sự được tính i=0và không khai báo.

tôi <N; Điều này sẽ được thực hiện N + 1 lần

i ++; Điều này sẽ được thực hiện N lần

Vì vậy, số lượng các hoạt động được yêu cầu bởi vòng lặp này là

{1+ (N + 1) + N} = 2N + 2

Lưu ý: Điều này vẫn có thể sai, vì tôi không tự tin về sự hiểu biết của mình về việc tính toán độ phức tạp thời gian

Những gì tôi muốn biết ?

Ok, vì vậy những tính toán cơ bản nhỏ này tôi nghĩ là tôi biết, nhưng trong hầu hết các trường hợp, tôi đã thấy sự phức tạp của thời gian là

O (N), O (n2), O (log n), O (n!) .... và nhiều thứ khác ,

Bất cứ ai có thể giúp tôi hiểu làm thế nào để tính toán độ phức tạp thời gian của một thuật toán? Tôi chắc chắn có rất nhiều người mới như tôi muốn biết điều này.


138
Phần thưởng cho những người quan tâm: The Big O Cheat Sheet bigochcoateet.com
msanford

4
Kiểm tra blog này: mohalerskymsorbit.blogspot.com . Nó bao gồm cả các thuật toán lặp đệ quy và (đặc biệt).
Mohamed Ennahdi El Idrissi

1
tại sao Console.Write ('Xin chào thế giới!'); không phải là một hướng dẫn máy?
Chetan


1
@Chetan Nếu bạn muốn nói rằng bạn nên cân nhắc Console.Writekhi tính toán độ phức tạp, điều đó đúng, nhưng cũng không liên quan trong trường hợp này, vì điều đó chỉ thay đổi một yếu tố không đổi, mà big-O bỏ qua (xem câu trả lời), vì vậy kết quả cuối cùng vẫn là độ phức tạp của O (N).
Bernhard Barker

Câu trả lời:


394

Làm thế nào để tìm độ phức tạp thời gian của một thuật toán

Bạn thêm bao nhiêu lệnh máy sẽ thực hiện như một hàm có kích thước của đầu vào, sau đó đơn giản hóa biểu thức thành số lớn nhất (khi N rất lớn) và có thể bao gồm bất kỳ hệ số hằng đơn giản hóa nào.

Ví dụ: hãy xem cách chúng tôi đơn giản hóa các 2N + 2hướng dẫn máy để mô tả điều này như chỉ O(N).

Tại sao chúng ta loại bỏ hai 2s?

Chúng tôi quan tâm đến hiệu suất của thuật toán khi N trở nên lớn.

Xét hai thuật ngữ 2N và 2.

Ảnh hưởng tương đối của hai thuật ngữ này khi N trở nên lớn là gì? Giả sử N là một triệu.

Sau đó, nhiệm kỳ đầu tiên là 2 triệu và nhiệm kỳ thứ hai chỉ là 2.

Vì lý do này, chúng tôi bỏ tất cả trừ các điều khoản lớn nhất cho N lớn.

Vì vậy, bây giờ chúng tôi đã đi từ 2N + 2đến 2N.

Theo truyền thống, chúng tôi chỉ quan tâm đến hiệu suất lên đến các yếu tố không đổi .

Điều này có nghĩa là chúng tôi không thực sự quan tâm nếu có sự khác biệt nhiều về hiệu suất khi N lớn. Đơn vị của 2N không được xác định rõ ở vị trí đầu tiên. Vì vậy, chúng ta có thể nhân hoặc chia cho một yếu tố không đổi để có được biểu thức đơn giản nhất.

Vì vậy, 2Ntrở thành chỉ N.


53
hey cảm ơn vì đã cho tôi biết "tại sao O (2N + 2) đến O (N)" được giải thích rất hay, nhưng đây chỉ là một phần của câu hỏi lớn hơn, tôi muốn ai đó chỉ ra một số liên kết đến một tài nguyên ẩn hoặc trong nói chung tôi muốn biết làm thế nào để bạn kết thúc với sự phức tạp về thời gian như O (N), O (n2), O (log n), O (n!), v.v. Tôi biết tôi có thể hỏi nhiều, nhưng tôi vẫn có thể thử: {)
Yasser Shaikh

3
Vâng, sự phức tạp trong ngoặc chỉ là thời gian thuật toán mất bao lâu, được đơn giản hóa bằng phương pháp tôi đã giải thích. Chúng tôi tìm ra thuật toán mất bao lâu bằng cách chỉ cần thêm số lượng lệnh máy sẽ thực hiện. Chúng ta có thể đơn giản hóa bằng cách chỉ nhìn vào các vòng lặp bận rộn nhất và chia cho các yếu tố không đổi như tôi đã giải thích.
Andrew Tomazos

4
Đưa ra một ví dụ trong câu trả lời sẽ giúp ích rất nhiều cho những độc giả tương lai. Chỉ cần đưa ra một liên kết mà tôi phải đăng ký, thực sự không giúp tôi khi tôi chỉ muốn xem qua một số văn bản được giải thích độc đáo.
bad_keypoint

2
Tôi sẽ đề nghị xem video của Tiến sĩ Naveen Garg (Giáo sư IIT Delhi) nếu bạn muốn có kiến ​​thức tốt về độ phức tạp DS và Thời gian. Hãy kiểm tra liên kết. nptel.ac.in/cifts/106102064
Rohit Bandil

2
(tt số mũ. Có đúng 10! giây trong sáu tuần nhưng vũ trụ chưa đến 20! giây cũ.
John P

389

Đây là một bài viết xuất sắc: http://www.daniweb.com/software-development/computer-science/threads/13488/time-complexity-of-alerskym

Câu trả lời dưới đây được sao chép từ phía trên (trong trường hợp liên kết tuyệt vời bị phá vỡ)

Số liệu phổ biến nhất để tính độ phức tạp thời gian là ký hiệu Big O. Điều này loại bỏ tất cả các yếu tố không đổi để thời gian chạy có thể được ước tính liên quan đến N khi N tiến đến vô cùng. Nói chung bạn có thể nghĩ về nó như thế này:

statement;

Là hằng số. Thời gian chạy của câu lệnh sẽ không thay đổi liên quan đến N.

for ( i = 0; i < N; i++ )
     statement;

Là tuyến tính. Thời gian chạy của vòng lặp tỷ lệ thuận với N. Khi N nhân đôi, thời gian chạy cũng vậy.

for ( i = 0; i < N; i++ ) {
  for ( j = 0; j < N; j++ )
    statement;
}

Là bậc hai. Thời gian chạy của hai vòng tỷ lệ với bình phương của N. Khi N nhân đôi, thời gian chạy tăng thêm N * N.

while ( low <= high ) {
  mid = ( low + high ) / 2;
  if ( target < list[mid] )
    high = mid - 1;
  else if ( target > list[mid] )
    low = mid + 1;
  else break;
}

Là logarit. Thời gian chạy của thuật toán tỷ lệ thuận với số lần N có thể chia cho 2. Điều này là do thuật toán chia vùng làm việc thành một nửa với mỗi lần lặp.

void quicksort ( int list[], int left, int right )
{
  int pivot = partition ( list, left, right );
  quicksort ( list, left, pivot - 1 );
  quicksort ( list, pivot + 1, right );
}

Là N * log (N). Thời gian chạy bao gồm N vòng lặp (lặp hoặc đệ quy) là logarit, do đó thuật toán là sự kết hợp giữa tuyến tính và logarit.

Nói chung, làm một cái gì đó với mọi mục trong một chiều là tuyến tính, làm một cái gì đó với mọi mục trong hai chiều là bậc hai, và chia khu vực làm việc thành một nửa là logarit. Có các biện pháp Big O khác như khối vuông, hàm mũ và căn bậc hai, nhưng chúng gần như không phổ biến. Ký hiệu Big O được mô tả là O ( <type> )nơi <type>đo. Thuật toán quicksort sẽ được mô tả là O ( N * log ( N ) ).

Lưu ý rằng không ai trong số này đã tính đến các biện pháp tốt nhất, trung bình và tồi tệ nhất. Mỗi người sẽ có ký hiệu Big O riêng. Cũng lưu ý rằng đây là một lời giải thích đơn giản RẤT. Big O là phổ biến nhất, nhưng nó cũng phức tạp hơn mà tôi đã thể hiện. Ngoài ra còn có các ký hiệu khác như omega lớn, ít o và theta lớn. Bạn có thể sẽ không gặp phải chúng bên ngoài một khóa học phân tích thuật toán. ;)


10
Các quicksort thuật toán trong trường hợp xấu nhất có một thời gian chạy của N ^ 2, mặc dù hành vi này là rất hiếm.
nbro 4/03/2015

2
IIRC, ít o và omega lớn được sử dụng cho độ phức tạp trường hợp tốt nhất và trung bình (với O lớn là trường hợp xấu nhất), do đó, "các biện pháp tốt nhất, trung bình và tồi tệ nhất. Mỗi trường hợp sẽ có ký hiệu Big O riêng." sẽ không chính xác. Thậm chí còn có nhiều biểu tượng với ý nghĩa cụ thể hơn và CS không phải lúc nào cũng sử dụng biểu tượng phù hợp nhất. Tôi đến để tìm hiểu tất cả những cái này với tên Landau btw. Dù sao +1 b / c câu trả lời tốt nhất.
hiergiltdiestfu

@hiergiltdiestfu Big-O, Big-Omega, v.v. có thể được áp dụng cho bất kỳ thời gian chạy tốt nhất, trung bình hoặc tồi tệ nhất của thuật toán. Làm thế nào để O và Ω liên quan đến trường hợp xấu nhất và tốt nhất?
Bernhard Barker

Ngoài ra, nếu bất cứ ai đang tìm cách tính O lớn cho bất kỳ phương thức nào: stackoverflow.com/a/60354355/4260691
OhadM

Một trong những lời giải thích tốt nhất.
Shivaraj Patil

172

Lấy từ đây - Giới thiệu về độ phức tạp thời gian của thuật toán

1. Giới thiệu

Trong khoa học máy tính, độ phức tạp thời gian của thuật toán sẽ định lượng lượng thời gian mà thuật toán sử dụng để chạy như một hàm theo độ dài của chuỗi đại diện cho đầu vào.

2. Ký hiệu Big O

Độ phức tạp thời gian của một thuật toán thường được biểu thị bằng cách sử dụng ký hiệu O lớn, loại trừ các hệ số và các điều khoản bậc thấp hơn. Khi diễn đạt theo cách này, độ phức tạp thời gian được cho là được mô tả không có triệu chứng, nghĩa là, khi kích thước đầu vào chuyển sang vô cùng.

Ví dụ: nếu thời gian mà thuật toán yêu cầu trên tất cả các đầu vào có kích thước n nhiều nhất là 5n 3 + 3n, thì độ phức tạp thời gian tiệm cận là O (n 3 ). Thêm về điều đó sau.

Một vài ví dụ khác:

  • 1 = O (n)
  • n = O (n 2 )
  • log (n) = O (n)
  • 2 n + 1 = O (n)

3. O (1) Thời gian không đổi:

Một thuật toán được cho là chạy trong thời gian không đổi nếu nó yêu cầu cùng một lượng thời gian bất kể kích thước đầu vào.

Ví dụ:

  • mảng: truy cập bất kỳ phần tử nào
  • ngăn xếp kích thước cố định: phương pháp đẩy và pop
  • hàng đợi có kích thước cố định: phương pháp enqueue và dequeue

4. O (n) Thời gian tuyến tính

Một thuật toán được cho là chạy trong thời gian tuyến tính nếu thời gian thực hiện của nó tỷ lệ thuận với kích thước đầu vào, tức là thời gian tăng tuyến tính khi kích thước đầu vào tăng.

Hãy xem xét các ví dụ sau, bên dưới tôi đang tìm kiếm tuyến tính cho một phần tử, điều này có độ phức tạp thời gian là O (n).

int find = 66;
var numbers = new int[] { 33, 435, 36, 37, 43, 45, 66, 656, 2232 };
for (int i = 0; i < numbers.Length - 1; i++)
{
    if(find == numbers[i])
    {
        return;
    }
}

Thêm ví dụ:

  • Mảng: Tìm kiếm tuyến tính, Traversing, Tìm tối thiểu vv
  • ArrayList: chứa phương thức
  • Hàng đợi: chứa phương thức

5. O (log n) Thời gian logarit:

Một thuật toán được cho là chạy trong thời gian logarit nếu thời gian thực hiện của nó tỷ lệ thuận với logarit của kích thước đầu vào.

Ví dụ: Tìm kiếm nhị phân

Nhớ lại trò chơi "hai mươi câu hỏi" - nhiệm vụ là đoán giá trị của một số ẩn trong một khoảng. Mỗi lần bạn đoán, bạn sẽ được biết liệu dự đoán của bạn quá cao hay quá thấp. Hai mươi câu hỏi trò chơi ngụ ý một chiến lược sử dụng số đoán của bạn để giảm một nửa kích thước khoảng. Đây là một ví dụ về phương pháp giải quyết vấn đề chung được gọi là tìm kiếm nhị phân

6. O (n 2 ) Thời gian bậc hai

Một thuật toán được cho là chạy trong thời gian bậc hai nếu thời gian thực hiện của nó tỷ lệ với bình phương của kích thước đầu vào.

Ví dụ:

7. Một số liên kết hữu ích


17
Lưu ý: liên kết đầu tiên bị hỏng.
Ziezi

2
O (n2) nên được viết O (n ^ 2) để tránh nhầm lẫn.
Rizki Hadiaturrasyid

100

Mặc dù có một số câu trả lời tốt cho câu hỏi này. Tôi muốn đưa ra một câu trả lời khác ở đây với một số ví dụ về loop.

  • O (n) : Độ phức tạp thời gian của một vòng lặp được coi là O (n) nếu các biến vòng lặp được tăng / giảm theo một lượng không đổi. Ví dụ các hàm sau có độ phức tạp thời gian O (n) .

    // Here c is a positive integer constant   
    for (int i = 1; i <= n; i += c) {  
        // some O(1) expressions
    }
    
    for (int i = n; i > 0; i -= c) {
        // some O(1) expressions
    }
    
  • O (n ^ c) : Độ phức tạp thời gian của các vòng lặp lồng nhau bằng số lần câu lệnh trong cùng được thực thi. Ví dụ, các vòng lặp mẫu sau có độ phức tạp thời gian O (n ^ 2)

    for (int i = 1; i <=n; i += c) {
       for (int j = 1; j <=n; j += c) {
          // some O(1) expressions
       }
    }
    
    for (int i = n; i > 0; i += c) {
       for (int j = i+1; j <=n; j += c) {
          // some O(1) expressions
    }
    

    Ví dụ: Sắp xếp lựa chọn và Sắp xếp chèn có độ phức tạp thời gian O (n ^ 2) .

  • Độ phức tạp thời gian O (Logn) của vòng lặp được coi là O (Logn) nếu các biến vòng lặp được chia / nhân với một lượng không đổi.

    for (int i = 1; i <=n; i *= c) {
       // some O(1) expressions
    }
    for (int i = n; i > 0; i /= c) {
       // some O(1) expressions
    }
    

    Ví dụ: Tìm kiếm nhị phân có độ phức tạp thời gian O (Logn) .

  • Độ phức tạp thời gian O (LogLogn) của một vòng lặp được coi là O (LogLogn) nếu các biến vòng lặp được giảm / tăng theo cấp số nhân theo một lượng không đổi.

    // Here c is a constant greater than 1   
    for (int i = 2; i <=n; i = pow(i, c)) { 
       // some O(1) expressions
    }
    //Here fun is sqrt or cuberoot or any other constant root
    for (int i = n; i > 0; i = fun(i)) { 
       // some O(1) expressions
    }
    

Một ví dụ về phân tích độ phức tạp thời gian

int fun(int n)
{    
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j < n; j += i)
        {
            // Some O(1) task
        }
    }    
}

Phân tích :

For i = 1, the inner loop is executed n times. For i = 2, the inner loop is executed approximately n/2 times. For i = 3, the inner loop is executed approximately n/3 times. For i = 4, the inner loop is executed approximately n/4 times. ……………………………………………………. For i = n, the inner loop is executed approximately n/n times.

Vì vậy, tổng độ phức tạp thời gian của thuật toán trên là (n + n/2 + n/3 + … + n/n), trở thànhn * (1/1 + 1/2 + 1/3 + … + 1/n)

Điều quan trọng về chuỗi (1/1 + 1/2 + 1/3 + … + 1/n)là bằng O (Logn) . Vì vậy, độ phức tạp thời gian của mã trên là O (nLogn) .


Tham chiếu: 1 2 3


1
@Simon, bạn có thể vui lòng tìm ra phần nào không đúng?
zangw

Cam ơn vi đa hỏi. Tôi đọc sai mã. Tôi đã xóa bình luận của tôi. Lấy làm tiếc!
Simon

74

Thời gian phức tạp với các ví dụ

1 - Các thao tác cơ bản (số học, so sánh, truy cập các phần tử của mảng, phép gán): Thời gian chạy luôn không đổi O (1)

Thí dụ :

read(x)                               // O(1)
a = 10;                               // O(1)
a = 1.000.000.000.000.000.000         // O(1)

2 - Nếu sau đó câu lệnh khác: Chỉ lấy thời gian chạy tối đa từ hai hoặc nhiều câu lệnh có thể.

Thí dụ:

age = read(x)                               // (1+1) = 2
if age < 17 then begin                      // 1
      status = "Not allowed!";              // 1
end else begin
      status = "Welcome! Please come in";   // 1
      visitors = visitors + 1;              // 1+1 = 2
end;

Vì vậy, độ phức tạp của mã giả ở trên là T (n) = 2 + 1 + max (1, 1 + 2) = 6. Do đó, oh lớn của nó vẫn là hằng số T (n) = O (1).

3 - Vòng lặp (for, while, repeat): Thời gian chạy cho câu lệnh này là số vòng lặp nhân với số lượng hoạt động bên trong vòng lặp đó.

Thí dụ:

total = 0;                                  // 1
for i = 1 to n do begin                     // (1+1)*n = 2n
      total = total + i;                    // (1+1)*n = 2n
end;
writeln(total);                             // 1

Vậy, độ phức tạp của nó là T (n) = 1 + 4n + 1 = 4n + 2. Do đó, T (n) = O (n).

4 - Vòng lặp lồng nhau (vòng lặp bên trong vòng lặp): Vì có ít nhất một vòng lặp bên trong vòng lặp chính, thời gian chạy của câu lệnh này đã sử dụng O (n ^ 2) hoặc O (n ^ 3).

Thí dụ:

for i = 1 to n do begin                     // (1+1)*n  = 2n
   for j = 1 to n do begin                  // (1+1)n*n = 2n^2
       x = x + 1;                           // (1+1)n*n = 2n^2
       print(x);                            // (n*n)    = n^2
   end;
end;

Thời gian chạy chung

Có một số thời gian chạy phổ biến khi phân tích một thuật toán:

  1. O (1) - Thời gian không đổi Thời gian không đổi có nghĩa là thời gian chạy không đổi, nó không bị ảnh hưởng bởi kích thước đầu vào .

  2. O (n) - Thời gian tuyến tính Khi thuật toán chấp nhận n kích thước đầu vào, nó cũng sẽ thực hiện n hoạt động.

  3. O (log n) - Thuật toán thời gian logarit có thời gian chạy O (log n) nhanh hơn một chút so với O (n). Thông thường, thuật toán phân chia vấn đề thành các vấn đề phụ có cùng kích thước. Ví dụ: thuật toán tìm kiếm nhị phân, thuật toán chuyển đổi nhị phân.

  4. O (n log n) - Thời gian tuyến tính Thời gian chạy này thường được tìm thấy trong "thuật toán chia & chinh phục", phân chia vấn đề thành các vấn đề phụ theo cách đệ quy và sau đó hợp nhất chúng trong thời gian n. Ví dụ: Hợp nhất Sắp xếp thuật toán.

  5. O (n 2 ) - Thuật toán sắp xếp bong bóng theo thời gian bậc hai!

  6. O (n 3 ) - Thời gian khối Nó có cùng nguyên tắc với O (n 2 ).

  7. O (2 n ) - Thời gian theo cấp số mũ Rất chậm khi đầu vào trở nên lớn hơn, nếu n = 1000.000, T (n) sẽ là 21000.000. Thuật toán Brute Force có thời gian chạy này.

  8. O (n!) - Thời gian yếu tố TUYỆT VỜI !!! Ví dụ: Vấn đề nhân viên bán hàng du lịch (TSP)

Lấy từ bài viết này . Giải thích rất tốt nên cho đọc.


Trong ví dụ thứ 2 của bạn, bạn đã viết visitors = visitors + 11 + 1 = 2. Bạn có thể vui lòng giải thích cho tôi tại sao bạn làm điều đó?
Sajib Acharya

3
@Sajib Acharya Nhìn nó từ phải sang trái. Bước đầu tiên: tính visitors + 1 Bước thứ hai: gán giá trị từ bước đầu tiên cho visitors So, biểu thức trên được tạo thành từ hai câu lệnh; Bước đầu tiên + bước thứ hai => 1 + 1 = 2
Bozidar Sikanjic

@nbro Tại sao lại là 1 + 1 inage = read(x) // (1+1) = 2
Humty

@BozidarSikanjic Tại sao lại là 1 + 1age = read(x) // (1+1) = 2
Humty

1
@Humty Kiểm tra phần đầu của câu trả lời này: read(x) // O(1) a = 10; // O(1)Đầu tiên là hàm gọi => O (1) ///// Thứ hai là gán, như nbro đã nói, nhưng 10 là hằng số, vì vậy thứ hai là => O (1) ...
Bozidar Sikanjic

41

Khi bạn đang phân tích mã, bạn phải phân tích từng dòng một, đếm mọi thao tác / nhận biết độ phức tạp của thời gian, cuối cùng, bạn phải tổng hợp nó để có được toàn bộ hình ảnh.

Ví dụ, bạn có thể có một vòng lặp đơn giản với độ phức tạp tuyến tính , nhưng sau đó trong cùng một chương trình, bạn có thể có một vòng lặp ba có độ phức tạp khối , vì vậy chương trình của bạn sẽ có độ phức tạp khối . Thứ tự chức năng tăng trưởng đi vào chơi ngay tại đây.

Chúng ta hãy xem các khả năng về độ phức tạp thời gian của thuật toán là gì, bạn có thể thấy thứ tự tăng trưởng mà tôi đã đề cập ở trên:

  • Thời gian không đổi có thứ tự tăng trưởng1, ví dụ :a = b + c.

  • Thời gian logarit có thứ tự tăng trưởngLogN, nó thường xảy ra khi bạn chia một nửa thứ gì đó (tìm kiếm nhị phân, cây, thậm chí các vòng lặp) hoặc nhân một thứ gì đó theo cùng một cách.

  • Tuyến tính , thứ tự tăng trưởng làN, ví dụ

    int p = 0;
    for (int i = 1; i < N; i++)
      p = p + 2;
    
  • Tuyến tính , thứ tự tăng trưởngn*logN, thường xảy ra trong các thuật toán phân chia và chinh phục.

  • Hình khối , thứ tự tăng trưởngN^3, ví dụ cổ điển là một vòng lặp ba trong đó bạn kiểm tra tất cả các bộ ba:

    int x = 0;
    for (int i = 0; i < N; i++)
       for (int j = 0; j < N; j++)
          for (int k = 0; k < N; k++)
              x = x + 2
    
  • Hàm mũ , thứ tự tăng trưởng2^N, thường xảy ra khi bạn thực hiện tìm kiếm toàn diện, ví dụ kiểm tra các tập hợp con của một số bộ.


Nếu đây là trường hợp, sự phức tạp sẽ là gì? for (int i = 0; i <N; i ++) for (int j = i + 1; j <N; j ++) for (int k = j + 1; k <N; k ++) x = x + 2
user3156040

35

Nói một cách lỏng lẻo, độ phức tạp thời gian là cách tóm tắt số lượng hoạt động hoặc thời gian chạy của thuật toán tăng lên khi kích thước đầu vào tăng.

Giống như hầu hết mọi thứ trong cuộc sống, một bữa tiệc cocktail có thể giúp chúng ta hiểu.

TRÊN)

Khi bạn đến bữa tiệc, bạn phải bắt tay mọi người (thực hiện thao tác trên mọi vật phẩm). Khi số lượng người tham dự Ntăng lên, thời gian / công việc sẽ khiến bạn bắt tay mọi người tăng lên O(N).

Tại sao O(N)và không cN?

Có sự khác biệt về thời gian cần thiết để bắt tay với mọi người. Bạn có thể lấy trung bình này ra và chụp nó trong một hằng số c. Nhưng hoạt động cơ bản ở đây --- bắt tay với mọi người --- sẽ luôn tỷ lệ thuận O(N), bất kể clà gì . Khi tranh luận về việc chúng ta có nên đi dự tiệc cocktail hay không, chúng ta thường quan tâm nhiều hơn đến việc chúng ta sẽ phải gặp mọi người hơn là chi tiết trong vài phút về những cuộc họp đó.

Ô (N ^ 2)

Chủ nhà của bữa tiệc cocktail muốn bạn chơi một trò chơi ngớ ngẩn, nơi mọi người gặp gỡ mọi người khác. Do đó, bạn phải gặp N-1người khác và vì người tiếp theo đã gặp bạn, họ phải gặp N-2người khác, v.v. Tổng của loạt bài này là x^2/2+x/2. Khi số lượng người tham dự tăng lên, x^2thuật ngữ này trở nên nhanh chóng , vì vậy chúng tôi chỉ cần bỏ mọi thứ khác.

Ô (3 ^)

Bạn phải gặp gỡ mọi người khác và trong mỗi cuộc họp, bạn phải nói về những người khác trong phòng.

Ô (1)

Chủ nhà muốn thông báo điều gì đó. Họ ding một ly rượu và nói to. Mọi người nghe họ. Hóa ra không quan trọng là có bao nhiêu người tham dự, hoạt động này luôn mất cùng một lượng thời gian.

O (log N)

Chủ nhà đã đặt tất cả mọi người ra bàn theo thứ tự bảng chữ cái. Đàn đâu rồi? Bạn lý do rằng anh ta phải ở đâu đó giữa Adam và Mandy (chắc chắn không phải giữa Mandy và Zach!). Cho rằng, anh ta là giữa George và Mandy? Không. Anh ta phải ở giữa Adam và Fred, và giữa Cindy và Fred. Và cứ thế ... chúng ta có thể định vị Dan một cách hiệu quả bằng cách nhìn vào một nửa tập hợp và một nửa tập hợp đó. Cuối cùng, chúng tôi xem xét các cá nhân O (log_2 N) .

O (N log N)

Bạn có thể tìm nơi ngồi xuống bàn bằng thuật toán trên. Nếu một số lượng lớn người đến bàn, từng người một và tất cả đã làm điều này, điều đó sẽ mất thời gian O (N log N) . Điều này hóa ra là mất bao lâu để sắp xếp bất kỳ bộ sưu tập các mặt hàng khi chúng phải được so sánh.

Trường hợp tốt nhất / tồi tệ nhất

Bạn đến bữa tiệc và cần tìm Inigo - sẽ mất bao lâu? Nó phụ thuộc vào khi bạn đến. Nếu mọi người xung quanh bạn gặp phải trường hợp xấu nhất: sẽ mất O(N)thời gian. Tuy nhiên, nếu tất cả mọi người đang ngồi xuống bàn, sẽ chỉ mất O(log N)thời gian. Hoặc có lẽ bạn có thể tận dụng sức mạnh la hét của chủ nhà và sẽ chỉ mất O(1)thời gian.

Giả sử máy chủ không khả dụng, chúng tôi có thể nói rằng thuật toán tìm kiếm Inigo có giới hạn dưới O(log N)và giới hạn trên O(N), tùy thuộc vào trạng thái của bữa tiệc khi bạn đến.

Không gian & Truyền thông

Những ý tưởng tương tự có thể được áp dụng để hiểu cách các thuật toán sử dụng không gian hoặc giao tiếp.

Knuth đã viết một bài báo hay về bài trước có tựa đề "Sự phức tạp của các bài hát" .

Định lý 2: Tồn tại các bài hát dài tùy ý có độ phức tạp O (1).

BÀI VIẾT: (do Casey và ban nhạc Ánh Dương). Hãy xem xét các bài hát Sk được xác định bởi (15), nhưng với

V_k = 'That's the way,' U 'I like it, ' U
U   = 'uh huh,' 'uh huh'

cho tất cả k.


Bạn đóng đinh nó, Bây giờ bất cứ khi nào tôi đến một bữa tiệc cocktail, tôi sẽ vô thức thử tìm Thời gian phức tạp của bất kỳ sự kiện vui vẻ nào. Cảm ơn một ví dụ hài hước như vậy.
Sabunkar Tejas Sahailesh

5

Tôi biết câu hỏi này đi ngược lại và có một số câu trả lời xuất sắc ở đây, dù sao tôi cũng muốn chia sẻ một chút cho những người có đầu óc toán học sẽ vấp ngã trong bài đăng này. Các định lý tổng thể là một điều hữu ích để biết khi nghiên cứu phức tạp. Tôi không thấy nó được đề cập trong các câu trả lời khác.


2

O (n) là ký hiệu O lớn được sử dụng để viết độ phức tạp thời gian của thuật toán. Khi bạn cộng số lượng thực thi trong algoritm, bạn sẽ nhận được một biểu thức có kết quả như 2N + 2, trong biểu thức này N là thuật ngữ thống trị (thuật ngữ có ảnh hưởng lớn nhất đến biểu thức nếu giá trị của nó tăng hoặc giảm). Bây giờ O (N) là độ nhạy thời gian trong khi N đang thống trị thuật ngữ. Thí dụ

For i= 1 to n;
  j= 0;
while(j<=n);
  j=j+1;

ở đây tổng số lần thực hiện cho vòng lặp bên trong là n + 1 và tổng số lần thực hiện cho vòng lặp bên ngoài là n (n + 1) / 2, vì vậy tổng số lần thực hiện cho toàn bộ thuật toán là n + 1 + n (n + 1/2 ) = (n ^ 2 + 3n) / 2. ở đây n ^ 2 là thuật ngữ thống trị nên độ phức tạp thời gian của thuật toán này là O (n ^ 2)

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.