Sự khác biệt giữa độ phức tạp lý thuyết và hiệu quả thực tế


7

Nếu tôi có mã giả này:

for i=0 to n/2 do
   for j=0 to n/2 do 
       ... do anything ....

Số lần lặp là .n2/4

Sự phức tạp của chương trình này là gì? Có phải là không?O(n2)

Trực giác chính thức / không chính thức cho sự phức tạp đó là gì?

Tiếp theo, nếu tôi có mã giả khác này:

for i=0 to n do
   for j=0 to n do 
       ... do anything ....

Sự phức tạp là một lần nữa O(n2) -- đúng không?

Có sự khác biệt nào giữa hiệu quả trong thực tiễn và độ phức tạp lý thuyết trong trường hợp này không? Là một trong những nhanh hơn so với cái khác?


1
Quy tắc 3 từ Rob Pike, như được trích dẫn trong Nghệ thuật lập trình Unix : "Quy tắc 3. Các thuật toán ưa thích chậm khi nnhỏ và nthường nhỏ. Các thuật toán ưa thích có hằng số lớn. Cho đến khi bạn biết rằng nnó thường sẽ lớn, đừng 'không được ưa thích. " Điều này là do sự khác biệt giữa độ phức tạp lý thuyết và hiệu quả thực tế, được xác định rõ ràng trong các câu trả lời dưới đây.
tự đại diện

@Wildcard Điều đó có vẻ như là một quy tắc tốt. Điểm chuẩn một số thuật toán ứng cử viên sẽ còn tốt hơn.
G. Bạch

@Wildcard Trích dẫn rất thú vị. Thật thú vị khi nghĩ về sự phức tạp khi bạn thiết kế / thực hiện các quy trình nhưng tôi thường không phá vỡ chúng cho đến khi quy trình được đề cập dường như là một nút cổ chai.
Auberon

Câu trả lời:


7

Big O chính thức

O(f(n))={g(n)|c>0,n0>0,n>n0:0g(n)cf(n)}

Big O không chính thức

O(f(n)) là tập hợp các hàm phát triển chậm hơn (hoặc nhanh tương đương) so với f(n). Điều này có nghĩa là bất cứ chức năng nào bạn chọn từO(f(n)), hãy đặt tên cho cô ấy g(n)và bạn chọn một số tiền đủ lớn n, f(n) sẽ lớn hơng(n). Hay nói cách khácf(n) cuối cùng sẽ vượt qua bất kỳ chức năng nào trong O(f(n)) khi nào n phát triển lớn hơn. n là kích thước đầu vào của thuật toán của bạn.


Làm ví dụ Hãy chọnf(n)=n2.

Chúng ta có thể nói về điều đó f(n)O(n3). Lưu ý rằng, theo thời gian, chúng tôi cho phép lạm dụng ký hiệu nên hầu như mọi người đều viếtf(n)=O(n3) hiện nay.

Thông thường khi chúng tôi đánh giá các thuật toán, chúng tôi xem xét thứ tự của chúng. Bằng cách này, chúng ta có thể dự đoán như thế nào thời gian chạy của thuật toán sẽ tăng lên khi kích thước đầu vào (n) tăng.

Trong ví dụ của bạn, cả hai thuật toán đều có thứ tự O(n2). Vì vậy, thời gian chạy của chúng sẽ tăng theo cùng một cách (bậc hai) nhưntăng. Thuật toán thứ hai của bạn sẽ chậm hơn bốn lần so với thuật toán đầu tiên, nhưng đó là điều chúng tôi thường không quan tâm đến ** về mặt lý thuyết *. Các thuật toán có cùng thứ tự có thể có các yếu tố khác nhau (ctrong định nghĩa chính thức) hoặc các thuật ngữ bậc thấp khác nhau trong hàm đánh giá số bước. Nhưng thường thì đó là vì chi tiết triển khai và chúng tôi không quan tâm đến điều đó.

Nếu bạn có một thuật toán chạy trong O(log(n)) thời gian chúng ta có thể nói rằng nó sẽ nhanh hơn O(n) [1] bởi vì log(n)=O(n). Dù tệ đến đâuÔi(tôiog(n)) thuật toán được triển khai, cho dù bạn đặt thuật toán bao nhiêu tiền, nó sẽ luôn nhanh hơn [1] Ôi(n)thuật toán. Ngay cả khi số lượng các bước trong thuật toán là9999*tôiog(n)= =Ôi(tôiog(n))0,0001*n= =Ôi(n), các Ôi(tôiog(n)) thuật toán cuối cùng sẽ nhanh hơn [1].

Nhưng, có lẽ, trong ứng dụng của bạn, n luôn luôn thấp và sẽ không bao giờ đủ lớn để Ôi(tôiog(n))thuật toán sẽ nhanh hơn trong thực tế . Vì vậy, sử dụngÔi(n) thuật toán sẽ dẫn đến thời gian chạy nhanh hơn, mặc dù n= =Ôi(tôiog(n))

[1] nếu n là đủ lớn.


1
An Ôi(đăng nhậpn) không phải lúc nào cũng nhanh hơn một Ôi(n)một. Nó phụ thuộc vào giá trị củanvà trên các hằng số ẩn. Một trường hợp điển hình là nhân ma trận nhanh, không nhanh chút nào trong thực tế.
Yuval Filmus

Đó là lý do tại sao tôi thêm vào: Nếu n đủ lớn. Trong trường hợp bạn đề cập, nếu ma trận phát triển đủ lớn,Ôi(tôiog(n))SILL nhanh hơn, theo định nghĩa
Auberon

Không phải tất cả mọi thứ bạn đang nói đã có trong câu trả lời của tôi?
Auberon

7
OP hỏi về sự khác biệt giữa lý thuyết và thực hành và bạn đang bỏ qua điều đó. Ký hiệu tiệm cận thường, nhưng không phải lúc nào cũng hữu ích trong thực tế. Cải thiện thời gian chạy bốn lần có thể tạo ra một sự khác biệt lớn trong thực tế, nhưng ký hiệu tiệm cận là hoàn toàn không biết gì về nó.
Yuval Filmus

5

Auberon cung cấp một lời giải thích rất tốt của Big O . Tôi sẽ cố gắng giải thích điều đó có nghĩa gì với bạn.

Đầu tiên bạn nói đúng. Ví dụ mã đầu tiên cón24lặp đi lặp lại, nhưng vẫn còn trong lớp phức tạp O(n^2).

Tại sao?

Vấn đề của các lớp phức tạp là, chúng tôi cho rằng làm việc gì đó nhiều lần không phải là điều tồi tệ đối với thời gian chạy.

Tưởng tượng một Thuật toán có độ phức tạp O(2^n)chạy trong n=3và mất 1 giây.

Chúng tôi có thể chạy nó 10 lần và vẫn mong đợi câu trả lời sau khoảng 10 giây.

Bây giờ Hãy tưởng tượng tăng nthêm 10. Chương trình sẽ mất 2^10=1024vài giây.

Vì vậy, các nhà khoa học máy tính về cơ bản đã nói: " Con người, những nhân tố hàng đầu thật khó chịu. Chúng ta hãy giả sử n phát triển đến vô cùng và so sánh các chức năng ở đó "

Mà dẫn đến Big O .

Trong thực tế

Trong thực tế, rất có thể một giải pháp với độ phức tạp tồi tệ hơn sẽ chạy nhanh hơn nhiều (đối với các đầu vào "nhỏ"). Thật dễ dàng để tưởng tượng một Chương trình chạy trong O(n)nhưng cần 10^10*nlặp lại.

Nó là O (n) nhưng thậm chí là mộtO(2^n) giải pháp có thể nhanh hơn cho n nhỏ.

Tóm tắt:

Các Big O là một công cụ hữu ích. Nhưng bạn vẫn phải suy nghĩ về tốc độ của thuật toán trong thực tế, vì nó chỉ mô tả thời gian cần thiết hơn bao nhiêu nếu đầu vào phát triển.


3

Nếu tôi có mã giả này:

for i=0 to n/2 do
   for j=0 to n/2 do 
       ... do anything ....

Số lần lặp là n2/4.

Thật ra, nó (1+n/2)2= =n2/4+n+1.

Sự phức tạp của chương trình này là gì? ĐúngÔi(n2)?

Điều đó phụ thuộc hoàn toàn vào "làm bất cứ điều gì". Ví dụ: nếu "làm bất cứ điều gì" được thay thế bằng dòng

for k=0 to 2^n do {}

sau đó thời gian chạy là Θ(n22n).

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.