Về mặt kỹ thuật, đây có phải là thuật toán O (1) cho “Hello World” không?


117

Điều này có được phân loại là thuật toán O (1) cho "Xin chào, Thế giới!" ??

public class Hello1
{
   public static void Main()
   {
      DateTime TwentyYearsLater = new DateTime(2035,01,01);
      while ( DateTime.Now < TwentyYearsLater )
      { 
          System.Console.WriteLine("It's still not time to print the hello ...");
      }
      System.Console.WriteLine("Hello, World!");
   }
}

Tôi đang nghĩ đến việc sử dụng

DateTime TwentyYearsLater = new DateTime(2035,01,01);
while ( DateTime.Now < TwentyYearsLater )
{ 
   // ... 
}

đoạn mã như một vòng lặp bận rộn để đưa vào như một trò đùa bất cứ khi nào ai đó yêu cầu một thuật toán có độ phức tạp nhất định. Điều này có đúng không?


15
Đây O(N)không phải là sự phức tạpO(1)
Fabjan

19
@SubparWebDev Không, bạn không biết nó sẽ vượt qua vòng lặp bao nhiêu lần, ngay cả khi bạn biết sự khác biệt chính xác về thời gian giữa thời điểm bạn bắt đầu chương trình và ngày được chỉ định. Nó phụ thuộc vào tốc độ chạy của máy tính, những gì khác đang chạy trên nó, cách CPU lên lịch tác vụ, v.v.
Servy

131
@Fabjan Không có Nthuật toán phụ thuộc vào, vì vậy bạn không thể thực sự nói rằng đó là thuật toán O (N).
Phục vụ

29
Về mặt kỹ thuật không có đầu vào, vì vậy Nthậm chí không có ý nghĩa gì. Nhưng bạn có thể xem xét DateTime.Nowmột đầu vào khiến điều này vẫn phụ thuộc vào kết quả. Nếu bạn có thể giả định một giá trị thực tế cho DateTime.Now, thì có, chương trình sẽ lặp lại một lượng thời gian không đổi.
poke

43
Câu lệnh bài toán phải xác định N là gì.
Yacoub Massad

Câu trả lời:


406

Ký hiệu Big O trong ngữ cảnh này đang được sử dụng để mô tả mối quan hệ giữa kích thước của đầu vào của một hàm và số lượng các thao tác cần được thực hiện để tính toán kết quả cho đầu vào đó.

Hoạt động của bạn không có đầu vào mà đầu ra có thể liên quan đến, vì vậy việc sử dụng ký hiệu Big O là vô nghĩa. Thời gian hoạt động diễn ra độc lập với các yếu tố đầu vào của hoạt động (là ... không có). Kể từ đó không có mối quan hệ giữa đầu vào và số lượng các hoạt động thực hiện, bạn không thể sử dụng Big O để mô tả mối quan hệ không tồn tại


6
Về O(max(1, 2035 - yearTheProgramIsStarted))thì sao?
Bergi

19
@Bergi [Thực tế là không [( stackoverflow.com/questions/34048740/… ), bạn không thể chỉ mô tả số lần lặp lại của vòng lặp hoàn toàn dựa trên thời gian bạn thực hiện công việc. Và tất nhiên bạn kết hợp điều đó với thực tế là người dùng có thể thay đổi đồng hồ hệ thống bất kỳ lúc nào thành bất kỳ lúc nào họ muốn, v.v. và bạn vẫn không có đầu vào được định dạng tốt có thể liên quan chính xác đến một số các hoạt động cần thiết để tạo ra đầu ra. Heck, ngay cả bản thân đầu ra cũng không nhất quán.
Servy

23
Người ta có thể tranh luận rằng trạng thái của hệ thống (bao gồm đồng hồ) là một phần của đầu vào cho chương trình. Theo nghĩa đó, bạn có thể sử dụng ngày như một tham số đầu vào, mặc dù không rõ ràng. Nó là kỳ lạ, mặc dù.
Connor Clark

9
Để rõ ràng hơn, đầu vào 'ngầm định' là đồng bằng giữa ngày 1 tháng 1 năm 2035 và ngày hôm nay.
Connor Clark

6
@Hoten Nhưng thời gian hệ thống không phải là một giá trị cố định. Chức năng này không giống như chỉ chấp nhận DateTimethời gian bắt đầu làm đầu vào. Như tôi đã nói trước đó, đồng hồ hệ thống có thể thay đổi theo thời gian . Và một lần nữa, bạn không thể ánh xạ trực tiếp đầu vào quazi mà bạn đang mô tả với một đầu ra cố định. Không có một số lượng nhất định các hoạt động được thực hiện trong một thời gian bắt đầu nhất định hoặc thậm chí đối với hai chương trình luôn nhận giá trị hợp lý DateTime.Now, vì vậy bạn không thể liên hệ cả hai khi thời gian thay đổi, bởi vì bạn thậm chí không thể liên hệ chúng khi thời gian không thay đổi.
Servy

88

Ký hiệu Big-O có nghĩa đại khái là 'cho một phép toán trên một lượng công việc, N, bao nhiêu thời gian tính toán, tỷ lệ với N, thuật toán mất bao nhiêu thời gian?'. Ví dụ: sắp xếp một mảng có kích thước N có thể lấy N ^ 2, Nlog (N), v.v.

Điều này không có lượng dữ liệu đầu vào để thực hiện. Vì vậy, nó không phải O(anything).

Thậm chí tệ hơn; về mặt kỹ thuật đây không phải là một thuật toán. Thuật toán là một phương pháp để tính toán giá trị của một hàm toán học - các hàm toán học là một ánh xạ từ một đầu vào đến một đầu ra. Vì điều này không cần đầu vào và không trả về gì nên nó không phải là một hàm, theo nghĩa toán học. Từ wikipedia:

Thuật toán là một phương pháp hiệu quả có thể được biểu diễn trong một khoảng không gian và thời gian hữu hạn và bằng một ngôn ngữ hình thức được xác định rõ ràng để tính toán một hàm. Bắt đầu từ trạng thái ban đầu và đầu vào ban đầu (có thể trống), các lệnh mô tả một phép tính, khi được thực thi, sẽ tiến hành thông qua một số hữu hạn các trạng thái liên tiếp được xác định rõ, cuối cùng tạo ra "đầu ra" và kết thúc ở trạng thái kết thúc cuối cùng.

Về mặt kỹ thuật, đây là một hệ thống điều khiển. Từ wikipedia;

Hệ thống điều khiển là một thiết bị hoặc một tập hợp các thiết bị, quản lý, ra lệnh, chỉ đạo hoặc điều chỉnh hành vi của các thiết bị hoặc hệ thống khác.

Đối với những người muốn có câu trả lời chuyên sâu hơn về sự khác biệt giữa các hàm toán học và thuật toán cũng như khả năng mạnh mẽ hơn của máy tính để thực hiện những việc hiệu quả hơn như xuất bảng điều khiển, hiển thị đồ họa hoặc điều khiển rô bốt, hãy đọc bài báo này trên Giả thuyết mạnh về nhà thờ Turing

trừu tượng

Quan điểm cổ điển về tính toán vị trí tính toán như một phép biến đổi hộp kín của đầu vào (số hữu tỉ hoặc chuỗi hữu hạn) thành đầu ra. Theo quan điểm tương tác của máy tính, tính toán là một quá trình tương tác liên tục chứ không phải là một sự chuyển đổi dựa trên chức năng của một đầu vào thành đầu ra. Cụ thể, giao tiếp với thế giới bên ngoài xảy ra trong quá trình tính toán, không phải trước hay sau nó. Cách tiếp cận này thay đổi hoàn toàn sự hiểu biết của chúng ta về tính toán là gì và cách nó được mô hình hóa.

Việc chấp nhận tương tác như một mô hình mới bị cản trở bởi Strong Church-Turing Thesis (SCT), niềm tin phổ biến rằng Máy Turing (TM) nắm bắt tất cả các phép tính, vì vậy các mô hình tính toán biểu cảm hơn TM là không thể. Trong bài báo này, chúng tôi chỉ ra rằng SCT diễn giải lại Luận văn Church-Turing (CTT) ban đầu theo cách mà Turing không bao giờ có ý định; sự tương đương thường được giả định của nó với bản gốc là một huyền thoại. Chúng tôi xác định và phân tích các lý do lịch sử dẫn đến niềm tin rộng rãi vào thuế TTĐB. Chỉ bằng cách chấp nhận rằng điều đó là sai, chúng ta mới có thể bắt đầu chấp nhận tương tác như một mô hình tính toán thay thế


Nó không cần phải là một chuỗi. Đó chỉ là một số đầu vào dữ liệu và ký hiệu landau mô tả thời gian chạy liên quan đến một số chỉ số trên dữ liệu đó - thường là một cái gì đó liên quan đến kích thước.
Bergi

@Bergi - vâng, hãy xem quan điểm của bạn! Thực ra, chỉ là tính toán gần đúng - nếu bạn có thể đo lường khối lượng công việc phải làm và số bước cần thực hiện để đạt được điều đó, thì big-o phản ánh mối quan hệ của hai thước đo đó. Gần hơn?
Steve Cooper

@kapep - nó không phải là một hàm thuần túy vì nó là một phương thức void, nhưng nếu chúng ta đếm đầu ra của bảng điều khiển, nó vẫn là ngẫu nhiên; nó có thể xuất ra bất kỳ lệnh nào trong số {"Hello, World!", "Vẫn chưa đến lúc in lời chào ... \ nHello, World!", "Vẫn chưa đến lúc in lời chào ... Vẫn chưa đến lúc in xin chào ... \ nHello, World! ", ...}
Steve Cooper

1
In ra stdout không phải là đầu ra?
rpax

4
@rpax Không phải về mặt toán học, không. Hàm là một bản dịch không thay đổi từ đầu vào đến đầu ra; ví dụ: 'square' là hàm luôn trả về 9 nếu bạn nhập 3. Phương thức c # chỉ là một hàm toán học nếu một lệnh gọi có cùng tham số luôn cho cùng một giá trị trả về. Mặt khác - nếu nó có các tác dụng phụ như ghi vào bảng điều khiển, hiển thị đồ họa, phân bổ bộ nhớ - thì đó không phải là các hàm toán học. (Sẽ thêm một liên kết đến câu trả lời của tôi để đi sâu vào nó một cách chi tiết :))
Steve Cooper

41

Không, mã của bạn có độ phức tạp về thời gian O(2^|<DeltaTime>|),

Để có một mã hóa thích hợp của thời điểm hiện tại.
Làm ơn, cho tôi xin lỗi trước vì tiếng Anh của tôi.

Big O là gì và cách thức hoạt động của Big O trong CS

Ký hiệu Big O không được sử dụng để ràng buộc đầu vào của một chương trình với thời gian chạy của nó .
Ký hiệu Big O, để lại tính chặt chẽ, là một cách để biểu thị tỷ số tiệm cận của hai đại lượng .

Trong trường hợp phân tích thuật toán, hai đại lượng này không phải là đầu vào (mà đại lượng này trước tiên phải có chức năng "đo") và thời gian chạy.
Chúng là độ dài mã hóa của một trường hợp của vấn đề 1 và một chỉ số quan tâm.

Các chỉ số thường được sử dụng là

  1. Số bước cần thiết để hoàn thành thuật toán trong một mô hình tính toán nhất định.
  2. Không gian cần thiết, nếu có bất kỳ khái niệm nào như vậy tồn tại, bởi mô hình tính toán.

Ngầm được giả định một TM là mô hình do đó điểm đầu tiên chuyển đến số ứng dụng của quá trình chuyển đổi 2 chức năng , tức là "bước", và một trong những thứ hai chuyển số lượng tế bào băng khác nhau bằng văn bản ít nhất một lần .

Có phải người ta cũng thường mặc nhiên cho rằng chúng ta có thể sử dụng mã hóa đa thức liên quan thay vì mã hóa ban đầu, chẳng hạn như một hàm tìm kiếm một mảng từ đầu đến cuối có O(n)độ phức tạp mặc dù thực tế là mã hóa một phiên bản của mảng đó phải có độ dài là n*b+(n-1)ở đâu blà số ký hiệu (hằng số) của mỗi phần tử. Điều này là do bđược coi là hằng số của mô hình tính toán nên biểu thức trên vàn tiệm cận là giống nhau.

Điều này cũng giải thích tại sao một thuật toán như Bộ phận Thử nghiệm là một thuật toán hàm mũ mặc dù về cơ bản là một for(i=2; i<=sqr(N); i++)thuật toán giống 3 .

Xem này .

Điều này cũng có nghĩa là ký hiệu O lớn có thể sử dụng nhiều tham số mà người ta có thể cần để mô tả vấn đề, không có gì lạ khi có k tham số cho một số thuật toán.

Vì vậy, đây không phải là về "đầu vào" hay "không có đầu vào".

Nghiên cứu trường hợp ngay bây giờ

Ký hiệu Big O không đặt câu hỏi về thuật toán của bạn, nó chỉ giả định rằng bạn biết bạn đang làm gì. Về cơ bản, nó là một công cụ có thể áp dụng ở mọi nơi, ngay cả đối với thuật toán có thể cố tình phức tạp (như của bạn).

Để giải quyết vấn đề của bạn, bạn đã sử dụng ngày hiện tại và ngày trong tương lai, vì vậy bằng cách nào đó chúng phải là một phần của vấn đề; nói một cách đơn giản: chúng là một phần của ví dụ của vấn đề.

Cụ thể ví dụ là:

<DeltaTime>

Trường hợp <>có nghĩa là bất kỳ, không bệnh lý, mã hóa của sự lựa chọn.

Xem bên dưới cho rất quan trọng .

Vì vậy, thời gian phức tạp O lớn của bạn là O(2^|<DeltaTime>|)do bạn thực hiện một số lần lặp phụ thuộc vào giá trị của thời gian hiện tại. Không có ích gì khi đặt các hằng số khác vì ký hiệu tiệm cận rất hữu ích vì nó loại bỏ các hằng số (ví dụ như việc sử dụngO(10^|<DeltaTime>|*any_time_unit) là vô nghĩa).

Phần khó khăn nằm ở đâu

Chúng tôi đã đưa ra một giả định quan trọng ở trên: rằng mô hình tính toán đặc tả lại 5 thời gian, và theo thời gian, ý tôi là thời gian vật lý (thực?). Không có khái niệm này trong mô hình tính toán tiêu chuẩn, một TM không biết thời gian, chúng ta liên kết thời gian với số bước bởi vì đây là cách thực tế của chúng ta hoạt động 4 .

Trong mô hình của bạn, tuy nhiên thời gian là một phần của tính toán, bạn có thể sử dụng thuật ngữ của những người chức năng bằng cách nói rằng Main không thuần túy nhưng khái niệm thì giống nhau.

Để hiểu điều này, cần lưu ý rằng không có gì ngăn cản Framework sử dụng thời gian giả chạy nhanh hơn gấp đôi, năm, mười lần thời gian vật lý đó. Bằng cách này, mã của bạn sẽ chạy trong "một nửa", "một phần năm", "một phần mười" của "thời gian".

Sự phản ánh này rất quan trọng đối với việc chọn mã hóa <DeltaTime>, về cơ bản đây là một cách cô đọng để viết <(CurrentTime, TimeInFuture)>. Vì thời gian không tồn tại ở thời điểm ban đầu, nên mã hóa của CurrentTime rất có thể là từ Bây giờ (hoặc bất kỳ lựa chọn nào khác) ngày trước có thể được mã hóa là Ngày hôm qua , bằng cách phá vỡ giả định rằng thời lượng mã hóa tăng lên theo thời gian thực. tiếp tục (và một trong số DeltaTime giảm)

Chúng ta phải mô hình hóa thời gian đúng cách trong mô hình tính toán của mình để làm điều gì đó hữu ích.

Sự lựa chọn an toàn duy nhất mà chúng tôi có thể làm là mã hóa dấu thời gian với độ dài ngày càng tăng (nhưng vẫn không sử dụng đơn phân) khi thời gian vật lý tiến lên. Đây là thuộc tính thực sự duy nhất về thời gian mà chúng ta cần và là thuộc tính mã hóa cần nắm bắt. Có phải chỉ với loại mã hóa này, thuật toán của bạn có thể có độ phức tạp về thời gian.

Sự nhầm lẫn của bạn, nếu có, phát sinh từ thực tế là từ thời gian trong các cụm từ ' Độ phức tạp về thời gian của nó là gì?' và ' Sẽ mất bao nhiêu thời gian ?' có nghĩa là những thứ rất khác

Than ôi, thuật ngữ sử dụng những từ giống nhau, nhưng bạn có thể thử sử dụng "độ phức tạp của các bước" trong đầu và tự hỏi lại câu hỏi của mình, tôi hy vọng điều đó sẽ giúp bạn hiểu câu trả lời thực sự là gì ^ _ ^


1 Điều này cũng giải thích sự cần thiết của phương pháp tiệm cận vì mỗi trường hợp có độ dài khác nhau, nhưng không tùy ý.
2 Tôi hy vọng tôi đang sử dụng thuật ngữ tiếng Anh chính xác ở đây.
3 Ngoài ra, đây là lý do tại sao chúng ta thường tìm log(log(n))các số hạng trong toán học.
4 Id est, một bước phải chiếm một khoảng thời gian hữu hạn, nhưng không rỗng, cũng không liên kết với nhau.
5 Điều này có nghĩa là chế độ tính toán như một kiến ​​thức về thời gian vật lý trong nó, có thể diễn đạt nó bằng các thuật ngữ của nó. Tương tự là cách các generic hoạt động trong .NET framework.


3
"Vì vậy, thời gian chạy O lớn của bạn chỉ là" .. Tôi chắc chắn rằng bạn có nghĩa là 'độ phức tạp O lớn' ?. Ngoài ra, chúng ta vẫn có thể gọi 'deltaTime' là đúng 'n' của chúng ta .. vì vậy câu nói O (2 ^ N) của bạn giống như độ phức tạp của thuật toán Fibonacci. Làm thế nào bạn đến với "2 ^"?
Ross

@Ross, cảm ơn vì điểm này. Tôi đến với 2 thói quen làm việc với số nhị phân. Vấn đề là các bước là tuyến tính với độ dài của biểu diễn số. Cơ sở thực tế không thực sự quan trọng và thay đổi dựa trên mã hóa cụ thể. Nó là tuyến tính giả .
Yuni Mj

Tôi xin lỗi, nhưng bạn có thể vui lòng giải thích thêm trong câu trả lời của mình như thế nào bạn kết luận rằng độ phức tạp O(2^n)không? Nó không rõ ràng cho người mới bắt đầu.
Arturo Torres Sánchez

2
@YuniMj Trong khi lập luận của bạn là về mặt kỹ thuật không sai, tôi nghĩ rằng bằng cách khẳng định để đo kích thước của DeltaTimethay vì nó giá trị , bạn chỉ cần thêm sự nhầm lẫn bổ sung. Ví dụ: nhưng suy luận không có thuật toán sắp xếp tối ưu có độ phức tạp về thời gian $ O (n \ cdot log n) $. Tại sao? Bởi vì bạn hoặc bạn chỉ có rất nhiều đối tượng có thể phân biệt được để sắp xếp, trong trường hợp đó, bạn luôn có thể sử dụng sắp xếp theo nhóm để sắp xếp trong $ O (n) $. Hoặc kích thước đối tượng của bạn là không giới hạn, trong trường hợp $ O (n \ cdot log n) $ sẽ không giữ, vì một sự so sánh đơn lẻ sẽ không có thời gian thường xuyên nữa ...
FGP

1
FWIW O (2 ^ n)! = O (10 ^ n) stackoverflow.com/questions/19081673/…
Nathan FD

29

Mặc dù có rất nhiều câu trả lời tuyệt vời ở đây, nhưng hãy để tôi diễn đạt lại tất cả chúng một chút.

Kí hiệu Big-O tồn tại để mô tả các chức năng . Khi được áp dụng để phân tích các thuật toán, điều này trước tiên đòi hỏi chúng ta phải xác định một số đặc điểm của thuật toán này dưới dạng một hàm . Sự lựa chọn phổ biến là coi số bước là một hàm của kích thước đầu vào . Như đã lưu ý trong các câu trả lời khác, việc đưa ra hàm như vậy trong trường hợp của bạn có vẻ lạ, vì không có "đầu vào" được xác định rõ ràng. Tuy nhiên, chúng tôi vẫn có thể cố gắng làm điều đó:

  • Chúng tôi có thể coi thuật toán của bạn là một hàm hằng lấy bất kỳ đầu vào nào có kích thước bất kỳ, bỏ qua nó, đợi một khoảng thời gian cố định và kết thúc. Trong trường hợp này, thời gian chạy của nó là f (n) = const , và nó là một thuật toán thời gian O (1). Đây là những gì bạn mong đợi được nghe, phải không? Vâng, về mặt kỹ thuật, nó là một thuật toán O (1) .
  • Chúng ta có thể coi TwentyYearsLatertham số giống như "kích thước đầu vào" được quan tâm. Trong trường hợp này thời gian chạy là f (n) = (nx) trong đó x là "bây giờ" tại thời điểm gọi. Khi nhìn theo cách này, nó là một thuật toán thời gian O (n). Mong đợi phản đối này bất cứ khi nào bạn hiển thị thuật toán O (1) về mặt kỹ thuật của mình cho người khác.
  • Ồ, nhưng chờ đã, nếu k =TwentyYearsLater là đầu vào, thì kích thước n của nó thực sự là số bit cần thiết để biểu diễn nó, tức là n = log (k) . Do đó, sự phụ thuộc giữa kích thước của đầu vào n và thời gian chạy là f (n) = 2 ^ n - x . Có vẻ như thuật toán của bạn vừa trở nên chậm chạp theo cấp số nhân! Ặc.
  • Một đầu vào khác cho chương trình trên thực tế là dòng câu trả lời được đưa ra bởi Hệ điều hành cho chuỗi các lệnh DateTime.Nowgọi trong vòng lặp. Chúng ta thực sự có thể tưởng tượng rằng toàn bộ chuỗi này được cung cấp làm đầu vào tại thời điểm chúng ta chạy chương trình. Sau đó, thời gian chạy có thể được coi là phụ thuộc vào thuộc tính của chuỗi này - cụ thể là độ dài của nó cho đến TwentyYearsLaterphần tử đầu tiên . Trong trường hợp này, thời gian chạy lại là f (n) = n và thuật toán là O (n) .

Nhưng một lần nữa, trong câu hỏi của bạn, bạn thậm chí không nói rằng bạn quan tâm đến thời gian chạy. Nếu bạn muốn sử dụng bộ nhớ thì sao? Tùy thuộc vào cách bạn mô hình hóa tình huống, bạn có thể nói thuật toán là O (1) -memory hoặc, có lẽ, O (n) -memory (nếu việc triển khai DateTime.Nowyêu cầu theo dõi toàn bộ chuỗi lệnh gọi nào đó).

Và nếu mục tiêu của bạn là đưa ra một điều gì đó vô lý, tại sao bạn không đi vào và nói rằng bạn quan tâm đến kích thước của mã thuật toán tính bằng pixel trên màn hình phụ thuộc vào mức thu phóng đã chọn như thế nào. Đây có thể là một cái gì đó giống như f (zoom) = 1 / zoom và bạn có thể tự hào tuyên bố thuật toán của mình là kích thước O (1 / n) -pixel!


+1. Tôi tin rằng "luồng câu trả lời được đưa ra bởi Hệ điều hành cho chuỗi DateTime.Nowlời gọi` là đầu vào thực sự ở đây. Nhưng tôi nghĩ kết luận không nên là O (n), mà là O (k), trong đó k là độ dài cho đến TwentyYearsLaterphần tử đầu tiên .
trước

7
Đây là câu trả lời tốt nhất cho đến nay - để Big O có ý nghĩa, bạn phải áp dụng ngữ nghĩa / giả định toán học vào việc triển khai vật lý (về cơ bản là xác định mô hình toán học cho chương trình với định nghĩa có nghĩa là "đầu vào"). Theo nghĩa này, độ phức tạp của "chương trình" phụ thuộc vào ngữ nghĩa mà bạn áp dụng - nếu bạn giả định rằng N là chênh lệch thời gian có tỷ lệ tuyến tính với số lượng hoạt động, thì đó là O (n). Nếu bạn giả sử một số lượng hoạt động cố định là kết quả của một khoảng thời gian cố định, thì đó là O (1).
Ant P

21

Tôi hơi không đồng ý với Servy. Có một đầu vào cho chương trình này, ngay cả khi nó không rõ ràng, và đó là thời gian của hệ thống. Đây có thể là một kỹ thuật mà bạn không dự định, nhưng TwentyYearsFromNowbiến của bạn không phải là hai mươi năm so với thời điểm của hệ thống bây giờ , nó được gán tĩnh vào ngày 1 tháng 1 năm 2035.

Vì vậy, nếu bạn lấy mã này và thực thi nó trên một máy có thời gian hệ thống là ngày 1 tháng 1 năm 1970, thì sẽ mất 65 năm để hoàn thành, bất kể máy tính có nhanh như thế nào (có thể có một số biến thể nếu đồng hồ của nó bị lỗi ). Nếu bạn lấy mã này và thực thi nó trên máy có thời gian hệ thống là ngày 2 tháng 1 năm 2035, nó sẽ hoàn tất gần như ngay lập tức.

Tôi sẽ nói rằng đầu vào của bạn n, làJanuary 1st, 2035 - DateTime.Now , và đó là O (n).

Sau đó, cũng có vấn đề về số lượng hoạt động. Một số người đã lưu ý rằng máy tính nhanh hơn sẽ chạy vòng lặp nhanh hơn, gây ra nhiều hoạt động hơn, nhưng điều đó không liên quan. Khi làm việc với ký hiệu big-O, chúng tôi không xem xét tốc độ của bộ xử lý hoặc số lượng hoạt động chính xác. Nếu bạn sử dụng thuật toán này và chạy trên máy tính, sau đó chạy lại nhưng lâu hơn 10 lần trên cùng một máy tính, bạn sẽ mong đợi số lượng hoạt động tăng theo cùng một hệ số là 10 lần.

Đối với điều này:

Tôi đang nghĩ đến việc sử dụng đoạn mã [redacted code] như một vòng lặp bận rộn để đưa vào như một trò đùa bất cứ khi nào ai đó yêu cầu một thuật toán có độ phức tạp nhất định. Điều này có đúng không?

Không thật sự lắm. Các câu trả lời khác đã đề cập đến vấn đề này, vì vậy tôi chỉ muốn đề cập đến nó. Nhìn chung, bạn không thể so sánh số năm thực thi với bất kỳ ký hiệu big-O nào. Ví dụ. Không có cách nào để nói 20 năm thực hiện = O (n ^ 87) hay bất cứ điều gì khác cho vấn đề đó. Ngay cả trong thuật toán bạn đã đưa ra, tôi có thể thay đổi TwentyYearsFromNownăm 20110, 75699436 hoặc 123456789 và big-O vẫn là O (n).


7
Thời gian không phải là đầu vào cho hàm, nó liên tục thay đổi trạng thái được quan sát trong suốt quá trình thực thi phương thức. Đồng hồ hệ thống thậm chí có thể được thay đổi trong khi chức năng đang chạy . Để Big O có ý nghĩa, bạn cũng cần phải có mỗi đầu vào tương ứng 1-1 với một giá trị đầu ra, cũng như một số thao tác cần thiết để tính toán nó. Đối với hoạt động này đầu ra là thậm chí không phù hợp cho các đầu vào tương tự (trong thực tế nó thay đổi một cách hoang dại ), ngoài số lượng các hoạt động thực hiện cũng khác nhau một cách hoang dại.
Bữa

When working with big-O notation, we don't consider the speed of the processor or the exact number of operations.Đây là một tuyên bố sai. Khá nhiều hoạt động hợp lý mà bạn cố gắng tính toán giá trị Big O sẽ không thay đổi số lượng hoạt động được thực hiện dựa trên phần cứng, nhưng điều này thì có . Big O chỉ là một cách liên hệ số lượng hoạt động với kích thước của đầu vào. Đối với hầu hết các hoạt động độc lập với phần cứng hệ thống. Trong trường hợp này thì không .
Servy

If you took this algorithm and ran it on a computer, and then ran it again but for 10x longer on the same computer, you would expect the number of operations to grow by the same factor of 10x.Đó cũng là một nhận định sai lầm. Môi trường không nhất thiết phải thay đổi số lượng hoạt động trong vòng lặp một cách tuyến tính. Chẳng hạn, có thể có các chương trình khác trên máy tính sử dụng nhiều hơn hoặc ít hơn thời gian CPU tại các thời điểm khác nhau, thay đổi thời gian liên tục cho ứng dụng này theo thời gian.
Servy

Tôi với @Servy về vấn đề này, nhưng vì một lý do hơi khác. Hàm main không nhận tham số và không trả về đầu vào. Đó là một hàm của nil => nil, nếu bạn thích. Không quan trọng thời gian là gì, nó vẫn không trả lại gì.
Steve Cooper

1
Nếu chúng ta đang sử dụng định nghĩa này - "Trong toán học, một hàm là mối quan hệ giữa một tập hợp các đầu vào và một tập hợp các đầu ra được phép với thuộc tính mà mỗi đầu vào liên quan đến chính xác một đầu ra." (wikipedia) - và chúng tôi đang tính đầu ra của bảng điều khiển là 'đầu ra của hàm', điều này thay đổi, sẽ lâu hơn trên máy tính nhanh hơn, vì nó sẽ viết "" Vẫn chưa đến lúc in lời chào ... " thương xuyên hơn.
Steve Cooper

13

Phân tích Big-O đề cập đến số lượng xử lý liên quan khi lượng dữ liệu được xử lý tăng lên mà không có giới hạn.

Ở đây, bạn thực sự chỉ xử lý một đối tượng duy nhất có kích thước cố định. Do đó, việc áp dụng phân tích big-O phụ thuộc rất nhiều (chủ yếu?) Vào cách bạn xác định các điều khoản của mình.

Ví dụ, bạn có thể có nghĩa là in đầu ra nói chung và áp đặt một thời gian chờ đợi quá lâu để bất kỳ lượng dữ liệu hợp lý nào sẽ / sẽ được in chính xác trong cùng một khoảng thời gian. Bạn cũng phải bổ sung thêm một chút theo cách định nghĩa hơi khác thường (nếu không muốn nói là hoàn toàn sai) để đi được rất xa - đặc biệt, phân tích big-O thường là được định nghĩa theo số lượng các thao tác cơ bản cần thiết để thực hiện nhiệm vụ cụ thể (nhưng lưu ý rằng độ phức tạp cũng có thể được xem xét về những thứ như sử dụng bộ nhớ, không chỉ sử dụng CPU / hoạt động được thực hiện).

Tuy nhiên, số lượng các hoạt động cơ bản thường chuyển dịch khá chặt chẽ với thời gian thực hiện, vì vậy nó không phải là một khoảng lớn khi coi cả hai là đồng nghĩa. Tuy nhiên, thật không may, chúng tôi vẫn còn mắc kẹt với phần khác: số lượng dữ liệu đang được xử lý ngày càng tăng mà không có giới hạn. Trong trường hợp đó, không có sự chậm trễ cố định nào mà bạn có thể áp đặt sẽ thực sự hiệu quả. Để cân bằng O (1) với O (N), bạn phải áp dụng độ trễ vô hạn để bất kỳ lượng dữ liệu cố định nào sẽ mất vĩnh viễn để in, giống như lượng dữ liệu vô hạn.


10

big-O liên quan đến cái gì?

Bạn dường như đang hiểu rằng đó twentyYearsLaterlà một "đầu vào". Nếu thực sự bạn đã viết hàm của mình là

void helloWorld(int years) {
   // ...
}

Nó sẽ là O (N) trong đó N = năm (hoặc chỉ nói O(years) ).

Tôi sẽ nói thuật toán của bạn là O (N) so với bất kỳ số nào bạn tình cờ viết trong dòng mã bắt đầu bằng twentyYearsLater =. Nhưng mọi người thường không coi các số trong mã nguồn thực tế là đầu vào. Họ có thể coi đầu vào dòng lệnh là đầu vào hoặc đầu vào chữ ký hàm là đầu vào, nhưng, rất có thể không phải chính mã nguồn. Đó là những gì bạn đang tranh chấp với bạn của bạn - đây có phải là "đầu vào"? Bạn thiết lập mã của mình theo cách để làm cho nó trông giống như một đầu vào một cách trực quan và bạn chắc chắn có thể hỏi thời gian chạy O lớn của nó đối với số N trên dòng 6 của chương trình của bạn, nhưng nếu bạn sử dụng lựa chọn không mặc định như vậy là đầu vào, bạn thực sự cần phải rõ ràng về nó.

Nhưng nếu bạn coi đầu vào là một cái gì đó bình thường hơn, như dòng lệnh hoặc đầu vào cho hàm, sẽ không có đầu ra nào cả và hàm là O (1). Phải mất hai mươi năm, nhưng vì big-O không thay đổi đến bội số không đổi nên O (1) = O (hai mươi năm).

Câu hỏi tương tự - thời gian chạy của:

void sortArrayOfSizeTenMillion(int[] array)

Giả sử nó thực hiện những gì nó nói và đầu vào là hợp lệ, và thuật toán khai thác sắp xếp nhanh hoặc bong bóng hoặc bất cứ điều gì hợp lý, đó là O (1).


Mã hóa cứng đầu vào không có nghĩa là đầu vào biến mất. Cũng không phải là nhanh và bong bóng phức tạp thời gian O (1) trong bất kỳ trường hợp nào. bigocheatsheet.com
Theo Brinkman

@TheoBrinkman Nếu bạn muốn trở thành kỹ thuật, trong mô hình máy Turing, hãy mã hóa những gì bạn nghĩ về đầu vào, vào chính máy Turing, theo định nghĩa, nó sẽ biến nó thành đầu vào. Máy Turing sau đó sẽ chạy trong một thời gian không đổi, không phụ thuộc vào bất kỳ đầu vào thực tế nào mà nó có. Theo một nghĩa nào đó, nó không chạy "sắp xếp bong bóng" vì nó không sắp xếp bất cứ thứ gì mà là hoạt động trên biểu diễn của chính nó, tuy nhiên, về mặt phi kỹ thuật, tất nhiên bạn có thể mô tả thuật toán như một loại bong bóng.
djechlin

Trong 'thuật ngữ phi kỹ thuật' tương tự, bạn có thể mô tả thuật toán được đề cập như một cây cầu treo.
Theo Brinkman

@TheoBrinkman không, bạn không thể. Điều đó sẽ không có ý nghĩa đối với bất kỳ ai.
djechlin

Nó có ý nghĩa giống như mô tả nó như một loại bong bóng O (1).
Theo Brinkman

8

"Thuật toán" này được mô tả chính xác là O (1) hoặc thời gian không đổi. Người ta lập luận rằng không có đầu vào cho chương trình này, do đó không có N để phân tích về Big Oh. Tôi không đồng ý rằng không có đầu vào. Khi điều này được biên dịch thành tệp thực thi và được gọi, người dùng có thể chỉ định bất kỳ đầu vào nào có độ dài tùy ý. Độ dài đầu vào đó là N.

Chương trình chỉ bỏ qua đầu vào (có độ dài bất kỳ), do đó thời gian thực hiện (hoặc số lượng lệnh máy được thực thi) là như nhau bất kể độ dài của đầu vào (môi trường cố định đã cho = thời gian bắt đầu + phần cứng), do đó O (1 ).


Nhưng số lượng hoạt động không nhất thiết phải nhất quán, ngay cả với cùng thời gian bắt đầu và phần cứng. Trên hết, để xác nhận một thuật toán O (1), kết quả đầu ra sẽ phải luôn không đổi và không phải vậy, nó sẽ thay đổi rất nhiều dựa trên thời gian bắt đầu và phần cứng. Nó cũng có thể rất dễ dàng là vô hạn, mà chắc chắn không phải là bất biến. Không có mối quan hệ nào giữa đầu vào bạn đã xác định và số lượng hoạt động được thực hiện. Đó không phải là hằng số, đó chỉ là không xác định. Bạn không thể đặt tên cho một số hữu hạn và biết rằng sẽ luôn có ít phép toán hơn thế.
Phục vụ

Thời gian thực tối đa mà nó sẽ mất là 20 năm. Nếu chúng tôi bắt đầu nó trong tương lai, có, nó sẽ mất nhiều thời gian hơn. Giả sử rằng có một giới hạn dưới hữu hạn về lượng thời gian lặp lại vòng lặp và chúng ta đang chạy trên phần cứng nối tiếp. Sau đó, tôi có thể ràng buộc số lần vòng lặp sẽ chạy, có nghĩa là toàn bộ tính toán có thể được giới hạn bởi một hàm hằng, bất kể kích thước của đầu vào bị bỏ qua.
waldol1

Let's suppose that there is a finite lower bound on the amount of time a loop iteration takesĐó là một giả định sai lầm. Chương trình có thể chạy mãi mãi. Tất cả những gì tôi phải làm là đặt đồng hồ hệ thống của mình thành 50 năm kể từ bây giờ, khởi động nó và nó sẽ không bao giờ kết thúc. Hoặc tôi có thể tiếp tục di chuyển đồng hồ trở lại nhanh hơn đồng hồ tiến lên hoặc bắt đầu đồng hồ ở một điểm không xác định trong quá khứ . Bạn chỉ đơn giản là không thể giả định rằng có một giới hạn thấp hơn về thời gian chương trình chạy; nó có thể chạy mãi mãi. Tuy nhiên, ngay cả khi chúng tôi coi giả định (sai) của bạn là đúng, bạn vẫn không thể liên hệ số lượng hoạt động được thực hiện với đầu vào.
Phục vụ

Một lần lặp lại vòng lặp mất một khoảng thời gian hữu hạn. Nó có thể thực hiện vô số lần, nhưng mỗi lần phải gần như không đổi. Tôi không thấy có vấn đề gì với giả định đó.
waldol1

Theo logic [hoàn toàn không chính xác] đó, mọi thuật toán đều luôn O (1) vì mọi hoạt động riêng lẻ luôn không đổi. Bạn chỉ đơn giản là chứng minh rằng bạn không biết Big O là gì. Đó là một công cụ để (trong ngữ cảnh) mô tả mối quan hệ giữa kích thước của đầu vào với số lượng các thao tác liên quan được thực hiện. O (1) có nghĩa là có một số lượng không đổi các hoạt động được thực hiện bất kể đầu vào. Ở đây có không phải là một hằng số của hoạt động thực hiện không phụ thuộc vào đầu vào, có khả năng hoạt động vô hạn thực hiện, vô hạn! = Không đổi.
Phục vụ

6

Một điều tôi ngạc nhiên vẫn chưa được đề cập: ký hiệu big-O là một giới hạn trên!

Vấn đề mà mọi người đều nhận thấy là không có N nào mô tả các đầu vào cho thuật toán, vì vậy không có gì để thực hiện phân tích big-O. Tuy nhiên, điều này có thể dễ dàng được giảm thiểu với một số thủ thuật cơ bản, chẳng hạn như chấp nhận int nvà in nthời gian "Hello World" . Điều đó sẽ giải quyết được lời phàn nàn đó và quay trở lại câu hỏi thực sự là làm thế nàoDateTime hoạt động của sự quái dị .

Không có gì đảm bảo thực tế rằng vòng lặp while sẽ kết thúc. Chúng tôi nghĩ rằng nó phải vào một lúc nào đó, nhưng hãy cân nhắc điều đó DateTime.nowtrả về ngày và giờ hệ thống . Thực tế không có gì đảm bảo rằng điều này đang tăng lên một cách đơn điệu. Có thể có một con khỉ được huấn luyện bệnh lý nào đó liên tục thay đổi ngày và giờ hệ thống trở lại ngày 21 tháng 10 năm 2015 12:00:00 UTC cho đến khi ai đó đưa cho con khỉ một số đôi giày tự động lắp và một ván trượt. Vòng lặp này thực sự có thể chạy trong một khoảng thời gian vô hạn!

Khi bạn thực sự tìm hiểu định nghĩa toán học của các ký hiệu big-O, chúng là giới hạn trên. Họ chứng minh tình huống xấu nhất, bất kể khả năng xảy ra như thế nào. Trường hợp xấu nhất * ở đây là thời gian chạy vô hạn, vì vậy chúng tôi buộc phải tuyên bố rằng không có ký hiệu big-O nào để mô tả độ phức tạp thời gian chạy của thuật toán này. Nó không tồn tại, cũng như 1/0 không tồn tại.

* Chỉnh sửa: từ cuộc thảo luận của tôi với KT, không phải lúc nào cũng hợp lệ khi cho rằng tình huống chúng ta đang lập mô hình với ký hiệu big-O là trường hợp xấu nhất. Trong hầu hết các trường hợp, nếu một cá nhân không xác định được trường hợp nào chúng tôi đang sử dụng, họ định khám phá trường hợp xấu nhất. Tuy nhiên, bạn có thể thực hiện phân tích độ phức tạp big-O trong thời gian chạy trường hợp tốt nhất.


2
O, theo một nghĩa nào đó, thực sự là một "giới hạn trên", nhưng nó không có nghĩa là bạn chỉ có thể nói về "độ phức tạp trong trường hợp xấu nhất" bằng cách sử dụng ký hiệu O. Độ phức tạp mong đợi, độ phức tạp trong trường hợp tốt nhất, bất kỳ thuộc tính chức năng nào khác - tất cả chúng đều có thể được thảo luận theo giới hạn O của chúng.
KT.

Độ phức tạp trường hợp tốt nhất của @KY được gọi là little-o và độ phức tạp dự kiến ​​là lớn. big-o luôn là trường hợp phức tạp nhất, theo định nghĩa toán học của nó.
Cort Ammon

Không, bạn nhầm ở đây. Kiểm tra lại các định nghĩa.
KT.

@KT Được rồi, tôi sẽ kiểm tra lại chúng. Bạn cũng kiểm tra lại chúng. vi.wikipedia.org/wiki/Big_O_notation Các ký hiệu thuộc Gia đình Bachmann – Landau
Cort Ammon

Tôi cho rằng bạn có thể làm điều gì đó điên rồ như lấy một hàm fvà khai báo hàm ggiống như f, nhưng với miền bị hạn chế để chỉ bao gồm ftrường hợp tốt nhất và sau đó làm lớn g, nhưng nó bắt đầu suy giảm khi bạn làm cái đó.
Cort Ammon

5

Độ phức tạp được sử dụng để đo "mã lực" tính toán về thời gian / không gian. Ký hiệu Big O được sử dụng để so sánh vấn đề nào là "có thể tính toán được" hoặc "không thể tính toán được" và cũng để so sánh các giải pháp - thuật toán- tốt hơn các giải pháp khác. Như vậy, bạn có thể chia bất kỳ thuật toán nào thành hai loại: những thuật toán có thể giải được trong thời gian đa thức và những thuật toán không thể.

Các bài toán như Sàng Erathostene là O (n ^ exp) và do đó có thể giải được đối với các giá trị nhỏ của n. Chúng có thể tính toán được, chỉ không tính theo thời gian đa thức (NP) và do đó khi được hỏi liệu một số đã cho có phải là số nguyên tố hay không, câu trả lời phụ thuộc vào độ lớn của số đó. Hơn nữa, độ phức tạp không phụ thuộc vào phần cứng, vì vậy việc có máy tính nhanh hơn không có gì thay đổi ...

Hello World không phải là một thuật toán và như vậy là vô nghĩa để cố gắng xác định độ phức tạp của nó - mà là không có. Một thuật toán đơn giản có thể là một cái gì đó như: cho một số ngẫu nhiên, xác định xem nó là chẵn hay lẻ. Bây giờ, nó có quan trọng là số đã cho có 500 chữ số không? Không, vì bạn chỉ cần kiểm tra xem chữ số cuối cùng là chẵn hay lẻ. Một thuật toán phức tạp hơn sẽ là xác định xem một số nhất định có chia đều cho 3. Mặc dù một số số "dễ tính", những số khác lại "khó" và điều này là do độ lớn của nó: so sánh thời gian cần thiết để xác định phần dư giữa một số có một chữ số và một số khác có 500 chữ số.

Một trường hợp phức tạp hơn sẽ là giải mã một văn bản. Bạn có một dãy ký hiệu ngẫu nhiên rõ ràng mà bạn cũng biết đang chuyển tải một thông điệp cho những người có khóa giải mã. Giả sử rằng người gửi đã sử dụng phím ở bên trái và Hello World của bạn sẽ đọc: Gwkki Qieks. Giải pháp "búa lớn, không cần não" sẽ tạo ra tất cả các kết hợp cho các chữ cái đó: từ Aaaa đến Zzzz và sau đó tìm kiếm từ điển từ để xác định từ nào hợp lệ và chia sẻ hai chữ cái phổ biến trong cypher (i, k) trong cùng một vị trí. Hàm biến đổi này là những gì Big O đo lường!


4

Hầu hết mọi người dường như đang thiếu hai thứ rất quan trọng.

  1. Chương trình này không có một đầu vào. Đây là ngày / giờ được mã hóa cứng để so sánh với thời gian của hệ thống. Các đầu vào nằm dưới sự kiểm soát của người chạy thuật toán, còn thời gian của hệ thống thì không. Điều duy nhất mà người chạy chương trình này có thể kiểm soát là ngày / giờ họ đã mã hóa cứng vào so sánh.

  2. Chương trình thay đổi dựa trên giá trị đầu vào , nhưng không phải kích thước của tập hợp đầu vào , đó là điều mà ký hiệu big-O quan tâm.

Do đó, nó là không xác định và ký hiệu 'big-O' tốt nhất cho chương trình này có thể là O (null), hoặc có thể là O (NaN).


1
(2) là căn hộ sai. Thông thường "độ dài của đầu vào" được xem xét. Đối với một danh sách hoặc mảng các đối tượng có kích thước cố định (như số nguyên), nó thực sự sẽ là kích thước của tập hợp. Nhân tố một số như 1395195191600333, nó sẽ là độ dài của biểu diễn nhị phân (hoặc thập phân, v.v.) của nó tức là số chữ số. Như đã nêu định nghĩa của bạn trong (2) cấm sử dụng big-O để thảo luận về độ phức tạp của "findPrimeFactors (int num)", điều mà hầu hết mọi nhà mật mã sẽ phản đối.
djechlin

4

Mọi người đều chỉ ra một cách chính xác rằng bạn không định nghĩa N , nhưng câu trả lời là không theo cách giải thích hợp lý nhất. Nếu N là độ dài của chuỗi chúng ta đang in và "xin chào, thế giới!" chỉ là một ví dụ, vì chúng tôi có thể suy ra từ mô tả về điều này như một thuật toán “cho” hello, world!, sau đó thuật toán là O ( N ), bởi vì bạn có thể có một chuỗi đầu ra mất ba mươi, bốn mươi hoặc năm mươi năm để in và bạn chỉ thêm một thời gian không đổi vào đó. O ( kN + c ) ∈ O ( N ).

Phụ lục:

Tôi ngạc nhiên, ai đó đang tranh chấp điều này. Nhắc lại các định nghĩa về lớn O và lớn Θ. Giả sử chúng ta có một thuật toán chờ một khoảng thời gian không đổi c và sau đó in ra một thông báo có độ dài N theo thời gian tuyến tính. (Đây là tổng quát của mẫu mã gốc.) Hãy tùy tiện nói rằng chúng ta đợi hai mươi năm để bắt đầu in, và việc in một nghìn tỷ ký tự phải mất thêm hai mươi năm nữa. Hãy c = 20 và k = 10¹², ví dụ, nhưng bất kỳ số thực dương sẽ làm. Đó là tỷ lệ d = c / k (trong trường hợp này là 2 × 10⁻¹¹) năm cho mỗi ký tự, vì vậy thời gian thực hiện của chúng tôi f ( dN + N ) là tiệm cận cnhiều năm. Bất cứ khi nào N > k , dN = c / k N > c . Do đó, dN < dN + c = f ( N ) <2 dN với mọi N > k , và f ( N ) ∈ Θ ( N ). QED


Nơi chúng tôi có N = 13.
djechlin

Nhưng nó không chỉ in "Hello world", nó còn in một số dòng "Vẫn chưa đến lúc". Ngoài ra, Big O không thực sự được sử dụng để so sánh kích thước của đầu vào với kích thước của đầu ra, nó thường được sử dụng để so sánh kích thước của đầu vào với số lượng hoạt động hoặc dung lượng bộ nhớ được sử dụng.
Servy

@Servy Đó là bộ nhớ không đổi, nhưng tôi đã ngầm giới hạn thời gian thực thi. Kích thước của đầu ra cũng là O ( N ), đối với một chuỗi tùy ý: chuỗi mà chúng tôi in khi đến lúc có thể lớn tùy ý, ngay cả khi so sánh với thông báo vui lòng đợi có giá trị trong hai mươi năm.
Davislor

@Servy Tôi đã chỉnh sửa để làm rõ điều đó, không, N ở đây không phải là kích thước của đầu ra. Tôi không chắc mình đã tạo ấn tượng đó như thế nào, nhưng tôi sẽ xóa mọi sự mơ hồ.
Davislor

1
Vì vậy, nếu bạn giả sử chương trình nhận một đầu vào, khi nó không, đầu ra có thể lớn tùy ý, khi nó không thể, vòng lặp không thực hiện bất cứ điều gì, khi nó làm và đầu ra liên quan đến đầu vào, khi không, thì có, chương trình là tuyến tính. Tất nhiên, mọi giả định trong số đó đều hoàn toàn sai, vì vậy kết luận bạn rút ra từ những giả định đó không đúng. Nếu bạn có thể chứng minh quan điểm của mình mà không đưa ra các giả định sai, thì điều đó có nghĩa.
Phục vụ

4

Tôi nghĩ rằng mọi người đang bị bỏ rơi vì mã trông không giống như một thuật toán truyền thống. Đây là bản dịch của đoạn mã được hình thành tốt hơn, nhưng vẫn đúng với tinh thần câu hỏi của OP.

void TrolloWorld(long currentUnixTime, long loopsPerMs){
    long laterUnixTime = 2051222400000;  //unix time of 01/01/2035, 00:00:00
    long numLoops = (laterUnixTime-currentUnixTime)*loopsPerMs;

    for (long i=0; i<numLoops; i++){
        print ("It's still not time to print the hello …");
    }
    print("Hello, World!");
}

Các đầu vào là rõ ràng trong khi trước khi chúng được đưa ra một cách ngầm định bởi thời điểm mã được bắt đầu và tốc độ của phần cứng chạy mã. Mã là xác định và có đầu ra được xác định rõ ràng cho các đầu vào nhất định.

Do các giới hạn được áp đặt cho các đầu vào mà chúng tôi có thể cung cấp, có một giới hạn trên đối với số lượng hoạt động sẽ được thực hiện, vì vậy thuật toán này trên thực tế là O (1).


2

Tại thời điểm này, có

Thuật toán này có một đầu vào ngầm định, cụ thể là thời gian chương trình được bắt đầu. Thời gian thực hiện sẽ thay đổi tuyến tính 1 tùy thuộc vào thời điểm nó được bắt đầu. Trong suốt năm 2035 trở về sau, vòng lặp while ngay lập tức thoát ra và chương trình kết thúc sau các hoạt động liên tục 2 . Vì vậy, có thể nói rằng thời gian chạy là O(max(2035 - start year, 1))3 . Nhưng vì năm bắt đầu của chúng ta có giá trị nhỏ nhất, nên thuật toán sẽ không bao giờ mất hơn 20 năm để thực thi (tức là một giá trị không đổi).

Bạn có thể làm cho thuật toán của mình phù hợp hơn với ý định của mình bằng cách xác định DateTime TwentyYearsLater = DateTime.Now + new TimeSpan(365*20,0,0,0);4

1 Điều này mang lại cảm giác kỹ thuật hơn về thời gian thực hiện được đo bằng số lượng hoạt động vì có số lượng tối đa hoạt động trên một đơn vị thời gian.
2 Giả sử tìm nạp DateTime.Nowlà một hoạt động liên tục, điều này là hợp lý.
3 Tôi hơi lạm dụng ký hiệu O lớn ở đây bởi vì đây là một chức năng giảm dần đối với start year, nhưng chúng tôi có thể dễ dàng khắc phục điều này bằng cách diễn đạt nó dưới dạng years prior to 2035.
4 Sau đó, thuật toán không còn phụ thuộc vào đầu vào ngầm định của thời gian bắt đầu, nhưng điều đó không có hậu quả.


1

Tôi tranh luận rằng đây là O (n). sử dụng http://www.cforcoding.com/2009/07/plain-english-explanation-of-big-o.html làm tài liệu tham khảo.

Big O là gì?

Ký hiệu Big O tìm cách mô tả độ phức tạp tương đối của một thuật toán bằng cách giảm tốc độ tăng trưởng cho các yếu tố chính khi yếu tố chính có xu hướng vô cùng.

Ví dụ tốt nhất về Big-O mà tôi có thể nghĩ đến là thực hiện số học. Các phép toán số học cơ bản mà chúng tôi đã học ở trường là:

thêm vào; phép trừ; phép nhân; và sự phân chia. Mỗi điều này là một hoạt động hoặc một vấn đề. Một phương pháp giải những điều này được gọi là một thuật toán.

Ví dụ của bạn,

cho đầu vào là n = 20 (với đơn vị năm).

thuật toán là một hàm toán học f (). trong đó f () sẽ đợi trong n năm, với các chuỗi 'gỡ lỗi' ở giữa. Hệ số tỷ lệ là 1. f () có thể giảm / hoặc tăng bằng cách thay đổi hệ số tỷ lệ này.

đối với trường hợp này, đầu ra cũng là 20 (thay đổi đầu vào thay đổi đầu ra một cách tuyến tính).

về cơ bản chức năng là

f(n) = n*1 = n
    if  n = 20, then 
f(20) = 20 
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.