O (log n) chính xác có nghĩa là gì?


2139

Tôi đang tìm hiểu về thời gian chạy Big O Notation và thời gian khấu hao. Tôi hiểu khái niệm về thời gian tuyến tính O (n) , có nghĩa là kích thước của đầu vào ảnh hưởng đến sự tăng trưởng của thuật toán theo tỷ lệ ... và ví dụ tương tự, ví dụ, thời gian bậc hai O (n 2 ), v.v. , chẳng hạn như bộ tạo hoán vị, với số lần O (n!) , phát triển theo giai thừa.

Ví dụ, hàm sau là O (n) vì thuật toán tăng tỷ lệ với n đầu vào của nó :

f(int n) {
  int i;
  for (i = 0; i < n; ++i)
    printf("%d", i);
}

Tương tự, nếu có một vòng lặp lồng nhau, thời gian sẽ là O (n 2 ).

Nhưng chính xác thì O (log n) là gì? Ví dụ, có nghĩa gì khi nói rằng chiều cao của cây nhị phân hoàn chỉnh là O (log n) ?

Tôi biết (có thể không chi tiết lắm) Logarit là gì, theo nghĩa là: log 10 100 = 2, nhưng tôi không thể hiểu cách xác định hàm với thời gian logarit.


60
Cây nhị phân 1 nút có chiều cao log2 (1) +1 = 1, cây 2 nút có chiều cao log2 (2) +1 = 2, cây 4 nút có chiều cao log2 (4) +1 = 3 và Sớm. Cây nút n có chiều cao log2 (n) +1, do đó, việc thêm nút vào cây sẽ khiến chiều cao trung bình của nó tăng theo logarit.
David R Tribble

36
Một điều tôi thấy trong hầu hết các câu trả lời là về cơ bản chúng mô tả "O (một cái gì đó)" có nghĩa là thời gian chạy của thuật toán tăng tỷ lệ thuận với "một cái gì đó". Cho rằng bạn đã hỏi "ý nghĩa chính xác" của "O (log n)", điều đó không đúng. Đó là mô tả trực quan của ký hiệu Big-Theta, không phải Big-O. O (log n) theo trực giác có nghĩa là thời gian chạy tăng theo tỷ lệ thuận với "log n": stackoverflow.com/questions/471199/
Kẻ

31
Tôi luôn nhớ phân chia và chinh phục làm ví dụ cho O (log n)
RichardOD

14
Điều quan trọng là phải nhận ra rằng cơ sở nhật ký 2 của nó (không phải cơ sở 10). Điều này là do tại mỗi bước trong một thuật toán, bạn loại bỏ một nửa các lựa chọn còn lại của mình. Trong khoa học máy tính, chúng ta hầu như luôn luôn xử lý log cơ sở 2 vì chúng ta có thể bỏ qua các hằng số. Tuy nhiên, có một số trường hợp ngoại lệ (ví dụ: thời gian chạy Quad Tree là log cơ sở 4)
Ethan

13
@Ethan: Không quan trọng bạn đang ở cơ sở nào, vì chuyển đổi cơ sở chỉ là một phép nhân không đổi, Công thức là log_b (x) = log_d (x) / log_d (b). Log_d (b) sẽ chỉ là một hằng số.
mindvirus

Câu trả lời:


2711

Tôi không thể hiểu làm thế nào để xác định một chức năng với thời gian đăng nhập.

Các thuộc tính phổ biến nhất của hàm thời gian chạy logarit là:

  • sự lựa chọn của yếu tố tiếp theo để thực hiện một số hành động là một trong nhiều khả năng và
  • chỉ một người sẽ cần được chọn.

hoặc là

  • các yếu tố mà hành động được thực hiện là chữ số của n

Đây là lý do tại sao, ví dụ, tìm kiếm người trong danh bạ điện thoại là O (log n). Bạn không cần phải kiểm tra từng người trong danh bạ để tìm đúng người; thay vào đó, bạn có thể chỉ cần phân chia và chinh phục bằng cách tìm kiếm dựa trên tên của chúng theo thứ tự bảng chữ cái và trong mỗi phần bạn chỉ cần khám phá một tập hợp con của mỗi phần trước khi cuối cùng bạn tìm thấy số điện thoại của ai đó.

Tất nhiên, một danh bạ điện thoại lớn hơn sẽ vẫn khiến bạn mất nhiều thời gian hơn, nhưng nó sẽ không phát triển nhanh như sự gia tăng tỷ lệ trong kích thước bổ sung.


Chúng ta có thể mở rộng ví dụ về danh bạ điện thoại để so sánh các loại hoạt động khác và thời gian chạy của chúng . Chúng tôi sẽ cho rằng danh bạ điện thoại của chúng tôi có các doanh nghiệp ("Trang vàng") có tên và người duy nhất ("Trang trắng") có thể không có tên duy nhất. Một số điện thoại được gán cho tối đa một người hoặc doanh nghiệp. Chúng tôi cũng sẽ cho rằng phải mất thời gian liên tục để lật sang một trang cụ thể.

Dưới đây là thời gian chạy của một số thao tác chúng tôi có thể thực hiện trên danh bạ điện thoại, từ nhanh nhất đến chậm nhất:

  • O (1) (trong trường hợp xấu nhất): Cho trang có tên doanh nghiệp và tên doanh nghiệp, hãy tìm số điện thoại.

  • O (1) (trong trường hợp trung bình): Cho trang có tên của một người và tên của họ, hãy tìm số điện thoại.

  • O (log n): Đặt tên của một người, tìm số điện thoại bằng cách chọn một điểm ngẫu nhiên khoảng nửa phần của cuốn sách bạn chưa tìm kiếm, sau đó kiểm tra xem tên của người đó có ở điểm đó không. Sau đó lặp lại quá trình khoảng nửa phần của cuốn sách nơi tên của người đó nằm. (Đây là tìm kiếm nhị phân cho tên của một người.)

  • O (n): Tìm tất cả những người có số điện thoại chứa chữ số "5".

  • O (n): Cho một số điện thoại, tìm người hoặc doanh nghiệp có số đó.

  • O (n log n): Có một sự pha trộn tại văn phòng của máy in và danh bạ điện thoại của chúng tôi có tất cả các trang được chèn theo thứ tự ngẫu nhiên. Khắc phục thứ tự sao cho chính xác bằng cách nhìn vào tên đầu tiên trên mỗi trang và sau đó đặt trang đó vào vị trí thích hợp trong danh bạ điện thoại mới, trống.

Đối với các ví dụ dưới đây, chúng tôi hiện đang ở văn phòng của máy in. Danh bạ điện thoại đang chờ để được gửi đến từng cư dân hoặc doanh nghiệp và có một nhãn dán trên mỗi danh bạ điện thoại xác định nơi cần gửi đến. Mỗi người hoặc doanh nghiệp được một danh bạ điện thoại.

  • O (n log n): Chúng tôi muốn cá nhân hóa danh bạ điện thoại, vì vậy chúng tôi sẽ tìm tên của mỗi người hoặc doanh nghiệp trong bản sao được chỉ định của họ, sau đó khoanh tròn tên của họ trong cuốn sách và viết một lời cảm ơn ngắn cho sự bảo trợ của họ .

  • O (n 2 ): Xảy ra lỗi tại văn phòng và mỗi mục trong mỗi danh bạ điện thoại có thêm "0" ở cuối số điện thoại. Lấy một số trắng ra và loại bỏ từng số không.

  • O (n · n!): Chúng tôi đã sẵn sàng tải danh bạ lên bến tàu. Thật không may, robot được cho là tải sách đã bị hỏng: nó đặt sách lên xe tải theo thứ tự ngẫu nhiên! Tệ hơn nữa, nó tải tất cả sách lên xe tải, sau đó kiểm tra xem chúng có đúng thứ tự không, và nếu không, nó sẽ dỡ chúng ra và bắt đầu lại. (Đây là loại bogo đáng sợ .)

  • O (n n ): Bạn sửa chữa robot để nó tải mọi thứ chính xác. Ngày hôm sau, một trong những đồng nghiệp của bạn chơi khăm bạn và nối dây cho robot tải vào hệ thống in tự động. Mỗi khi robot đi tải một cuốn sách gốc, máy in của nhà máy sẽ tạo ra một bản sao tất cả các danh bạ! May mắn thay, các hệ thống phát hiện lỗi của robot đủ tinh vi để robot không thử in nhiều bản sao hơn nữa khi gặp một cuốn sách trùng lặp để tải, nhưng nó vẫn phải tải mọi cuốn sách gốc và bản sao được in.


81
@cletus: Trùng hợp, tôi sợ. Tôi chọn nó vì danh bạ có N lớn, mọi người hiểu chúng là gì và làm gì, và vì nó linh hoạt như một ví dụ. Thêm vào đó tôi đã sử dụng robot trong lời giải thích của tôi! Một chiến thắng tất cả xung quanh. (Ngoài ra, có vẻ như câu trả lời của bạn đã được đưa ra trước khi tôi thậm chí còn là thành viên trên StackOverflow để bắt đầu!)
John Women'sella

12
"Một lỗi xảy ra tại văn phòng và mỗi mục trong mỗi danh bạ điện thoại đều có thêm" 0 "ở cuối số điện thoại. Lấy một số trắng ra và xóa từng số không." <- đây không phải là thứ tự N bình phương. N được định nghĩa là kích thước của đầu vào. Kích thước của đầu vào là số lượng điện thoại, là số lượng mỗi cuốn sách nhân với số lượng sách. Đó vẫn là một hoạt động thời gian tuyến tính.
Billy ONeal

21
@Billy: Trong ví dụ này, Nlà số người trong một cuốn sách. Bởi vì mỗi người trong danh bạ điện thoại cũng có được bản sao của cuốn sách đó, có những cuốn danh bạ N giống hệt nhau , mỗi cuốn có Nnhững người trong đó là O (N ^ 2).
John Femaleella

48
Không phải O (1) là trường hợp tốt nhất, chứ không phải là trường hợp xấu nhất vì nó được làm nổi bật một cách kỳ lạ như?
Svip

54
Tôi đã mất thời gian O (long⅝n! N-55/2) để tìm định nghĩa O (log n) mà cuối cùng cũng có ý nghĩa. +1
iAteABug_And_iLiked_it

611

O(log N)về cơ bản có nghĩa là thời gian tăng tuyến tính trong khi ntăng theo cấp số nhân. Vì vậy, nếu phải mất 1thứ hai để tính 10các phần tử, sẽ mất 2vài giây để tính 100các phần tử, 3giây để tính 1000các phần tử, v.v.

Đó là O(log n)khi chúng ta phân chia và chinh phục loại thuật toán, ví dụ như tìm kiếm nhị phân. Một ví dụ khác là sắp xếp nhanh trong đó mỗi lần chúng ta chia mảng thành hai phần và mỗi lần cần có O(N)thời gian để tìm phần tử trục. Do đó nó N O(log N)


108
Ba dòng trí tuệ đánh bại tất cả các câu trả lời tiểu luận khác ... :) Chỉ trong trường hợp ai đó đang thiếu nó, trong bối cảnh lập trình, cơ sở của nhật ký là 2 (không phải 10), vì vậy O (log n) quy mô như 1 giây trong 10 các yếu tố, 2 giây cho 20, 3 cho 40, v.v.
nawfal

3
Đồng ý, ngắn gọn và rõ ràng, mặc dù câu hỏi kết thúc từ OP là làm thế nào để xác định hàm logarit, không hoàn toàn là "nó là gì"
Adam

4
vâng, hàm logarit là hàm nghịch đảo của hàm số mũ. ((log x) cơ sở a) nghịch đảo với (a power x). Phân tích định tính các chức năng này với các biểu đồ sẽ cho trực giác nhiều hơn.
trao đổi quá mức

7
Điều này khiến tôi mất khoảng 3 lần đọc để nhận ra rằng nó không sai. Thời gian đi lên tuyến tính, trong khi số lượng các yếu tố là theo cấp số nhân. Điều này có nghĩa là nhiều yếu tố hơn trong thời gian ít hơn . Đây là cách đánh thuế tinh thần đối với những người hình dung lognhư đường cong log quen thuộc trên biểu đồ.
Qix - MONICA ĐƯỢC PHÂN PHỐI

1
Tôi nghĩ rằng đây là một câu trả lời rất hay, ngoại trừ phần mà nó tuyên bố rằng tìm kiếm nhị phân là một thuật toán phân chia và chinh phục. Không phải vậy.
code_dredd

579

Nhiều câu trả lời hay đã được đăng lên câu hỏi này, nhưng tôi tin rằng chúng ta thực sự đang thiếu một câu trả lời quan trọng - cụ thể là câu trả lời minh họa.

Điều đó có nghĩa gì khi nói rằng chiều cao của cây nhị phân hoàn chỉnh là O (log n)?

Bản vẽ sau mô tả một cây nhị phân. Lưu ý cách mỗi cấp chứa gấp đôi số nút so với mức trên (do đó là nhị phân ):

Cây nhị phân

Tìm kiếm nhị phân là một ví dụ với độ phức tạp O(log n). Giả sử các nút ở cấp độ dưới cùng của cây trong hình 1 biểu thị các mục trong một số bộ sưu tập được sắp xếp. Tìm kiếm nhị phân là một thuật toán phân chia và bản vẽ, và bản vẽ cho thấy chúng ta sẽ cần (nhiều nhất) 4 so sánh để tìm bản ghi mà chúng ta đang tìm kiếm trong bộ dữ liệu 16 mục này.

Giả sử chúng ta đã có một bộ dữ liệu với 32 yếu tố. Tiếp tục bản vẽ ở trên để thấy rằng bây giờ chúng ta sẽ cần 5 so sánh để tìm thấy những gì chúng ta đang tìm kiếm, vì cây chỉ phát triển sâu hơn một cấp khi chúng ta nhân số lượng dữ liệu. Kết quả là, độ phức tạp của thuật toán có thể được mô tả như một thứ tự logarit.

Vẽ log(n)trên một tờ giấy đơn giản, sẽ dẫn đến một biểu đồ trong đó sự gia tăng của đường cong giảm tốc khi ntăng:

O (log n)


60
"Lưu ý cách mỗi cấp chứa số nút gấp đôi so với mức trên (do đó là nhị phân)" Điều này không chính xác. Những gì bạn đang mô tả là một cây nhị phân cân bằng . Cây nhị phân chỉ có nghĩa là mỗi nút có tối đa hai con.
Oenotria

8
Trên thực tế, đó là một cây nhị phân cân bằng rất đặc biệt, được gọi là cây nhị phân hoàn chỉnh. Tôi đã chỉnh sửa câu trả lời nhưng cần ai đó chấp thuận.
dùng21820

5
Một cây nhị phân hoàn chỉnh không cần phải có mức cuối cùng để được điền đầy đủ. Tôi muốn nói, một 'cây nhị phân đầy đủ' là phù hợp hơn.
Ông AJ

Câu trả lời của bạn cố gắng trả lời cụ thể hơn cho vấn đề ban đầu của OP, vì vậy nó tốt hơn câu trả lời được chấp nhận hiện tại (IMO), nhưng nó vẫn chưa hoàn chỉnh: bạn chỉ đưa ra một nửa ví dụ và 2 hình ảnh ...
nbro

2
Cây này có 31 mục trong đó, không phải 16. Tại sao nó được gọi là tập dữ liệu 16 mục? Mỗi nút trên nó đại diện cho một số, nếu không nó sẽ là một cây nhị phân không hiệu quả: P
Perry Monschau

245

Giải thích dưới đây là sử dụng trường hợp của cây nhị phân cân bằng hoàn toàn để giúp bạn hiểu làm thế nào chúng ta có được độ phức tạp thời gian logarit.

Cây nhị phân là trường hợp một vấn đề về kích thước n được chia thành vấn đề phụ có kích thước n / 2 cho đến khi chúng ta gặp vấn đề về kích thước 1:

chiều cao của cây nhị phân

Và đó là cách bạn có được O (log n), đó là khối lượng công việc cần phải thực hiện trên cây ở trên để đạt được giải pháp.

Một thuật toán phổ biến với độ phức tạp thời gian O (log n) là Binary Search có quan hệ đệ quy là T (n / 2) + O (1) tức là ở mọi cấp độ tiếp theo của cây bạn chia vấn đề thành một nửa và thực hiện số lượng công việc bổ sung không đổi.


2
người mới ở đây. Vì vậy, bạn có thể nói chiều cao cây là tỷ lệ phân chia bằng cách đệ quy để đạt kích thước n = 1?
Cody

@Cody, có phần lớn sự quan sát của bạn là chính xác. Ví dụ này minh họa / sử dụng log_2. Quan sát của bạn sẽ chi tiêu vượt xa log_2và sẽ chính xác cho bất kỳ log_xnơi nào x > 1. Thực hiện phép chia thẳng có thể không dẫn đến 1 chính xác, vì vậy bạn có thể muốn nói phép chia đệ quy cho đến khi phép Ceiling()chia mới nhất bằng 1 hoặc tương tự.
James Oravec

198

Tổng quat

Những người khác đã đưa ra các ví dụ sơ đồ tốt, chẳng hạn như sơ đồ cây. Tôi không thấy bất kỳ ví dụ mã đơn giản. Vì vậy, ngoài lời giải thích của tôi, tôi sẽ cung cấp một số thuật toán với các câu lệnh in đơn giản để minh họa cho sự phức tạp của các loại thuật toán khác nhau.

Trước tiên, bạn sẽ muốn có một ý tưởng chung về Logarit, mà bạn có thể nhận được từ https://en.wikipedia.org/wiki/Logarithm . Sử dụng khoa học tự nhiên evà nhật ký tự nhiên. Các môn đồ kỹ thuật sẽ sử dụng log_10 (log cơ sở 10) và các nhà khoa học máy tính sẽ sử dụng log_2 (log cơ sở 2) rất nhiều, vì máy tính là dựa trên nhị phân. Đôi khi bạn sẽ thấy các chữ viết tắt của nhật ký tự nhiên ln(), các kỹ sư thường bỏ _10 và chỉ sử dụng log()và log_2 được viết tắt là lg(). Tất cả các loại logarit đều phát triển theo kiểu tương tự nhau, đó là lý do tại sao chúng có cùng loại log(n).

Khi bạn xem các ví dụ mã bên dưới, tôi khuyên bạn nên xem O (1), sau đó O (n), sau đó O (n ^ 2). Sau khi bạn tốt với những người đó, sau đó nhìn vào những người khác. Tôi đã bao gồm các ví dụ rõ ràng cũng như các biến thể để chứng minh các thay đổi tinh tế vẫn có thể dẫn đến cùng một phân loại.

Bạn có thể nghĩ về O (1), O (n), O (logn), v.v. như các lớp hoặc loại tăng trưởng. Một số loại sẽ mất nhiều thời gian hơn để làm hơn những loại khác. Các loại này giúp cung cấp cho chúng tôi một cách sắp xếp hiệu suất thuật toán. Một số phát triển nhanh hơn khi đầu vào n phát triển. Bảng dưới đây cho thấy sự tăng trưởng về số lượng. Trong bảng dưới đây, hãy nghĩ về log (n) là trần của log_2.

nhập mô tả hình ảnh ở đây

Ví dụ mã đơn giản của các danh mục Big O khác nhau:

O (1) - Ví dụ về thời gian không đổi:

  • Thuật toán 1:

Thuật toán 1 in xin chào một lần và nó không phụ thuộc vào n, vì vậy nó sẽ luôn chạy trong thời gian không đổi, vì vậy nó là như vậy O(1).

print "hello";
  • Thuật toán 2:

Thuật toán 2 in xin chào 3 lần, tuy nhiên nó không phụ thuộc vào kích thước đầu vào. Ngay cả khi n phát triển, thuật toán này sẽ luôn chỉ in xin chào 3 lần. Điều đó đang được nói 3, là một hằng số, vì vậy thuật toán này cũng vậy O(1).

print "hello";
print "hello";
print "hello";

O (log (n)) - Ví dụ logarit:

  • Thuật toán 3 - Điều này hoạt động như "log_2"

Thuật toán 3 biểu thị một thuật toán chạy trong log_2 (n). Lưu ý hoạt động bài của vòng lặp for nhân giá trị hiện tại của i với 2, do đó iđi từ 1 đến 2 đến 4 đến 8 đến 16 đến 32 ...

for(int i = 1; i <= n; i = i * 2)
  print "hello";
  • Thuật toán 4 - Điều này hoạt động như "log_3"

Thuật toán 4 thể hiện log_3. Thông báo iđi từ 1 đến 3 đến 9 đến 27 ...

for(int i = 1; i <= n; i = i * 3)
  print "hello";
  • Thuật toán 5 - Điều này hoạt động như "log_1.02"

Thuật toán 5 rất quan trọng, vì nó giúp chỉ ra rằng miễn là số đó lớn hơn 1 và kết quả được nhân lên nhiều lần so với chính nó, rằng bạn đang xem xét một thuật toán logarit.

for(double i = 1; i < n; i = i * 1.02)
  print "hello";

O (n) - Ví dụ về thời gian tuyến tính:

  • Thuật toán 6

Thuật toán này là đơn giản, mà in xin chào n lần.

for(int i = 0; i < n; i++)
  print "hello";
  • Thuật toán 7

Thuật toán này hiển thị một biến thể, trong đó nó sẽ in hello n / 2 lần. n / 2 = 1/2 * n. Chúng ta bỏ qua hằng số 1/2 và thấy rằng thuật toán này là O (n).

for(int i = 0; i < n; i = i + 2)
  print "hello";

O (n * log (n)) - nlog (n) Ví dụ:

  • Thuật toán 8

Hãy nghĩ về điều này như là một sự kết hợp của O(log(n))O(n). Việc lồng các vòng lặp giúp chúng ta có đượcO(n*log(n))

for(int i = 0; i < n; i++)
  for(int j = 1; j < n; j = j * 2)
    print "hello";
  • Thuật toán 9

Thuật toán 9 giống như thuật toán 8, nhưng mỗi vòng lặp đã cho phép các biến thể, điều này vẫn dẫn đến kết quả cuối cùng là O(n*log(n))

for(int i = 0; i < n; i = i + 2)
  for(int j = 1; j < n; j = j * 3)
    print "hello";

O (n ^ 2) - n bình phương Ví dụ:

  • Thuật toán 10

O(n^2) có được dễ dàng bằng cách lồng tiêu chuẩn cho các vòng lặp.

for(int i = 0; i < n; i++)
  for(int j = 0; j < n; j++)
    print "hello";
  • Thuật toán 11

Giống như thuật toán 10, nhưng với một số biến thể.

for(int i = 0; i < n; i++)
  for(int j = 0; j < n; j = j + 2)
    print "hello";

O (n ^ 3) - n Ví dụ:

  • Thuật toán 12

Điều này giống như thuật toán 10, nhưng với 3 vòng thay vì 2.

for(int i = 0; i < n; i++)
  for(int j = 0; j < n; j++)
    for(int k = 0; k < n; k++)
      print "hello";
  • Thuật toán 13

Giống như thuật toán 12, nhưng với một số biến thể vẫn mang lại O(n^3).

for(int i = 0; i < n; i++)
  for(int j = 0; j < n + 5; j = j + 2)
    for(int k = 0; k < n; k = k + 3)
      print "hello";

Tóm lược

Ở trên đưa ra một số ví dụ thẳng về phía trước và các biến thể để giúp chứng minh những thay đổi tinh tế nào có thể được đưa ra mà thực sự không thay đổi phân tích. Hy vọng nó cung cấp cho bạn đủ hiểu biết.


17
Tuyệt vời. Lời giải thích tốt nhất cho tôi mà tôi từng thấy. Nó sẽ đẹp hơn nếu O(n^2)được ghi nhận là sự kết hợp của O(n)O(n), vì vậy O(n) * O(n) = O(n * n) = O(n^2). Nó cảm thấy như một chút nhảy mà không có phương trình này. Đây là sự lặp lại của lời giải thích trước, nhưng tôi nghĩ rằng sự lặp lại này có thể cung cấp sự tự tin hơn cho người đọc để hiểu.
Eonil

2
Đây chỉ đơn giản là lời giải thích tốt nhất từng có.
Edgar Kiljak

2
@IceTea, để cung cấp cho bạn cái nhìn sâu sắc / trực giác cho câu hỏi của bạn. Nếu bạn vạch ra nso với n/2bạn sẽ thấy rằng cả hai đều tạo thành một đường thẳng. Điều này đặt chúng vào cùng một lớp vì chúng có tốc độ tăng trưởng tương tự (nghĩ về nó như hình dạng của biểu đồ). Tương tự, nếu bạn lập biểu đồ log_2so với log_3bạn sẽ thấy rằng cả hai đều có "hình dạng tương tự" hoặc "tốc độ tăng trưởng tương tự".
James Oravec

1
@IceTea, Giải thích được đưa ra bởi @Shai và @James thì chính xác hơn, n/2 or 2n or n+2 or nsẽ có dòng khác nhau trong biểu đồ nhưng chúng sẽ có cùng tốc độ tăng trưởng, có nghĩa là tất cả chúng sẽ theo một sự tăng trưởng tuyến tính.
Naresh Joshi

2
Làm thế nào về trường hợp chúng ta có hai vòng lặp lồng nhau, nhưng vòng lặp thứ hai phụ thuộc vào vòng thứ nhất, sự phụ thuộc này có ảnh hưởng đến độ phức tạp thời gian không?
Bionix1441

131

Nếu bạn có một chức năng:

1 millisecond to complete if you have 2 elements.
2 milliseconds to complete if you have 4 elements.
3 milliseconds to complete if you have 8 elements.
4 milliseconds to complete if you have 16 elements.
...
n milliseconds to complete if you have 2^n elements.

Sau đó mất thời gian đăng nhập 2 (n). Các Big O ký hiệu , lỏng lẻo nói, phương tiện rằng mối quan hệ chỉ cần là đúng đối với n lớn, và các yếu tố liên tục và các điều khoản nhỏ hơn có thể được bỏ qua.


log2 (n) có giống với o (log n) không?
Sven van den Boogaart

Có, xem nhận xét của nawfal để biết câu trả lời khác tại đây: (sao chép) - trong ngữ cảnh lập trình, cơ sở của nhật ký là 2 (không phải 10), vì vậy O (log n) chia tỷ lệ như 1 giây cho 10 phần tử, 2 giây trong 20 , 3 cho 40, v.v.
Andrejs

@SvenvandenBoogaart, ví dụ trong giải pháp này minh họa log_2, đó là trong lớp O(log(n)). Có rất nhiều những người khác trong cùng một loại O(log(n))tức là log_xnơix > 1
James Oravec

@Andrejs, nhận xét của bạn so O(log n) scales like 1 sec for 10 elements, 2 sec for 20, 3 for 40 etckhông chính xác. Đó là mô hình / lớp sẽ phù hợp / class với O(n)không O(log(n)). Nếu ai đó quan tâm log_10thì một ví dụ tương đương sẽ là 1 giây cho 10 yếu tố, 2 giây cho 100, 3 cho 1000, v.v.
James Oravec

99

Thời gian chạy logarit ( O(log n)) về cơ bản có nghĩa là thời gian chạy tăng tỷ lệ với logarit của kích thước đầu vào - ví dụ, nếu 10 mục mất nhiều thời gian nhất xvà 100 mục mất tối đa 2x, và, 10.000 mục mất nhiều nhất 4x, sau đó nó trông giống như một O(log n)sự phức tạp thời gian.


1
+1, nhưng bạn thực sự nên chỉ ra rằng đó là log2 chứ không phải log10.
Quảng trường Adriano Varoli

62
log2 hoặc log10 là không liên quan. Chúng chỉ khác nhau bởi một yếu tố tỷ lệ, khiến chúng có cùng thứ tự, tức là chúng vẫn tăng trưởng với cùng một tốc độ.
Noldorin

17
Điều thú vị về logarit là khi so sánh độ cao tương đối, cơ sở chính xác bạn sử dụng không thành vấn đề. log 10,000 / log 100là 2 bất kể bạn sử dụng cơ sở nào.
Anon.

12
Để được nitpicky, O (lg n) có nghĩa là thời gian chạy tỷ lệ thuận nhất với lg n. Những gì bạn mô tả là Theta (lg n).

1
@rgrig: Đó là sự thật. Tôi đã chỉnh sửa trong một vài "tối đa" để chỉ ra bản chất giới hạn trên của big-O.
Anon.

95

Lôgarit

Ok, hãy thử và hiểu đầy đủ logarit thực sự là gì.

Hãy tưởng tượng chúng ta có một sợi dây và chúng ta đã buộc nó vào một con ngựa. Nếu sợi dây được buộc trực tiếp vào con ngựa, lực mà con ngựa sẽ cần phải kéo đi (giả sử, từ một người đàn ông) là trực tiếp 1.

Bây giờ hãy tưởng tượng sợi dây được vòng quanh một cây sào. Con ngựa để thoát khỏi bây giờ sẽ phải kéo mạnh hơn nhiều lần. Số lần sẽ phụ thuộc vào độ nhám của dây và kích thước của cột, nhưng giả sử nó sẽ nhân lên gấp 10 lần sức mạnh của một người (khi sợi dây hoàn thành một lượt).

Bây giờ nếu dây được thắt một lần, con ngựa sẽ cần kéo mạnh hơn gấp 10 lần. Nếu con người quyết định làm cho con ngựa thực sự khó khăn, anh ta có thể vòng dây một lần nữa quanh một cây cột, tăng sức mạnh của nó thêm 10 lần nữa. Một vòng lặp thứ ba sẽ một lần nữa tăng sức mạnh thêm 10 lần nữa.

nhập mô tả hình ảnh ở đây

Chúng ta có thể thấy rằng với mỗi vòng lặp, giá trị tăng thêm 10. Số lượt cần thiết để có được bất kỳ số nào được gọi là logarit của số tức là chúng ta cần 3 bài để nhân lên gấp 1000 lần, 6 bài để nhân sức mạnh của bạn lên gấp 1000 lần 1.000.000.

3 là logarit của 1.000 và 6 là logarit của 1.000.000 (cơ sở 10).

Vậy O (log n) thực sự có nghĩa là gì?

Trong ví dụ của chúng tôi ở trên, 'tốc độ tăng trưởng' của chúng tôi là O (log n) . Đối với mỗi vòng lặp bổ sung, lực mà dây của chúng tôi có thể xử lý gấp 10 lần:

Turns | Max Force
  0   |   1
  1   |   10
  2   |   100
  3   |   1000
  4   |   10000
  n   |   10^n

Bây giờ ví dụ trên đã sử dụng cơ sở 10, nhưng may mắn thay, cơ sở của nhật ký là không đáng kể khi chúng ta nói về ký hiệu o lớn.

Bây giờ hãy tưởng tượng bạn đang cố đoán một số trong khoảng 1-100.

Your Friend: Guess my number between 1-100! 
Your Guess: 50
Your Friend: Lower!
Your Guess: 25
Your Friend: Lower!
Your Guess: 13
Your Friend: Higher!
Your Guess: 19
Your Friend: Higher!
Your Friend: 22
Your Guess: Lower!
Your Guess: 20
Your Friend: Higher!
Your Guess: 21
Your Friend: YOU GOT IT!  

Bây giờ bạn phải mất 7 lần đoán để làm điều này đúng. Nhưng mối quan hệ ở đây là gì? Số lượng mục nhiều nhất mà bạn có thể đoán từ mỗi lần đoán bổ sung là bao nhiêu?

Guesses | Items
  1     |   2
  2     |   4
  3     |   8
  4     |   16
  5     |   32
  6     |   64
  7     |   128
  10    |   1024

Sử dụng biểu đồ, chúng ta có thể thấy rằng nếu chúng ta sử dụng tìm kiếm nhị phân để đoán một số trong khoảng 1-100, chúng ta sẽ mất tối đa 7 lần thử. Nếu chúng ta có 128 số, chúng ta cũng có thể đoán được số đó trong 7 số, nhưng 129 số sẽ đưa chúng ta nhiều nhất là 8 lần thử (liên quan đến logarit, ở đây chúng ta sẽ cần 7 lần đoán cho phạm vi giá trị 128, 10 lần đoán cho phạm vi giá trị 1024 7 là logarit của 128, 10 là logarit của 1024 (cơ sở 2)).

Lưu ý rằng tôi đã in đậm "nhiều nhất". Ký hiệu Big-O luôn đề cập đến trường hợp xấu hơn. Nếu bạn may mắn, bạn có thể đoán số trong một lần thử và vì vậy trường hợp tốt nhất là O (1), nhưng đó là một câu chuyện khác.

Chúng ta có thể thấy rằng với mỗi lần đoán, tập dữ liệu của chúng ta đang bị thu hẹp. Một nguyên tắc nhỏ để xác định xem thuật toán có thời gian logarit hay không là xem liệu tập dữ liệu co lại theo một thứ tự nhất định sau mỗi lần lặp

Còn O (n log n) thì sao?

Cuối cùng, bạn sẽ bắt gặp một thuật toán O (n log (n)) tuyến tính thời gian tuyến tính . Quy tắc ngón tay cái ở trên áp dụng một lần nữa, nhưng lần này hàm logarit phải chạy n lần, ví dụ như giảm kích thước của danh sách n lần , xảy ra trong các thuật toán như sáp nhập.

Bạn có thể dễ dàng xác định nếu thời gian thuật toán là n log n. Tìm kiếm một vòng lặp bên ngoài lặp qua danh sách (O (n)). Sau đó nhìn xem có vòng lặp bên trong không. Nếu vòng lặp bên trong đang cắt / giảm tập dữ liệu trên mỗi lần lặp, thì vòng lặp đó là (O (log n)), và do đó thuật toán tổng thể là = O (n log n) .

Tuyên bố miễn trừ trách nhiệm: Ví dụ logarit dây được lấy từ cuốn sách tuyệt vời của nhà toán học xuất sắc của W.Sawyer .


Không In our example above, our 'growth rate' is O(log n). For every additional loop, the force our rope can handle is 10 times more, được hỗ trợ bởi biểu đồ hiển thị n == số vòng lặp và our 'growth rate'=> 10 ^ n, đó không phải là log n. Ví dụ có thể được thực hiện chính xác bằng cách thực hiện n=# horses, trong đó yêu cầu các vòng lặp log n phải hạn chế. Các ví dụ sư phạm kém tạo ra những sinh viên chỉ tin rằng họ hiểu.
psimpson

56

Bạn có thể nghĩ về O (log N) bằng trực giác bằng cách nói thời gian tỷ lệ thuận với số chữ số trong N.

Nếu một hoạt động thực hiện công việc thời gian không đổi trên mỗi chữ số hoặc bit của đầu vào, toàn bộ hoạt động sẽ mất thời gian tỷ lệ thuận với số chữ số hoặc bit trong đầu vào, chứ không phải độ lớn của đầu vào; do đó, O (log N) chứ không phải O (N).

Nếu một thao tác tạo ra một loạt các quyết định thời gian không đổi, mỗi nửa sẽ giảm một nửa (giảm theo hệ số 3, 4, 5 ..) kích thước của đầu vào được xem xét, toàn bộ sẽ mất thời gian tỷ lệ thuận với cơ sở 2 (cơ sở 3 , cơ sở 4, cơ sở 5 ...) có kích thước N của đầu vào, thay vì là O (N).

Và như thế.


7
Tôi đủ chính xác và dễ dàng nắm bắt hơn hầu hết các giải thích, tôi nghĩ.
T.

đó là một lời giải thích log<sub>10</sub> N, phải không?
LiuYan 刘

1
@LiuYan 刘 họ không cho biết số lượng chữ số là bao nhiêu. Trong mọi trường hợp, log (n) = log₁₀ (n) / log₁₀ (2) và 1 / log₁₀ (2) do đó là một số nhân không đổi, với cùng một nguyên tắc áp dụng cho tất cả các căn cứ khác. Điều này cho thấy hai điều. Đầu tiên, nguyên tắc của moonshadow áp dụng cho bất kỳ cơ sở nào (mặc dù cơ sở càng thấp thì càng ít "jags" trong ước tính) và O (log n) là O (log n) cho dù tính toán nào đưa bạn đến kết luận đó .
Jon Hanna

"Tỷ lệ" ... "mỗi cái giảm một nửa kích thước của đầu vào" ??????
csguy

52

Cách tốt nhất mà tôi luôn phải hình dung về mặt tinh thần một thuật toán chạy trong O (log n) như sau:

Nếu bạn tăng kích thước bài toán lên một số nhân (tức là nhân kích thước của nó với 10), công việc chỉ được tăng thêm một lượng phụ gia.

Áp dụng điều này cho câu hỏi cây nhị phân của bạn để bạn có một ứng dụng hay: nếu bạn nhân đôi số nút trong cây nhị phân, chiều cao chỉ tăng thêm 1 (một lượng phụ gia). Nếu bạn nhân đôi nó một lần nữa, nó vẫn chỉ tăng thêm 1. (Rõ ràng tôi cho rằng nó vẫn cân bằng và như vậy). Bằng cách đó, thay vì nhân đôi công việc của bạn khi kích thước vấn đề được nhân lên, bạn chỉ làm việc nhiều hơn một chút. Đó là lý do tại sao thuật toán O (log n) là tuyệt vời.


52

Đầu tiên tôi khuyên bạn nên đọc cuốn sách sau;

Thuật toán (Phiên bản thứ 4)

Đây là một số chức năng và sự phức tạp dự kiến ​​của họ. Các con số đang chỉ ra tần số thực hiện câu lệnh .

Đây là một số chức năng và sự phức tạp dự kiến ​​của chúng

Theo sau Bảng xếp hạng độ phức tạp Big-O cũng được lấy từ bigochcoat Biểu đồ độ phức tạp Big-O

Cuối cùng giới thiệu rất đơn giản có hiển thị cách tính toán;

Cấu tạo của tần số thực hiện câu lệnh của chương trình.

Phân tích thời gian chạy của một chương trình (ví dụ).

Phân tích thời gian chạy chương trình


5
Tôi sẽ không đặt O (n log n) vào giỏ xấu . Nó thuộc về một trong những công bằng .
André Werlang

Khi xem biểu đồ phức tạp big-O (ở trên), bạn phải nhớ O (n) là điểm tuyến tính thực tế, không phải là bảng màu hồng / cam. @Andre Đó là lý do tại sao O (n log n) được đánh dấu chính xác trong khung hiệu suất 'xấu', nó có hiệu suất kém hơn tuyến tính.
JavaBeast

@JavaBeast đúng, trong khi hiệu suất của O (n log n) kém hơn về mặt kỹ thuật so với O (n), hãy tham khảo bảng trên, trình bày so sánh tốt về chúng (xem sự tăng trưởng của cả hai). otoh biểu đồ, từ một nguồn khác nhau, trái ngược nhau vì nó đặt O (1) và O (log n) trong cùng một hàng hóa / xuất sắc. thứ tự tăng trưởng tương đối của chúng tương đương với O (n) và O (n log n). tl; dr; O (n log n) không xuất sắc, nhưng khác xa.
André Werlang

1
Câu trả lời này là sai! Nó giả sử rằng N = N * N. Trong thực tế N = N! Ví dụ của bạn thực sự là N cubed. Bạn làm tương tự trong biểu đồ của bạn. O (n) của bạn thực sự nên là sự phân chia giữa khủng khiếp và tồi tệ. Chứng minh toán học: Bạn nói rằng vòng lặp for không đổi với O (1). Đó là những gì 1 thực sự có nghĩa, không phụ thuộc vào N. Nó chỉ có nghĩa là không thay đổi. Nhưng nó khác nhau vì nó phụ thuộc vào N. Twice N và một nửa thời gian. Do đó, nó không hợp lệ. Nếu đó là từ cuốn sách đó, đừng mua nó! Mã đồ họa bạn đã hiển thị không có thật, đó là một trò đùa, nhìn, "Theỉ", có nghĩa là ba người quan hệ tình dục cùng một lúc! OMG
jgmjgm

1
Không nên O (n) trên đường chéo?
gyosifov

46

Nhật ký b (n) là gì?

Đó là số lần bạn có thể cắt một bản ghi có độ dài n liên tục thành b phần bằng nhau trước khi đạt đến một phần có kích thước 1.


Nhận xét tuyệt vời! Đó là ngắn gọn và chính xác câu trả lời tôi sau.
DennisL

18

Các thuật toán phân chia và chinh phục thường có một lognthành phần cho thời gian chạy. Điều này xuất phát từ việc giảm một nửa lặp lại của đầu vào.

Trong trường hợp tìm kiếm nhị phân, mỗi lần lặp bạn sẽ bỏ đi một nửa số đầu vào. Cần lưu ý rằng trong ký hiệu Big-O, log là log cơ sở 2.

Chỉnh sửa: Như đã lưu ý, cơ sở nhật ký không thành vấn đề, nhưng khi đạt được hiệu suất Big-O của thuật toán, hệ số nhật ký sẽ đến từ một nửa, do đó tôi nghĩ đó là cơ sở 2.


2
Tại sao nó đăng nhập cơ sở 2? Ví dụ, trong quicksort ngẫu nhiên, tôi không nghĩ đó là cơ sở 2. Theo như tôi biết, cơ sở không quan trọng, vì cơ sở log a (n) = log2 (n) / log2 (a), vì vậy mọi logarit khác với hằng số khác và hằng số được bỏ qua trong ký hiệu big-o. Trong thực tế, viết cơ sở của một bản ghi trong ký hiệu big-o là một sai lầm theo quan điểm của tôi, vì bạn đang viết một hằng số.
IVlad


Rất đúng là nó có thể được chuyển đổi thành bất kỳ cơ sở nào và điều đó không thành vấn đề, nhưng nếu bạn đang cố gắng đạt được hiệu suất Big-O và bạn thấy giảm một nửa liên tục, sẽ giúp hiểu rằng bạn sẽ không thấy cơ sở nhật ký 10 được phản ánh trong mã.
David Kanarek

Bỏ qua một bên: Trong những thứ như cây B, nơi các nút có một quạt nhiều hơn 2 (tức là "rộng hơn cây nhị phân), bạn vẫn sẽ thấy sự tăng trưởng của O (logn), bởi vì nó vẫn phân chia - và -conquer, nhưng cơ sở của nhật ký sẽ liên quan đến fan-out.
Roger Lipscombe

Việc cải tiến trên log 2 thực sự rất hữu ích.
Dan Rosenstark

15

Nhưng chính xác thì O (log n) là gì? Ví dụ, có nghĩa gì khi nói rằng chiều cao của cây nhị phân hoàn chỉnh là O (log n)?

Tôi sẽ viết lại đây là 'chiều cao của cây nhị phân hoàn chỉnh là log n'. Hình chiều cao của cây nhị phân hoàn chỉnh sẽ là O (log n), nếu bạn đi qua từng bước một.

Tôi không thể hiểu làm thế nào để xác định một chức năng với thời gian logarit.

Logarit về cơ bản là nghịch đảo của lũy thừa. Vì vậy, nếu mỗi 'bước' của chức năng của bạn loại bỏ một yếu tố của các yếu tố khỏi bộ mục gốc, đó là thuật toán thời gian logarit.

Đối với ví dụ về cây, bạn có thể dễ dàng thấy rằng việc giảm một mức các nút sẽ cắt giảm số lượng phần tử theo cấp số nhân khi bạn tiếp tục di chuyển ngang. Ví dụ phổ biến về việc xem qua danh bạ điện thoại được sắp xếp tên về cơ bản tương đương với việc duyệt qua cây tìm kiếm nhị phân (trang giữa là phần tử gốc và bạn có thể suy luận ở mỗi bước là đi sang trái hay phải).


3
+1 để đề cập đến "Logarit về cơ bản là nghịch đảo của lũy thừa".
Talonx 16/11/13

12

2 trường hợp này sẽ mất thời gian O (log n)

case 1: f(int n) {
      int i;
      for (i = 1; i < n; i=i*2)
        printf("%d", i);
    }


 case 2  : f(int n) {
      int i;
      for (i = n; i>=1 ; i=i/2)
        printf("%d", i);
    }

Tôi chắc chắn rằng tôi đang thiếu một cái gì đó, nhưng tôi sẽ luôn luôn bằng không và các vòng lặp chạy mãi trong cả hai trường hợp đó, vì 0 * 2 = 0 và 0/2 = 0?
dj_segfault

2
@dj_segfault, đó là sai lầm của tôi. Tôi nghĩ bây giờ nó có ý nghĩa .. :)
Ravi Bisla

@RaviBisla Các câu trả lời khác nêu rõ rằng đầu vào 10 sẽ mất 1 lần nhiều nhất là 10 vòng và đầu vào 100 sẽ mất 3 lần thời gian đầu vào là 1, chắc chắn không phải như vậy với các ví dụ đó. stackoverflow.com/a/2307330/1667868
Sven van den Boogaart

12

O (log n) là một chút sai lệch, chính xác hơn là O (log 2 n), tức là (logarit với cơ sở 2).

Chiều cao của cây nhị phân cân bằng là O (log 2 n), vì mỗi nút có hai (lưu ý "hai" như trong log 2 n) các nút con. Vì vậy, một cây có n nút có chiều cao log 2 n.

Một ví dụ khác là tìm kiếm nhị phân, có thời gian chạy là O (log 2 n) bởi vì ở mỗi bước bạn chia không gian tìm kiếm cho 2.


4
O (log n) là cùng thứ tự với O (ld n) hoặc O (LN n). Chúng tỷ lệ thuận. Tôi hiểu rằng cho mục đích học tập, việc sử dụng ld sẽ dễ dàng hơn.
helios

4
"Chính xác hơn đó là O (ld n)" - Không, không phải: tất cả các bản ghi là cùng một thứ tự (mỗi bản ghi khác nhau chỉ bởi một số yếu tố tỷ lệ không đổi, bị bỏ qua / không thể biết được).
ChrisW

1
Bạn đúng chris, từ ngữ rất xấu. nên nói nó như helios đã làm. nó giúp cho việc học / hiểu nhưng cuối cùng tất cả các bản ghi là cùng một thứ tự.
stmax

10

O(log n) đề cập đến một hàm (hoặc thuật toán hoặc bước trong thuật toán) hoạt động trong một khoảng thời gian tỷ lệ với logarit (thường là cơ sở 2 trong hầu hết các trường hợp, nhưng không phải luôn luôn, và trong mọi trường hợp, điều này không đáng kể bằng ký hiệu big-O *) kích thước của đầu vào.

Hàm số logarit là nghịch đảo của hàm số mũ. Nói cách khác, nếu đầu vào của bạn tăng theo cấp số nhân (chứ không phải tuyến tính, như bạn thường xem xét nó), chức năng của bạn sẽ phát triển tuyến tính.

O(log n)thời gian chạy rất phổ biến trong bất kỳ loại ứng dụng phân chia và chinh phục nào, bởi vì bạn (lý tưởng) cắt giảm công việc một nửa mỗi lần. Nếu trong mỗi bước phân chia hoặc chinh phục, bạn đang thực hiện công việc thời gian không đổi (hoặc công việc không phải là thời gian không đổi, nhưng với thời gian tăng chậm hơn O(log n)), thì toàn bộ chức năng của bạn là O(log n). Thay vào đó, khá phổ biến khi mỗi bước yêu cầu thời gian tuyến tính trên đầu vào; điều này sẽ lên tới tổng độ phức tạp của thời gian O(n log n).

Độ phức tạp thời gian chạy của tìm kiếm nhị phân là một ví dụ về O(log n). Điều này là do trong tìm kiếm nhị phân, bạn luôn bỏ qua một nửa số đầu vào của mình trong mỗi bước sau bằng cách chia mảng thành một nửa và chỉ tập trung vào một nửa với mỗi bước. Mỗi bước là thời gian không đổi, bởi vì trong tìm kiếm nhị phân, bạn chỉ cần so sánh một yếu tố với khóa của mình để tìm ra việc cần làm tiếp theo bất kể mức độ mà bạn đang xem xét là lớn đến mức nào. Vì vậy, bạn thực hiện các bước log (n) / log (2).

Độ phức tạp thời gian chạy của sắp xếp hợp nhất là một ví dụ về O(n log n). Điều này là do bạn đang chia mảng thành một nửa cho mỗi bước, dẫn đến tổng số bước xấp xỉ (n) / log (2). Tuy nhiên, trong mỗi bước bạn cần thực hiện các thao tác hợp nhất trên tất cả các phần tử (cho dù đó là một thao tác hợp nhất trên hai danh sách con của n / 2 phần tử hay hai thao tác hợp nhất trên bốn danh sách con của n / 4 phần tử, đều không liên quan vì nó phải thêm vào làm điều này cho n phần tử trong mỗi bước). Như vậy, tổng độ phức tạp là O(n log n).

* Hãy nhớ rằng ký hiệu big-O, theo định nghĩa , hằng số không quan trọng. Cũng bởi sự thay đổi của quy tắc cơ sở cho logarit, sự khác biệt duy nhất giữa logarit của các cơ sở khác nhau là một yếu tố không đổi.


Ghi chú * cuối cùng đã giải quyết sự nhầm lẫn của tôi về logarit dựa trên 2 hoặc 10 :) Cảm ơn rất nhiều.
yahya


9

Nói một cách đơn giản: Ở mỗi bước của thuật toán, bạn có thể cắt công việc xuống một nửa. (Không có triệu chứng tương đương với thứ ba, thứ tư, ...)


2
Câu trả lời này rất thiếu chính xác. Trước hết, bạn có thể nghĩ đến việc cắt giảm một nửa công việc chỉ trong trường hợp logarit ở cơ sở 2. Thật không thể tin được câu trả lời này (và hầu hết các câu trả lời cho câu hỏi ban đầu) đã nhận được rất nhiều phiếu bầu. "(Không có triệu chứng tương đương với thứ ba, thứ tư, ...)"? Tại sao trả lời một câu hỏi nếu bạn không có thời gian?
nbro

8

Nếu bạn vẽ một hàm logarit trên máy tính đồ họa hoặc một cái gì đó tương tự, bạn sẽ thấy rằng nó tăng rất chậm - thậm chí chậm hơn cả hàm tuyến tính.

Đây là lý do tại sao các thuật toán có độ phức tạp thời gian logarit rất được tìm kiếm: ngay cả đối với n thực sự lớn (ví dụ: n = 10 ^ 8), chúng thực hiện nhiều hơn mức chấp nhận được.


7

Nhưng chính xác thì O (log n) là gì

Điều đó có nghĩa chính xác là "như ncó xu hướng infinity, timexu hướng hướng tới a*log(n)đâu alà một yếu tố tỷ lệ không đổi".

Hoặc thực sự, nó không hoàn toàn có nghĩa là; nhiều khả năng nó có nghĩa là một cái gì đó như " timechia theo a*log(n)xu hướng 1".

"Có xu hướng về phía" có ý nghĩa toán học thông thường từ 'phân tích': "nếu bạn chọn ví dụ, rằng bất kỳ tùy tiện nhỏ khác không liên tục k, sau đó tôi có thể tìm thấy một giá trị tương ứng Xnhư vậy ((time/(a*log(n))) - 1)là ít hơn kcho tất cả các giá trị của nlớn hơn X."


Theo thuật ngữ lay, điều đó có nghĩa là phương trình thời gian có thể có một số thành phần khác: ví dụ: nó có thể có thời gian khởi động không đổi; nhưng các thành phần khác này nhạt dần theo hướng không đáng kể đối với các giá trị lớn của n và a * log (n) là thuật ngữ thống trị cho n lớn.

Lưu ý rằng nếu phương trình là, ví dụ ...

thời gian (n) = a + b log (n) + c n + d n n

... thì đây sẽ là O (n bình phương) bởi vì, bất kể giá trị của các hằng số a, b, c và khác không d, d*n*nthuật ngữ này sẽ luôn chiếm ưu thế so với các giá trị n đủ lớn khác.

Đó là ký hiệu bit O nghĩa là gì: nó có nghĩa là "thứ tự của thuật ngữ chi phối cho bất kỳ n đủ lớn nào".



7

Tôi có thể thêm một cái gì đó thú vị, mà tôi đã đọc trong cuốn sách của Kormen và vv cách đây rất lâu. Bây giờ, hãy tưởng tượng một vấn đề, nơi chúng ta phải tìm một giải pháp trong một không gian vấn đề. Không gian vấn đề này nên hữu hạn.

Bây giờ, nếu bạn có thể chứng minh, rằng ở mỗi lần lặp thuật toán của bạn, bạn đã cắt một phần của không gian này, không dưới một giới hạn nào đó, điều này có nghĩa là thuật toán của bạn đang chạy trong thời gian O (logN).

Tôi nên chỉ ra rằng, chúng ta đang nói ở đây về một giới hạn phân số tương đối, không phải là giới hạn tuyệt đối. Tìm kiếm nhị phân là một ví dụ cổ điển. Ở mỗi bước chúng ta vứt đi 1/2 không gian vấn đề. Nhưng tìm kiếm nhị phân không phải là ví dụ duy nhất như vậy. Giả sử, bạn đã chứng minh bằng cách nào đó, rằng tại mỗi bước bạn vứt bỏ ít nhất 1/128 không gian vấn đề. Điều đó có nghĩa là, chương trình của bạn vẫn đang chạy ở thời gian O (logN), mặc dù chậm hơn đáng kể so với tìm kiếm nhị phân. Đây là một gợi ý rất tốt trong việc phân tích các thuật toán đệ quy. Thông thường có thể chứng minh rằng ở mỗi bước, đệ quy sẽ không sử dụng một vài biến thể và điều này dẫn đến việc cắt một số phần trong không gian vấn đề.


6

Tôi có thể đưa ra một ví dụ cho một vòng lặp for và có thể một khi đã nắm bắt được khái niệm này, có thể nó sẽ đơn giản hơn để hiểu trong các bối cảnh khác nhau.

Điều đó có nghĩa là trong vòng lặp, bước phát triển theo cấp số nhân. Ví dụ

for (i=1; i<=n; i=i*2) {;}

Độ phức tạp trong ký hiệu O của chương trình này là O (log (n)). Hãy thử lặp lại bằng tay (n ở đâu đó trong khoảng từ 512 đến 1023 (không bao gồm 1024):

step: 1   2   3   4   5    6    7    8     9     10
   i: 1   2   4   8   16   32   64   128   256   512

Mặc dù n ở đâu đó trong khoảng từ 512 đến 1023, nhưng chỉ có 10 lần lặp lại. Điều này là do bước trong vòng lặp tăng theo cấp số nhân và do đó chỉ mất 10 lần lặp để đạt được kết thúc.

Logarit của x (đến cơ sở của a) là hàm ngược của a ^ x.

Nó giống như nói rằng logarit là nghịch đảo của hàm mũ.

Bây giờ hãy thử xem nó theo cách đó, nếu hàm mũ tăng rất nhanh thì logarit phát triển (nghịch đảo) rất chậm.

Sự khác biệt giữa O (n) và O (log (n)) là rất lớn, tương tự như sự khác biệt giữa O (n) và O (a ^ n) (a là hằng số).


6

Trên thực tế, nếu bạn có một danh sách n phần tử và tạo một cây nhị phân từ danh sách đó (như trong thuật toán chia và chinh phục), bạn sẽ tiếp tục chia cho 2 cho đến khi bạn đạt được danh sách kích thước 1 (các lá).

Ở bước đầu tiên, bạn chia cho 2. Sau đó, bạn có 2 danh sách (2 ^ 1), bạn chia cho mỗi 2, vì vậy bạn có 4 danh sách (2 ^ 2), bạn chia lại, bạn có 8 danh sách (2 ^ 3 ) và cứ như vậy cho đến khi kích thước danh sách của bạn là 1

Điều đó cho bạn phương trình:

n/(2^steps)=1 <=> n=2^steps <=> lg(n)=steps

(bạn lấy lg của mỗi bên, lg là cơ sở nhật ký 2)


2
Cho đến khi một số phần mềm độc hại bắt đầu chèn một danh sách mới với độ dài x ở hai cấp độ trước khi các nút rời khỏi. Sau đó, nó dường như sẽ là một vòng lặp vô tận ...
Francis Cugler

1
Tôi không nhận được bình luận của bạn. Là giải thích của tôi sai?
Dinaiz

1
Tôi chỉ làm một trò đùa giả định. Tôi thực sự không có ý nghĩa gì với nó.
Francis Cugler

6

Mỗi khi chúng tôi viết một thuật toán hoặc mã, chúng tôi cố gắng phân tích độ phức tạp tiệm cận của nó. Nó khác với sự phức tạp thời gian của nó .

Độ phức tạp tiệm cận là hành vi về thời gian thực hiện của thuật toán trong khi độ phức tạp thời gian là thời gian thực hiện thực tế. Nhưng một số người sử dụng các thuật ngữ này thay thế cho nhau.

Bởi vì độ phức tạp thời gian phụ thuộc vào các thông số khác nhau viz.
1. Hệ thống vật lý
2. Ngôn ngữ lập trình
3. Phong cách mã hóa
4. Và nhiều hơn nữa ......

Thời gian thực hiện thực tế không phải là một biện pháp tốt để phân tích.


Thay vào đó, chúng tôi lấy kích thước đầu vào làm tham số vì bất kể mã là gì, đầu vào đều giống nhau. Vì vậy, thời gian thực hiện là một chức năng của kích thước đầu vào.

Sau đây là một ví dụ về thuật toán thời gian tuyến tính


Tìm kiếm tuyến tính
Cho n phần tử đầu vào, để tìm kiếm một phần tử trong mảng bạn cần nhiều nhất là so sánh 'n' . Nói cách khác, cho dù bạn sử dụng ngôn ngữ lập trình nào, phong cách mã hóa nào bạn thích, trên hệ thống nào bạn thực hiện nó. Trong trường hợp xấu nhất, nó chỉ yêu cầu n so sánh. Thời gian thực hiện tỷ lệ tuyến tính với kích thước đầu vào.

Và nó không chỉ là tìm kiếm, bất cứ điều gì có thể là công việc (tăng, so sánh hoặc bất kỳ hoạt động nào) đó là một chức năng của kích thước đầu vào.

Vì vậy, khi bạn nói bất kỳ thuật toán nào là O (log n), điều đó có nghĩa là thời gian thực hiện là log lần kích thước đầu vào n.

Khi kích thước đầu vào tăng công việc được thực hiện (ở đây thời gian thực hiện) tăng. (Do đó tỷ lệ)

      n      Work
      2     1 units of work
      4     2 units of work
      8     3 units of work

Xem khi kích thước đầu vào tăng, công việc được thực hiện được tăng lên và nó độc lập với bất kỳ máy nào. Và nếu bạn cố gắng tìm ra giá trị của các đơn vị công việc thì nó thực sự phụ thuộc vào các tham số đã chỉ định ở trên. Nó sẽ thay đổi theo hệ thống và tất cả.


5

Cây

log x to base b = y là nghịch đảo của b^y = x

Nếu bạn có cây M-ary có độ sâu d và kích thước n, thì:

  • đi qua toàn bộ cây ~ O (M ^ d) = O (n)

  • Đi bộ một con đường trong cây ~ O (d) = O (log n đến cơ sở M)


5

Trong công nghệ thông tin có nghĩa là:

  f(n)=O(g(n)) If there is suitable constant C and N0 independent on N, 
  such that
  for all N>N0  "C*g(n) > f(n) > 0" is true.

Ant có vẻ như ký hiệu này chủ yếu được lấy từ toán học.

Trong bài viết này có một câu trích dẫn: DE Knuth, "BIG OMICRON VÀ BIG OMEGA VÀ BIG THETA", 1976 :

Trên cơ sở các vấn đề được thảo luận ở đây, tôi đề xuất rằng các thành viên của SIGACT, và các biên tập viên của tạp chí khoa học máy tính và toán học, áp dụng các ký hiệu như được định nghĩa ở trên, trừ khi có thể sớm tìm ra giải pháp thay thế tốt hơn .

Hôm nay là năm 2016, nhưng chúng tôi vẫn sử dụng nó cho đến ngày hôm nay.


Trong phân tích toán học có nghĩa là:

  lim (f(n)/g(n))=Constant; where n goes to +infinity

Nhưng ngay cả trong phân tích toán học đôi khi biểu tượng này đã được sử dụng với nghĩa "C * g (n)> f (n)> 0".

Như tôi biết từ trường đại học, biểu tượng đã được nhà toán học người Đức Landau (1877-1938) giới thiệu


4

Ví dụ nhị phân hoàn chỉnh là O (ln n) vì tìm kiếm trông như thế này:

1 2 3 4 5 6 7 8 9 10 11 12

Tìm kiếm 4 mang lại 3 lượt truy cập: 6, 3 rồi 4. Và log2 12 = 3, đây là một ứng dụng tốt để xem có bao nhiêu lượt truy cập khi cần.


cảm ơn vì ví dụ Nó nói rõ ràng làm thế nào thuật toán của chúng tôi có thể sử dụng thời gian logarit trong phương pháp phân chia và chinh phục.
Abc

Vì vậy, nếu một vòng lặp của n / 2, nó luôn luôn ghi (n)?
Gil Beyruth

3

Nếu bạn đang tìm kiếm một câu trả lời dựa trên trực giác, tôi muốn đưa ra hai cách hiểu cho bạn.

  1. Hãy tưởng tượng một ngọn đồi rất cao với một cơ sở rất rộng là tốt. Để lên đến đỉnh đồi, có hai cách: một là con đường dành riêng đi vòng quanh ngọn đồi, trên đỉnh: sân thượng nhỏ như chạm khắc để cắt cầu thang. Bây giờ nếu cách thứ nhất đạt được trong thời gian tuyến tính O (n), thì cách thứ hai là O (log n).

  2. Tưởng tượng một thuật toán chấp nhận một số nguyên, nlàm đầu vào và hoàn thành theo thời gian tỷ lệ thuận với nnó là O (n) hoặc theta (n) nhưng nếu nó chạy theo tỷ lệ thời gian number of digits or the number of bits in the binary representation on numberthì thuật toán sẽ chạy trong O (log n) hoặc theta (log n) thời gian.


vui lòng chỉnh sửa. có "O (n) hoặc theta (n)" trong cả hai kịch bản ...? Ngoài ra, tôi đã nghe điều này rất nhiều, kích thước so với # chữ số. Có phải chúng ta đang nói kích thước === 128 cho n = 10000000 và chữ số === 8 cho n = 10000000? Hãy làm sáng tỏ.
Cody

2

Các thuật toán trong mô hình phân chia và chinh phục có độ phức tạp O (logn). Một ví dụ ở đây, tính toán hàm năng lượng của riêng bạn,

int power(int x, unsigned int y)
{
    int temp;
    if( y == 0)
        return 1;
    temp = power(x, y/2);
    if (y%2 == 0)
        return temp*temp;
    else
        return x*temp*temp;
}

từ http://www.geekforgeek.org/write-ac-program-to-calculate-powxn/

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.