117

Would this be classified as an O(1) algorithm for "Hello, World!" ??

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!");
   }
}

I'm thinking of using the

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

snippet of code as a busy loop to put in as a joke whenever someone asks for an algorithm of a certain complexity. Would this be correct?

Subpar Web Dev
  • 3,210
  • 7
  • 21
  • 35
  • 15
    This is `O(N)` complexity not `O(1)` – Fabjan Dec 02 '15 at 17:07
  • 3
    @Fabjan But doesn't the busy loop add a constant number of operations? – Subpar Web Dev Dec 02 '15 at 17:09
  • 19
    @SubparWebDev No, you don't know how many times it will get through the loop, even if you know the exact difference in time between when you start the program and the specified date. It's dependent on how fast the computer running it is, what else is running on it, how the CPU schedules the task, etc. – Servy Dec 02 '15 at 17:10
  • 132
    @Fabjan There is no `N` that the algorithm is dependent on, so you can't actually say that it's an O(N) algorithm. – Servy Dec 02 '15 at 17:12
  • 29
    Technically there is no input, so `N` does not even make any sense. But you could consider `DateTime.Now` an input which makes this still dependant on the result. If you can assume a realistic value for `DateTime.Now`, then yes, the program loops a constant amount of times. – poke Dec 02 '15 at 17:13
  • 2
    FYI http://bigocheatsheet.com/ – Liam Dec 02 '15 at 17:14
  • 44
    The problem statement must define what N is. – Yacoub Massad Dec 02 '15 at 17:15
  • 7
    @Servy The machine something runs on is completely irrelevant to the algorithmic complexity. The complexity is a theoretical measurement that does not take into account any implementation specific parts. – poke Dec 02 '15 at 17:15
  • 4
    @poke That's true for most algorithms. When you write a quick sort for a list the number of comparisons performed is completely independent of the hardware you run it on; it's only dependent on the number of items in the list. But the busy loop here results in code that performs the operation in a manor that *is* dependent on the hardware running it. Such a relationship is highly unusual (and usually not sensible), but certainly not impossible. – Servy Dec 02 '15 at 17:17
  • @Servy Well, in this scenario it is certainly the case... – Fabjan Dec 02 '15 at 17:31
  • @poke Also, Servy's original comment about the machine running the program was a response to OP's question about a "constant number of operations," so it wasn't (directly) making any claims about machines and algorithmic complexity. – Kyle Strand Dec 02 '15 at 19:15
  • 12
    @Servy "manner" not "manor". Not something that I would normally bother correcting but [probably at some point someone should let you know...](https://www.google.co.uk/search?q=servy+manor+stackexchange) – Martin Smith Dec 02 '15 at 21:27
  • 1
    I think a bit of code that depends on the externally defined `DateTime.Now` does not qualify as an algorithm in the first place. – RemcoGerlich Dec 02 '15 at 22:12
  • If you use the example Thread.Sleep(new Timespan(365*20, 0,0,0)) instead, that removes the issue of "SystemTime" as an input, which might make the question clearer. – deworde Dec 03 '15 at 14:11
  • First of all. An algorithm without any input could be considered an algorithm? witch problemas are resolving? Date and string to print could be considered as variables? PS:10 hour working, don't take this comment serious – jabujavi Dec 03 '15 at 16:30
  • 2
    This isn't even an algorithm. An algorithm involves making decisions to produce some desired output from a given input, but this just prints Hello World regardless. – Andy Dec 04 '15 at 07:03
  • 3
    There are some people here who definitly should read about [how Big O is defined](https://en.wikipedia.org/wiki/Big_O_notation) instead of mumbling something about input, output, number of computations blah, blah. – TobiMcNamobi Dec 04 '15 at 07:29
  • 1
    BTW the question shows very little research effort to say the least. But it is clear as crystal. And when a question causes that much discussion it must be useful as well. +1 :-) – TobiMcNamobi Dec 04 '15 at 07:40
  • 1
    You sure started a discussion among the intellectuals here, OP. – John Red Dec 04 '15 at 09:17
  • 1
    If we assume DateTime.Now as a parameter, then the program loops 0 times for sufficiently large values of DateTime.Now. Not that it would make any difference but n would be log(DateTime.Now) not DateTime.Now. – emory Dec 04 '15 at 16:39
  • 1
    `TwentyYearsLater` is essentially an input to the algorithm, in which case it's `O(N)`. IE: the algorithm takes exactly as long as you say for it to take; it's directly proportionate to the amount of time you've specified. – Dave Cousineau Dec 04 '15 at 20:19
  • In general, questions dealing with the analysis of algorithms might get better answers at [CS.SE]. – Juho Dec 06 '15 at 14:52
  • 1
    @SubparWebDev username checks out – Alex Van Liew Dec 07 '15 at 05:39
  • 1
    This doesn't even meet the requirements for "Hello World". It prints a bunch of extra stuff. It wouldn't pass my "Hello World" regression test suite... – GreenAsJade Dec 07 '15 at 13:23
  • this is not an algorithm, this is not solving any problem, just loops unless you execute it after the 1/1/2035 – albanx Dec 08 '15 at 22:56
  • please use a timer instead. reading of that while loop hurts – giammin Dec 09 '15 at 09:11

15 Answers15

409

Big O notation in this context is being used to describe a relationship between the size of the input of a function and the number of operations that need to be performed to compute the result for that input.

Your operation has no input that the output can be related to, so using Big O notation is nonsensical. The time that the operation takes is independent of the inputs of the operation (which is...none). Since there is no relationship between the input and the number of operations performed, you can't use Big O to describe that non-existent relationship

Servy
  • 202,030
  • 26
  • 332
  • 449
  • 6
    What about `O(max(1, 2035 - yearTheProgramIsStarted))`? – Bergi Dec 02 '15 at 19:12
  • 19
    @Bergi [Actually no[(http://stackoverflow.com/questions/34048740/is-this-technically-an-o1-algorithm-for-hello-world/34048908?noredirect=1#comment55852230_34048740), you can't just describe the number of iterations of the loop purely based on the time that you run the job. And of course you combine that with the fact that the system clock can be changed at any time by the user to whatever time they want, etc.and you still don't have a well formed input that can be accurately related to a number of operations needed to produce the output. Heck, even the output itself isn't consistent. – Servy Dec 02 '15 at 19:26
  • 24
    One could argue that the state of the system (which includes the clock) is part of the input for the program. In that sense, you could use the date as an input parameter, albeit not explicit. It is odd, though. – Connor Clark Dec 02 '15 at 19:41
  • 9
    To be more clear, the 'implicit' input is the delta between Jan 1st, 2035 and today. – Connor Clark Dec 02 '15 at 19:44
  • @Servy: I think you actually can model the number of iterations like that, and it sounds quite sensible. Of course, opening an "algorithm" to the real world (think `IO` in Haskell) is a bottomless pit, so if you take into account any kind of interference with the machine that runs the program we'll only get ridiculous results - up to incorporating the heat death of the universe :-) – Bergi Dec 02 '15 at 19:52
  • 6
    @Hoten But the system time isn't a fixed value. This function is *not* the same as just accepting a `DateTime` for the start time as input. As I said earlier, *the system clock can change over time*. And again, you can't directly map the quazi input that you're describing to a fixed output. There is not a known number of operations performed for a given start time, or even for two programs that always get a sensible value of `DateTime.Now`, so you can't relate the two as the time changes, because you can't even relate them when the time *doesn't* change. – Servy Dec 02 '15 at 20:07
  • 1
    @Hoten: Just because you *can* do something, doesn't mean you *should*. Using Big O notation to describe something like the OPs example is like using a screwdriver as a replacement for a hammer. Sure, it can be argued that it will work, but that doesn't mean it's all that useful. – whatsisname Dec 02 '15 at 21:41
  • The state of the machine it's run on becomes part of the input in this case; Datetime.now is an input. – scott_fakename Dec 02 '15 at 21:47
  • 2
    @scott_fakename It's not actually the simple. It's not like it just reads the current time once, and the acts based on that. The system time is accessed many times, at varying intervals, with different effects, etc. You can't treat it a single input value, or even a collection, sequence, or stream of values. And on top of all of that, you can't reliably relate whatever value(s) you do get to the output of the program. – Servy Dec 02 '15 at 22:00
  • 3
    Big O describes the growth rate of a function compared to the growth rate of another function. It doesn't make sense to talk about Big O without also stating what the two functions are. The OP has only given one of the functions: f(N) = 1. Until the OP provides the second function, the question is simply nonsensical. It isn't even right or wrong, it simply makes no sense so that even the question of "wright" or "wrong" doesn't even arise. – Jörg W Mittag Dec 03 '15 at 00:48
  • 2
    In short: This *program* does not represent an *algorithm*. For example, you cannot translate it to a Turing machine (those do not have real-time clocks) – Hagen von Eitzen Dec 03 '15 at 10:15
  • The system clock being able to change seems like a removable technicality, and there is a *maximum* number of operations per time on whichever device is running this… – Ry- Dec 03 '15 at 10:19
  • 2
    The problem here is that the words 'function' and 'algorithm' have precise mathematical meanings; computer subroutines are not the same things; people have just written programming languages that confuse these two concepts. The OP is a computer subroutine, not a function; it doesn't implement an algorithm, but a 'wait and report status' control system. – Steve Cooper Dec 03 '15 at 10:26
  • @SteveCooper: I disagree. That's a perfectly fine interactive algorithm. – Jörg W Mittag Dec 03 '15 at 12:09
  • 1
    @JörgWMittag - what's an 'interactive algorithm?' I was using the term in the sense used in mathematics and computer science -- https://en.wikipedia.org/wiki/Algorithm. Algorithms are incapable of interaction with anything but inputs and whatever internal state is required during the computation. In [Turing machine](https://en.wikipedia.org/wiki/Turing_machine) terms, the algorithm is the table of rules, the inputs and state are the tape. Turing machines have no clocks and no printers, so the code can't be translated to a Turing machine, so it's not an algorithm. It's a computer subroutine. – Steve Cooper Dec 03 '15 at 12:25
  • @SteveCooper: an interactive algorithm is an algorithm that interacts with the environment. The canonical example is the "bucket algorithm": in the morning, you put a bucket out in the yard, in the evening you take it back inside and measure the amount of (rain) water. This is an algorithm: it can be expressed in finite space and time (I just did), it has a finite number of discrete finite steps, it has an output. What more do you want? By the way, there are many algorithms which cannot be implemented in Turing Machines, interactive algorithms being one example, analog algorithms another. – Jörg W Mittag Dec 03 '15 at 12:33
  • @JörgWMittag - thanks for the link to the lectures; I've pulled up one now and it looks good so far! Your bucket example is interesting. I think you could implement that as the program for an robot - one equipped with sensors, a clock, etc.What I can't figure out is, if this is an algorithm, what computer program isn't an algorithm? – Steve Cooper Dec 03 '15 at 13:20
  • @SteveCooper: Why do there have to be programs which aren't algorithms? – Jörg W Mittag Dec 03 '15 at 13:22
  • Interesting; does this answer change if I make the function take an int array, return the number of elements (so it is an algorithm with an input), and then add the line "Thread.Sleep(new Timespan(365*20, 0,0,0));" (to remove the issue of SystemTime as an input?) – deworde Dec 03 '15 at 14:16
  • 1
    @deworde What are you then using Big O to measure? What operation are you intending to relate to what? Are you comparing the number of sleeps to the size of the input array, if so, then that's clearly constant. – Servy Dec 03 '15 at 14:32
  • By default, N refers to the size of the input to the program. For an invoked executable, that's the size of the cmd line args (or 0 is there is none). The program ignores the input, hence O(1). See my answer below. – waldol1 Dec 04 '15 at 05:49
  • @Servy From the point of view that above programm has no input you could still think of it as a constant function (on any set). It would be O(1) then. But I find this a bit stringent. The context (i.e. system time) *is* some kind of input and then you would have O(n). – TobiMcNamobi Dec 04 '15 at 07:37
  • @waldol1 Saying that the program has no input doesn't make the algorithm O(1). It doesn't complete in a constant amount of time. There is no relationship that you're describing here. That's not O(1), it's simply undefined. – Servy Dec 04 '15 at 14:13
  • @TobiMcNamobi But the number of operations performed isn't necessarily related linearly to that input value, if you can even call it an input value (as the program sits, you really can't). – Servy Dec 04 '15 at 14:14
  • 1
    @DavidRicherby True enough; it's what it's being used for in this context, not the only thing it's capable of being used for; edited accordingly. – Servy Dec 05 '15 at 19:53
  • We could also consider a "hello world" as if it would give back a constant for any input. In this sense, it is in O(0). – peterh Sep 05 '18 at 00:19
87

Big-O notation means roughly 'given an operation on an amount of work, N, how much calculation time, proportional to N, does the algorithm take?'. Eg, sorting an array of size N can take N^2, Nlog(N), etc.

This has no amount of input data to act on. So it's not O(anything).

Even worse; this isn't technically an algorithm. An algorithm is a method for computing the value of a mathematical function -- maths functions are a mapping from one input to an output. Since this takes no input and returns nothing, it's not a function, in the mathematical sense. From wikipedia:

An algorithm is an effective method that can be expressed within a finite amount of space and time and in a well-defined formal language for calculating a function. Starting from an initial state and initial input (perhaps empty), the instructions describe a computation that, when executed, proceeds through a finite number of well-defined successive states, eventually producing "output" and terminating at a final ending state.

What this is, technically, is a control system. From wikipedia;

A control system is a device, or set of devices, that manages, commands, directs or regulates the behavior of other devices or systems.

For people wanting a more in-depth answer about the difference between mathematical functions and algorithms, and the more powerful abilities of computers to do side-effecting things like console output, displaying graphics, or controlling robots, have a read of this paper on the Strong Church-Turing Hypothesis

Abstract

The classical view of computing positions computation as a closed-box transformation of inputs (rational numbers or finite strings) to outputs. According to the interactive view of computing, computation is an ongoing interactive process rather than a function-based transformation of an input to an output. Specifically, communication with the outside world happens during the computation, not before or after it. This approach radically changes our understanding of what is computation and how it is modeled.

The acceptance of interaction as a new paradigm is hindered by the Strong Church-Turing Thesis (SCT), the widespread belief that Turing Machines (TMs) capture all computation, so models of computation more expressive than TMs are impossible. In this paper, we show that SCT reinterprets the original Church-Turing Thesis (CTT) in a way that Turing never intended; its commonly assumed equivalence to the original is a myth. We identify and analyze the historical reasons for the widespread belief in SCT. Only by accepting that it is false can we begin to adopt interaction as an alternative paradigm of computation

Community
  • 1
  • 1
Steve Cooper
  • 20,542
  • 15
  • 71
  • 88
  • It doesn't need to be a sequence. It's just some data input, and landau notation describes running time in relation to some metric(s) on that data - typically something size-related. – Bergi Dec 02 '15 at 19:14
  • @Bergi - yeah, see your point! Just making an approximation, really, but yeah -- if you can measure the amount of work to do, and the amount of steps it takes to get there, big-o reflects the relation of those two measures. Closer? – Steve Cooper Dec 02 '15 at 19:51
  • @kapep - it's not a pure function because it's a void method, but if we count console output, it's still random; it could output any of { "Hello, World!", "It's still not time to print the hello ...\nHello, World!", "It's still not time to print the hello ...It's still not time to print the hello ...\nHello, World!", ... } – Steve Cooper Dec 02 '15 at 20:00
  • @SteveCooper oops, I totally missed the console output in the loop. I was thinking about a function that wastes some time in the loop and only returns/prints constant output once at the end. – kapex Dec 02 '15 at 20:50
  • 1
    Printing to stdout isn't an output? – rpax Dec 03 '15 at 07:25
  • 4
    @rpax Not mathematically, no. A function is an unchanging translation from inputs to outputs; eg, 'square' is the function that always returns 9 if you input 3. A c# method is only a maths function if a call with the same parameters always gives the same return value. Otherwise -- if it has side-effects like writing to the console, displaying graphics, allocating memory -- those aren't mathematical functions. (Gonna add a link to my answer that goes into it in excruciating detail :) ) – Steve Cooper Dec 03 '15 at 10:19
41

No, your code has time complexity of O(2^|<DeltaTime>|),

For a proper coding of the current time.
Please, let me first apologize for my English.

What is and how Big O works in CS

Big O notation is not used to tie the input of a program with its running time.
Big O notation is, leaving rigor behind, a way to express the asymptotic ratio of two quantities.

In the case of algorithm analysis these two quantities are not the input (for which one must first have a "measure" function) and the running time.
They are the length of the coding of an instance of the problem1 and a metric of interest.

The commonly used metrics are

  1. The number of steps required to complete the algorithm in a given model of computation.
  2. The space required, if any such concept exists, by the model of computation.

Implicitly is assumed a TM as the model so that the first point translates to the number of applications of the transition2 function, i.e. "steps", and the second one translates the number of different tape cells written at least once.

Is it also often implicitly assumed that we can use a polynomially related encoding instead of the original one, for example a function that search an array from start to end has O(n) complexity despite the fact that a coding of an instance of such array should have length of n*b+(n-1) where b is the (constant) number of symbols of each element. This is because b is considered a constant of the computation model and so the expression above and n are asymptotically the same.

This also explains why an algorithm like the Trial Division is an exponential algorithm despite essentially being a for(i=2; i<=sqr(N); i++) like algorithm3.

See this.

This also means that big O notation may use as many parameters one may needs to describe the problem, is it not unusual to have a k parameter for some algorithms.

So this is not about the "input" or that "there is no input".

Study case now

Big O notation doesn't question your algorithm, it just assumes that you know what you are doing. It is essentially a tool applicable everywhere, even to algorithm which may be deliberately tricky (like yours).

To solve your problem you used the current date and a future date, so they must be part of the problem somehow; simply put: they are part of the instance of the problem.

Specifically the instance is:

<DeltaTime>

Where the <> means any, non pathological, coding of choice.

See below for very important clarifications.

So your big O complexity time is just O(2^|<DeltaTime>|), because you do a number of iteration that depends on the value of current time. There is no point in putting other numeric constants as the asymptotic notation is useful as it eliminates constants (so for example the use of O(10^|<DeltaTime>|*any_time_unit) is pointless).

Where the tricky part is

We made one important assumption above: that the model of computation reificates5 time, and by time I mean the (real?) physical time. There is no such concept in the standard computational model, a TM does not know time, we link time with the number of steps because this is how our reality work4.

In your model however time is part of the computation, you may use the terminology of functional people by saying that Main is not pure but the concept is the same.

To understand this one should note that nothing prevent the Framework to using a fake time that run twice, five, ten times faster that physical time. This way your code will run in "half", "one fifth", "one tenth" of the "time".

This reflection is important for choosing the encoding of <DeltaTime>, this is essentially a condensed way of writing <(CurrentTime, TimeInFuture)>. Since time does not exist at priory, the coding of CurrentTime could very well be the word Now (or any other choice) the day before could be coded as Yesterday, there by breaking the assumption that the length of the coding increase as the physical time goes forward (and the one of DeltaTime decreases)

We have to properly model time in our computational model in order to do something useful.

The only safe choice we can do is to encode timestamps with increasing lengths (but still not using unary) as the physical time steps forward. This is the only true property of time we need and the one the encoding needs to catch. Is it only with this type of encoding that your algorithm maybe given a time complexity.

Your confusion, if any, arise from the fact that the word time in the phrases 'What is its time complexity?' and 'How much time will it take?' means to very very different things

Alas the terminology use the same words, but you can try using "steps complexity" in your head and re-ask yourself your question, I hope that will help you understand the answer really is ^_^


1 This also explains the need of an asymptotic approach as each instance has a different, yet not arbitrary, length.
2 I hope I'm using the correct English term here.
3 Also this is why we often find log(log(n)) terms in the math.
4 Id est, a step must occupy some finite, but not null, nor not connected, interval of time.
5 This means that the computational mode as a knowledge of physical time in it, that is can express it with its terms. An analogy are how generics work in the .NET framework.

Community
  • 1
  • 1
Yuni Mj
  • 493
  • 3
  • 3
  • 3
    "So your big O running time is just" .. I'm sure you meant 'big O complexity'?. Also, we can still just call 'deltaTime' our 'n' right.. so your saying O(2^N) something like the complexity of the Fibonacci algorithm. How did you come to the "2^"? – Ross Dec 03 '15 at 06:10
  • @Ross, thank for the point. I came with 2 out of habit for working with binary numbers. The point is that the steps are linear with the **length** of the representation of the number. The actual base is not really important and varies based on the specific encoding. It is [pseudo linear](https://en.wikipedia.org/wiki/Pseudo-polynomial_time). – Yuni Mj Dec 03 '15 at 19:32
  • I'm sorry, but could you please elaborate more in your answer how you concluded that the complexity is `O(2^n)`? It's not clear for beginners. – Arturo Torres Sánchez Dec 03 '15 at 21:03
  • `O(2^||)` - stating that this means "O(2^[the length of the representation of DeltaTime)]" (not "O(2^[the absolute value of DeltaTime])"), as well as explaning *why* the length of DeltaTime's representation matters - that is, what operation gets performed based on the number of bits - would make this answer much clearer and really great. – Esoteric Screen Name Dec 03 '15 at 22:03
  • @EsotericScreenName This is basically a `for(i = k1; i < k2; i++)`. The time complexity of this snippet is `O(2^||)`. Informally it is often said it has complexity `O(k2-k1)`, i.e. it is linear in the *DeltaK*. But it is a misuse. If `k1` and `k2` decimal numbers with 3 digits numbers (including leading 0s), their values are in the order of 10^3. If they were 4 digits numbers their values would be in the order of 10^4, and so on. This is how big O notation is used in CS. You are right though, i **really** should make this answer clearer! I lost something in translation :( – Yuni Mj Dec 04 '15 at 13:16
  • 2
    @YuniMj While your reasoning is technically not wrong, I think that by insisting to measure the *size* of `DeltaTime` instead of its *value*, you're just adding additional confusion. For example, but that reasoning no optimal sort algorithm has time complexity $O(n\cdot log n)$. Why? Because you you either only finitely many distinguishable objects to sort, in which case you can always use bucket sort to sort in $O(n)$. Or your object size is unbounded, in which case $O(n\cdot log n)$ won't hold, since a single comparison won't have constant time anymore... – fgp Dec 04 '15 at 22:58
  • @fgp When you work with Arrays, the length of the coding of an array is proportional to the number of elements. For example by using JS array notation, an array with `n` elements can be coded with `n*b + n-1 + 2`. Note how here the length is proportional to the **value** of `n`. However when coding an integer `k` the length of the coding is proportional to `log n`. So the sorting lower complexity **is** `O(n log n)`, where `n` is the number of elements. If the size of each element is unbounded we cannot even compare elements in [finite time](https://en.wikipedia.org/wiki/Hypercomputation). – Yuni Mj Dec 05 '15 at 09:04
  • @fpg Typo: It is `log k` not `log n` – Yuni Mj Dec 05 '15 at 09:11
  • @YuniMj No, unbounded just means that as the *number* of elements increases, their size does to. This *must* be the case if all elements are supposed to be distinguishable - on any Turing-Like machine, for every $n$ there are only finitely many distinguishable objects of size $n$. – fgp Dec 05 '15 at 11:13
  • @fgp Do you mean that, for an array of pair-wise distinguishable elements, as the numbers of elements grows, so must do the single element size? Because the latter grow is sublinear and so mine encoding with a constant length and yours give the same result within big O notation. – Yuni Mj Dec 05 '15 at 17:26
  • 1
    FWIW O(2^n) != O(10^n) http://stackoverflow.com/questions/19081673/big-o-notation-of-exponential-functions – panofsteel Dec 06 '15 at 03:44
  • @NathanFD I know that. But exponential TC is still exponential TC. – Yuni Mj Dec 06 '15 at 09:26
29

Although there are a bunch of great answers here, let me rephrase all of them a bit.

Big-O notation exists to describe functions. When applied to analysis of algorithms this requires us to first define some characteristic of this algorithm in terms of a function. The common choice is considering number of steps as a function of input size. As noted in other answers, coming up with such function in your case seems strange, because there is no clearly defined "input". We can still try to do it, though:

  • We can regard your algorithm as a constant function which takes any input of any size, ignores it, waits a fixed amount of time, and finishes. In this case its runtime is f(n) = const, and it is a O(1)-time algorithm. This is what you expected to hear, right? Yes, it is, technically, an O(1)-algorithm.
  • We can regard the TwentyYearsLater as the "input-size"-like parameter of interest. In this case the runtime is f(n) = (n-x) where x is the "now time" at the moment of invocation. When seen this way, it is a O(n)-time algorithm. Expect this counter-argument whenever you go showing your technically O(1)-algorithm to other people.
  • Oh, but wait, if k=TwentyYearsLater is the input, then its size n is, actually, the number of bits needed to represent it, i.e. n = log(k). The dependency between the size of the input n and runtime is therefore f(n) = 2^n - x. Seems like your algorithm has just become exponentially slow! Ugh.
  • Another input to the program is in fact the stream of answers given by the OS to the sequence of DateTime.Now invocations in the loop. We can actually imagine that this whole sequence is provided as input at the moment we run the program. The runtime can then be considered to depend on the property of this sequence - namely its length until the first TwentyYearsLater element. In this case the runtime is again f(n) = n and the algorithm is O(n).

But then again, in your question you did not even say you were interested in runtime. What if you meant memory use? Depending on how you model the situation you can say the algorithm is O(1)-memory or, perhaps, O(n)-memory (if the implementation of DateTime.Now requires to keep track of the whole invocation sequence somewhy).

And if your goal was to come up with something absurd, why don't you go all in and say that you are interested in how the size of the algorithm's code in pixels on the screen depends on the chosen zoom level. This might be something like f(zoom) = 1/zoom and you can proudly declare your algorithm to be O(1/n)-pixel size!

KT.
  • 10,815
  • 4
  • 47
  • 71
  • +1. I believe the "stream of answers given by the OS to the sequence of `DateTime.Now` invocations` is the real input here. But I think the conclusion should not be that it's O(n), but it's O(k), where k is the length until the first `TwentyYearsLater` element. – justhalf Dec 03 '15 at 03:55
  • 7
    This is the best answer so far - in order for Big O to be meaningful you have to apply mathematical semantics/assumptions to the physical implementation (essentially defining a mathematical model for the program with a meaningful definition of "input"). In this sense the complexity of the "program" depends on the semantics you apply - if you assume that N is the time difference which scales linearly with the number of operations, it's O(n). If you assume a fixed number of operations as a result of a fixed period of time, it's O(1). – Ant P Dec 03 '15 at 07:18
21

I have to disagree with Servy slightly. There is an input to this program, even if it's not obvious, and that's the system's time. This might be a technicality you hadn't intended, but your TwentyYearsFromNow variable isn't twenty years from the system's time now, it's statically assigned to January 1st, 2035.

So if you take this code and execute it on a machine that has a system time of January 1st, 1970, it's going to take 65 years to complete, regardless of how fast the computer is (there may be some variation if its clock is faulty). If you take this code and execute it on a machine that has a system time of January 2nd, 2035, it will complete almost instantly.

I would say your input, n, is January 1st, 2035 - DateTime.Now, and it's O(n).

Then there's also the issue of the number of operations. Some people have noted that faster computers will hit the loop faster, causing more operations, but that's irrelevant. When working with big-O notation, we don't consider the speed of the processor or the exact number of operations. 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.

As for this:

I'm thinking of using the [redacted code] snippet of code as a busy loop to put in as a joke whenever someone asks for an algorithm of a certain complexity. Would this be correct?

No, not really. Other answers have covered this, so I just wanted to mention it. You can't generally correlate years of execution to any big-O notation. Eg. There's no way to say 20 years of execution = O(n^87) or anything else for that matter. Even in the algorithm you gave, I could change the TwentyYearsFromNow to the year 20110, 75699436, or 123456789 and the big-O is still O(n).

Shaz
  • 1,376
  • 8
  • 18
  • 7
    The time isn't an input to the function, it's *constantly changing state* that is observed throughout the execution of the method. The system clock can even be changed *while the function is running*. For Big O to be meaningful you would also need to have each input correspond 1-1 with an output value, as well as a number of operations necessary to compute it. For this operation the output isn't even consistent for the same input (in fact it varies *wildly*), in addition to the number of operations performed also varying wildly. – Servy Dec 02 '15 at 20:00
  • `When working with big-O notation, we don't consider the speed of the processor or the exact number of operations.` This is a false statement. Pretty much any sensible operation that you would try to compute the Big O value of isn't going to change the number of operations performed based on the hardware, *but this one does*. Big O is just a way of relating the number of operations to the size of the input. For most operations that is independent of system hardware. *In this case it is not*. – Servy Dec 02 '15 at 20:02
  • `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.` That's also a false statement. The environment won't necessarily alter the number of operations in the loop linearly. There could, for example, be other programs on the computer that use more or less CPU time at different points in time, changing the time given to this application constantly over time. – Servy Dec 02 '15 at 20:03
  • I'm with @Servy on this one, but for a slightly different reason. The main function takes no parameters and returns no input. It's a function of nil => nil, if you like. Doesn't matter what the time is, it still returns nothing. – Steve Cooper Dec 02 '15 at 20:10
  • 1
    If we're using this definition -- "In mathematics, a function is a relation between a set of inputs and a set of permissible outputs with the property that each input is related to exactly one output." (wikipedia) -- and we're counting the console output as 'the output of the function', this varies, getting longer on faster computer, because it will write ""It's still not time to print the hello ..."" more often. – Steve Cooper Dec 02 '15 at 20:12
  • @SteveCooper It also varies based on the other applications running on the machine, the algorithm that the process (and thread) scheduler uses, what the standard output is and how long it takes to write to, etc. Some of these (i.e. the algorithm that the process scheduler uses) can very easily result in the relationship between how long the program has been executing and the number of iterations of the loop *being just about anything*. – Servy Dec 02 '15 at 20:17
  • @Ryan There's nothing unreasonable about changing the system clock at all. It's a factor that changes the output of the program. It's not a core assumption of C# that the system clock never changes; it's an entirely reasonable and supported operation. It's altering some *outside* state that the program explicitly checks. Outside state changes; such is its nature. – Servy Dec 02 '15 at 20:42
  • @Ryan No, the number of iterations of the loop *won't* necessarily be linear. It won't even necessarily approximate being linear. Pretty much all OS process schedulers alter how much time they allow a process to run based on how long it has been running. The longer a process has been running the more time it tends to be given, (often up to a point) meaning that the relationship would actually be greater than linear; typically polynomial. An OS may even have throttling in place that begins to limit CPU time for very long running processes, so the relationship could be less than linear. – Servy Dec 02 '15 at 20:45
  • @Ryan Another trivial counter example, if I write a program that spins up a new worker that runs forever every set interval of time, then the time given to each worker goes down polynomially, meaning the operations performed is 1/n over time. – Servy Dec 02 '15 at 20:47
  • @Ryan Another program *can't* alter the local variable of another function, either from within the program or from outside of it. The system actively inhibits them from doing so. Conversely, the system time is *designed to be altered*. Any program that ever uses the system time should be written under the assumption that the user can change it. Fortunately, this program *even functions perfectly fine when the system clock is changed*. Changing the system clock alters how long it runs for, but it in no way breaks it's logic. – Servy Dec 02 '15 at 21:00
  • @Ryan Meaning that after 1 second the worker is getting 1/2 the CPU, after 10 seconds has 1/10th the CPU, after 100 seconds has 1/100th the CPU, and so on. So at time N it has 1/N of the CPU's resources, which should correlate to the number of operations per second it can perform. – Servy Dec 02 '15 at 21:02
  • @Ryan The spirit of Big O is to describe the relationship between two things. As a consequence, it tends to only be useful when describing things that have a sensible relationship. The problem here is that *you're trying to describe things with Big O that don't have a sensible relationship*, so it simply doesn't apply. That you don't understand the complexity of the relationship, and are instead assuming that it's dramatically simpler than it actually is, is the flaw in your logic. It doesn't really have anything to do with Big O. – Servy Dec 02 '15 at 21:04
13

Big-O analysis deals with the amount of processing involved as the amount of data being processed increases without limit.

Here, you're really only dealing with a single object of fixed size. As such, applying big-O analysis depends heavily (primarily?) upon how you define your terms.

For example, you could mean printing output in general, and imposing a wait so long that any reasonable amount of data would/will be printed in precisely the same period of time. You also have to add a little more in the way of somewhat unusual (if not outright wrong) definitions to get very far though--particularly, big-O analysis is usually defined in terms of the number of fundamental operations needed to carry out a particular task (but note that complexity can also be considered in terms of things like memory use, not just CPU use/operations carried out).

The number of fundamental operations usually translates fairly closely to time taken, however, so it isn't a huge stretch treat the two as synonymous. Unfortunately, however, we're still stuck with that other part: the amount of data being processed increasing without limit. That being the case, no fixed delay you can impose will really work. To equate O(1) with O(N), you'd have to impose an infinite delay so that any fixed amount of data took forever to print, just as an infinite amount of data would.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
10

big-O relative to what?

You seem to be intuiting that twentyYearsLater is an "input". If indeed you wrote your function as

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

It would be O(N) where N = years (or just say O(years)).

I would say your algorithm is O(N) relative to whatever number you happen to write in the line of code starting with twentyYearsLater =. But people do usually not consider numbers in the actual source code as the input. They might consider the command line input as the input, or the function signature input as the input, but, most likely not the source code itself. That is what you are disputing with your friend - is this the "input"? You set up your code in a way to make it intuitively seem like an input, and you can definitely ask its big O running time with respect to the number N on line 6 of your program, but if you use such a non-default choice as input you really need to be explicit about it.

But if you take the input to be something more usual, like the command line or the input to the function, there is no output at all and the function is O(1). It takes twenty years, but since big-O does not change up to a constant multiple, O(1) = O(twenty years).

Similar question - what is the runtime of:

void sortArrayOfSizeTenMillion(int[] array)

Assuming it does what it says and the input is valid, and the algorithm harnesses a quicksort or bubble sort or anything reasonable, it's O(1).

djechlin
  • 59,258
  • 35
  • 162
  • 290
  • Hardcoding the input doesn't mean the input disappears. Nor are quicksort and bubblesort of O(1) time complexity in any instance. http://bigocheatsheet.com/ – Theo Brinkman Dec 04 '15 at 16:14
  • @TheoBrinkman If you want to be technical, in a Turing machine model, encoding what you think of the input, into the Turing machine itself, makes it, by definition, not the input. The Turing machine will then run in a constant time independent of whatever actual input it has. It is in some sense not running a "bubble sort" since it's not sorting anything but rather operating on its own representation, however in non-technical terms of course you could describe the algorithm as a bubble sort. – djechlin Dec 04 '15 at 16:31
  • In equally 'non-technical terms', you could describe the algorithm in question as a suspension bridge. – Theo Brinkman Dec 04 '15 at 20:14
  • @TheoBrinkman no, you could not. That would not make sense to anyone. – djechlin Dec 04 '15 at 20:21
  • It makes every bit as much sense as describing it as an O(1) bubble sort. – Theo Brinkman Dec 04 '15 at 20:33
  • @TheoBrinkman Okay, good question. You could formalize this by starting with a Turing machine with an array of length N hardcoded as per above comment. The Turing machine will then instantiate a bubble sort Turing machine, input the array, and output the result. The original Turing machine will ignore whatever input it is given. This is then an O(1) algorithm that sorts the hardcoded array. It utilizes an O(quadratic) algorithm where the input to the O(quadratic) algorithm is independent of the input to the original TM. The original alg is O(1) and uses a bubble sort, in technical terms. – djechlin Dec 04 '15 at 20:36
  • @TheoBrinkman I wrote "the algorithm harnesses" instead of "the algorithm is" to make that more clear. "The algorithm is an instantiation of" would also be precise and technically correct. "The algorithm is a bubble sort" is not precise and you took the interpretation that would make it incorrect. – djechlin Dec 04 '15 at 20:45
  • There is no scenario where the Quicksort or Bubblesort algorithms have O(1) time complexity. (http://bigocheatsheet.com/), but that's beside the point, because the algorithm being discussed isn't *either* a quicksort or bubble sort. – Theo Brinkman Dec 04 '15 at 20:49
  • @TheoBrinkman right, I explained it. The algorithm in question is O(1) and as part of its algorithm calls a bubble /quick sort. My answer is now explicit about that. Is there still a problem? – djechlin Dec 04 '15 at 20:55
  • Two things: 1) there is not sort *of any kind* in the algorithm being discussed, and 2) you can't have an O(1) algorithm that, as part of its process calls/includes anything *other* than O(1) algorithms. At best, any algorithm that performs/includes an quick sort is going to be O(n). – Theo Brinkman Dec 04 '15 at 21:00
  • @TheoBrinkman that is false. If function A calls function B there is no guarantee A and B have the asymptotic complexity. This is fundamental to both computer science and computer programming. – djechlin Dec 04 '15 at 21:02
  • @TheoBrinkman you might be interested in approximations algorithms to NP hard problems such as bin packing, traveling salesman, etc. The technique to construct an approximation algorithm is frequently to feed the NP hard problem a special case that it can solve tractably. This is exactly the technique you are suggesting is impossible. – djechlin Dec 04 '15 at 21:05
  • If function A calls function B, function B is part of the algorithm implemented in function A. Therefore, the complete algorithm cannot have lower functional complexity than the worse of either part. If it could, then all non-O(1) algorithms could be turned into O(1) algorithms by wrapping them in an 'if(1=1)' conditional. – Theo Brinkman Dec 04 '15 at 21:07
  • @TheoBrinkman your reasoning is incorrect because A does not call B with the same input as is passed to A. This is the case in my toy algorithm above that ignores its input and calls bubble sort with another fixed array. It is also the technique used in the bin packing PTAS among other problems. – djechlin Dec 04 '15 at 21:11
  • So your answer is entirely dependent upon an algorithm that is completely different than the one up in the question? – Theo Brinkman Dec 04 '15 at 21:17
  • As I've mentioned at least twice before, the algorithm in the question isn't a sort of any type, thus your entire side trek here about turning a quick sort into an O(1) algorithm by including it as part of another algorithm is completely pointless as it doesn't address the asker's question at all. – Theo Brinkman Dec 04 '15 at 21:23
  • @TheoBrinkman approximately 4 lines of my answer are devoted to the "search" analogy. You are the one who wished to discuss this in some detail. – djechlin Dec 04 '15 at 21:24
  • Also, in your example of wrapping a quick sort (with different inputs) into an entirely different O(1) algorithm, you *still* don't end up with O(1), you end up no better than O(1)+O(n log(n)) (and that's assuming the quick sort data set is already sorted). – Theo Brinkman Dec 04 '15 at 21:37
  • But, back to the main part of your answer, the algorithm *still* isn't O(n) where n=years, because n is the size of the dataset being processed by the algorithm, not the value of a particular item in the data set. – Theo Brinkman Dec 04 '15 at 21:40
  • @TheoBrinkman That's misleading because "n" is not the input to the original algorithm. It is independent of the input, let's say N, to the original algorithm, so that O(1) + O(n log n) = O(1) where O is with respect to N. My answer most definitely draws attention to this distinction. – djechlin Dec 04 '15 at 21:40
  • @TheoBrinkman That is *not* the convention in any of math, computer science or programming. n is whatever you say you want it to be. My answer is *very* clear that the burden is on the OP, who is considering an unconventional viewpoint, on specifying what "n" is, if it should be something other than the size of the dataset of the original algorithm. – djechlin Dec 04 '15 at 21:42
  • No, 'n' representing the size of the dataset is established by the use of 'big-O' notation to describe the algorithm's time complexity. That's what n means in O(n); that the algorithm scales linearly with the size of the dataset. If you change that, you're no longer using 'big-O' notation. – Theo Brinkman Dec 04 '15 at 23:14
  • @TheoBrinkman I'm sorry but that is not correct, and your viewpoint would preclude ever analyzing multiple functions or multiple inputs at once (who gets the letter n?), considering the complexity of function of multiple variables, ever using a different letter (such as N, which is about as common), etc. Whenever there is a lack of clarity or a dispute the correct solution is to be explicit about what the hidden variables are, as I explained in quite a bit of detail in my answer. This is established practice in mathematics and computer science and programming across the globe. – djechlin Dec 04 '15 at 23:20
  • Yes, when you're establishing a particular algorithm you can apply any meaning to any variable. However, this question explicitly deals with 'big-O' notation, in which 'n' refers to the size of the data set, and not the value of any particular member of said data set, even if there is only one member of said data set. – Theo Brinkman Dec 04 '15 at 23:50
  • @TheoBrinkman I actually understood the question to be precisely on the topic of what "n" refers to, so I wrote an answer on how "n" usually refers to the function or command line input but the OP is free to specify another input mechanism, which you seem to have just agreed with, so we agree there is no problem with my answer now? – djechlin Dec 04 '15 at 23:55
8

This "algorithm" is correctly described as O(1) or constant time. It's been argued that there is no input to this program, hence there is no N to analyze in terms of Big Oh. I disagree that there is no input. When this is compiled into an executable and invoked, the user can specify any input of arbitrary length. That input length is the N.

The program just ignores the input (of whatever length), so the time taken (or the number of machine instructions executed) is the same irrespective of the length of the input (given fixed environment = start time + hardware), hence O(1).

waldol1
  • 1,841
  • 2
  • 18
  • 22
  • But the number of operations *isn't* necessarily consistent, even with the same start time and hardware. On top of that, to claim an O(1) algorithm the output would have to always be constant, and it's not, it will vary *wildly* based on the start time and hardware. It could also very easily be infinite, which is certainly not constant. There is *no relationship* between the input you've defined and the number of operations performed. That's not constant, that's just undefined. You can't name a finite number and know that there will always be less operations than that. – Servy Dec 04 '15 at 14:20
  • The maximum real time that it will take is 20 years. If we start it in the future, yes it will take longer. Let's suppose that there is a finite lower bound on the amount of time a loop iteration takes and that we are running on serial hardware. Then, I can bound the number of times the loop will run, which means the entire computation can be bounded by a constant function, no matter the size of the ignored input. – waldol1 Dec 08 '15 at 00:34
  • `Let's suppose that there is a finite lower bound on the amount of time a loop iteration takes` That's a false assumption. The program can run forever. All I have to do is set my system clock to 50 years from now, start it, and it'll never finish. Or I could keep moving the clock back faster than it moves forward, or start it at *an indeterminate point in the past*. You simply cannot assume that there's a lower bound on how long the program runs; it can run forever. But, even if we take your (false) assumption as true, you still can't relate the number of operations performed to the input. – Servy Dec 08 '15 at 02:17
  • A single loop iteration takes a finite amount of time. It might be possible for it to execute an infinite number of times, but each one should be roughly constant. I don't see a problem with that assumption. – waldol1 Dec 15 '15 at 00:43
  • By that [completely incorrect] logic every single algorithm every is *always* O(1) because every *individual* operation is *always* constant. You're simply demonstrating that you don't know what Big O even is. It's a tool for (in context) describing the relationship between the size of the input an the number of relevant operations performed. O(1) means that there are a constant number of operations performed regardless of the input. Here there *aren't* a constant number of operations performed regardless of the input, there are potentially infinite operations performed, infinite != constant. – Servy Dec 15 '15 at 03:50
5

One thing I'm surprised hasn't been mentioned yet: big-O notation is an upper bound!

The issue everyone has noticed is that there is no N describing the inputs to the algorithm, so there is nothing to do big-O analysis with. However, this is easily mitigated with some basic trickery, such as accepting an int n and printing "Hello World" n times. That would get around that complaint and get back to the real question of how that DateTime monstrosity works.

There is no actual guarantee that the while loop will ever terminate. We like to think it has to at some time, but consider that DateTime.now returns the system date and time. There is actually no guarantee that this is monotonically increasing. It is possible that there is some pathologically trained monkey constantly changing the system date and time back to October 21, 2015 12:00:00 UTC until someone gives the monkey some auto-fitting shoes and a hoverboard. This loop can actually run for an infinite amount of time!

When you actually dig into the mathematical definition of big-O notations, they are upper bounds. They demonstrate the worst case scenario, no matter how unlikely. The worst case* scenario here is an infinite runtime, so we are forced to declare that there is no big-O notation to describe the runtime complexity of this algorithm. It doens't exist, just as 1/0 doesn't exist.

* Edit: from my discussion with KT, it is not always valid to presume the scenario we are modeling with big-O notation is the worst-case. In most cases, if an individual fails to specify which case we're using, they intended to explore the worst case. However, you can do a big-O complexity analysis on the best-case runtime.

Cort Ammon
  • 10,221
  • 31
  • 45
  • 3
    O is, in a sense an "upper bound", indeed, but it does not mean you can only speak of "worst-case complexity" using the O-notation. Expected complexity, best-case complexity, any other functional property - all of them can be discussed in terms of their O bounds. – KT. Dec 03 '15 at 11:34
  • @KY best case complexity is called little-o, and expected complexity is big-theta. big-o is always worst case complexity, by its mathematical definition. – Cort Ammon Dec 03 '15 at 15:21
  • No, you are mistaken here. Re-check the definitions. – KT. Dec 03 '15 at 15:23
  • @KT Okay, I'll recheck them. You recheck them too. https://en.wikipedia.org/wiki/Big_O_notation Its under Family of Bachmann–Landau notations – Cort Ammon Dec 03 '15 at 15:26
  • I suppose you could do someting insane like take a function `f` and declare function `g` to be the same as `f`, but with a restricted domain to only include `f`'s best case, and then do big-oh on `g`, but it starts to sound degenerate when you do that. – Cort Ammon Dec 03 '15 at 15:29
  • Try opening any Wikipedia page on an algorithm (e.g. https://en.wikipedia.org/wiki/Quicksort) and studying the "Worst case", "Average case" and "Best case" complexity stats in the panel on the right. They are all O-s. No insanity there. – KT. Dec 03 '15 at 15:40
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/96900/discussion-between-cort-ammon-and-kt). – Cort Ammon Dec 03 '15 at 15:41
5

Complexity is used to measure computational "horsepower" in terms of time/space. Big O notation is used to compare which problems are "computable" or "not computable" and also to compare which solutions -algorithms- are better than other. As such, you can divide any algorithm into two categories: those that can be solved in polynomial time and those that can't.

Problems like the Sieve of Erathostene are O(n ^exp) and thus are solvable for small values of n. They are computable, just not in polynomial time (NP) and thus when asked if a given number is prime or not, the answer depends on the magnitude of such number. Moreover, complexity does not depend on the hardware, so having faster computers changes nothing...

Hello World is not an algorithm and as such is senseless to attempt to determine its complexity -which is none. A simple algorithm can be something like: given a random number, determine if it is even or odd. Now, does it matter that the given number has 500 digits? No, because you just have to check if the last digit is even or odd. A more complex algorithm would be to determine if a given number divides evenly by 3. Although some numbers are "easy" to compute, others are "hard" and this is because of its magnitude: compare the time it takes to determine the remaninder between a number with one digit and other with 500 digits.

A more complex case would be to decode a text. You have an apparent random array of symbols which you also know are conveying a message for those having the decrypting key. Let's say that the sender used the key to the left and your Hello World would read: Gwkki Qieks. The "big-hammer, no-brain" solution would produce all combinations for those letters: from Aaaa to Zzzz and then search a word dictionary to identify which words are valid and share the two common letters in the cypher (i, k) in the same position. This transformation function is what Big O measures!

Turing
  • 64
  • 4
4

I think people are getting thrown off because the code doesn't look like a traditional algorithm. Here is a translation of the code that is more well-formed, but stays true to the spirit of OP's question.

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!");
}

The inputs are explicit whereas before they were implicitly given by the time the code was started at and by the speed of the hardware running the code. The code is deterministic and has a well-defined output for given inputs.

Because of the limitations that are imposed on the inputs we can provide, there is an upper bound to the number of operations that will be executed, so this algorithm is in fact O(1).

panofsteel
  • 195
  • 9
3

Everyone’s correctly pointed out that you don’t define N, but the answer is no under the most reasonable interpretation. If N is the length of the string we’re printing and “hello, world!” is just an example, as we might infer from the description of this as an algorithm “for hello, world!,” then the algorithm is O(N), because you might have an output string that takes thirty, forty or fifty years to print, and you’re adding only a constant time to that. O(kN+c) ∈ O(N).

Addendum:

To my surprise, someone is disputing this. Recall the definitions of big O and big Θ. Assume we have an algorithm that waits for a constant amount of time c and then prints out a message of length N in linear time. (This is a generalization of the original code sample.) Let’s arbitrarily say that we wait twenty years to start printing, and that printing a trillion characters takes another twenty years. Let c = 20 and k = 10¹², for example, but any positive real numbers will do. That’s a rate of d = c/k (in this case 2×10⁻¹¹) years per character, so our execution time f(N) is asymptotically dN+c years. Whenever N > k, dN = c/k N > c. Therefore, dN < dN+c = f(N) < 2 dN for all N > k, and f(N) ∈ Θ(N). Q.E.D.

Community
  • 1
  • 1
Davislor
  • 14,674
  • 2
  • 34
  • 49
  • Where we have N = 13. – djechlin Dec 03 '15 at 13:41
  • But it doesn't just print "Hello world", it prints an unknown number of "It's still not time" lines. Additionally, Big O is not really used to compare the size of the input to the size of the output, it's generally used to compare the size of the input to the number of operations, or the amount of memory used. – Servy Dec 04 '15 at 14:23
  • @Servy It’s constant memory, but I was implicitly bounding the execution time. The size of the output is O(*N*) too, for an arbitrary string: the string we print when it’s time could be arbitrarily large, even in comparison to twenty years’ worth of please-wait messages. – Davislor Dec 04 '15 at 20:11
  • @Servy I’ve edited to clarify that, no, *N* here is not the size of the output. I’m not sure how I gave that impression, but I’ll remove any ambiguity. – Davislor Dec 04 '15 at 20:15
  • If you have an arbitrarily large input string and an arbitrarily large execution time that still doesn't mean that there's an O(n) relationship. The point is that increasing the input size wouldn't increase the output time by a proportional amount, and decreasing the input time wouldn't decease the execution time proportionally. That's exactly what it means to have an O(n) operation, but that clearly isn't the case here. Your answer says, "If N is the length of the string we’re printing." That pretty unambiguously says that N is the size of the output. – Servy Dec 05 '15 at 02:22
  • @Servy I am not referring to the output of the idle loop, which in any case is a constant string. I didn’t think it was ambiguous, especially after I directly told you that isn’t what I meant, but fine: I edited again to remove any possible basis for that misreading. I also expect people to have the background knowledge to know that printing a string of length *N* takes O(*N*) time. – Davislor Dec 05 '15 at 05:09
  • The output of the loop *isn't* just a constant string. It's an unknown number of constant strings. The string printed at the end is of a constant length, so printing it takes a constant amount of time; the time it takes to print that final string is not dependent on the size of the input, so you cannot in any way say that it's O(n). Changing the size of the (non-existent) input string doesn't change the number of operations, so you cannot say that there's an O(n) relationship there. – Servy Dec 05 '15 at 17:31
  • @Servy It’s a constant string because it repeats for a set period of time. But that’s irrelevant. Recall the definition of Θ. We can make *N*, in this case the length of the message we’re waiting to print, arbitrarily large! If we interpret the problem so that we print a bunch of *please wait* messages followed by an output of size *N*, then however long we wait, we can pick a message size so large that printing it out dominates the amount of time we waited. – Davislor Dec 05 '15 at 19:34
  • @Lorehead But this program doesn't do that, so you're not actually talking about the properties of this program, as asked, but talking about the properties of some *completely* different program that has no relation at all to this one. – Servy Dec 05 '15 at 19:51
  • @Servy Added a formal proof. – Davislor Dec 05 '15 at 20:17
  • @Servy You seem to have missed that I was generalizing the program so that talking about *N* is meaningful at all. – Davislor Dec 05 '15 at 20:19
  • @Lorehead So you were able to prove that a completely different program that exhibits none of the properties of the program shown has some given properties, and have therefore explained literally nothing about the properties that the question actually asks about. That's for confirming, conclusively, that your answer is entirely off topic. – Servy Dec 05 '15 at 22:46
  • 1
    So if you assume the program takes an input, when it doesn't, that the output can be arbitrarily large, when it can't, that the loop doesn't do anything, when it does, and that the output is related to the input, when it isn't, then yes, the program is linear. Of course, every single one of those assumptions is completely false, so the conclusion that you've drawn from those doesn't hold. If you're able to demonstrate your point without making false assumptions, then it'd mean something. – Servy Dec 05 '15 at 22:49
  • In order to talk about *N* at all, we need to generalize something as the input, and defining the string `hello, world!` as the input which is allowed to vary and *N* as its length is the natural choice based on the description. I’m sorry that point flew over your head and you misunderstood me as saying something different. I don’t think further discussion would be productive. – Davislor Dec 05 '15 at 22:52
  • I didn't fail to understand it. You're absolutely right that it doesn't make since to talk about N without defining it, but you *defined it as something that doesn't exist*. The whole point here is that *there is no input*. You're defining an entirely different program, with entirely different behavior that the one being asked about. The program that you have defined *is not the same as the one being asked about*. – Servy Dec 06 '15 at 00:35
  • @Servy You did. You initially thought I was talking about the number of characters the program outputs. At least we cleared that up. Look, I’m sorry you reject the idea of generalizing the algorithm at all: if we make `hello, world!` an example rather than the only thing it ever prints, discussion of its time complexity has some meaning, and we can address the actual point of the question, which is whether adding a big constant to the run time changes the order of its time complexity. (No.) Yelling at each other serves no purpose. I hope you reconsider your downvote. – Davislor Dec 06 '15 at 01:35
  • I'm in no way saying that adding a large constant time changes the asymptotic complexity, rather I'm arguing that the meaningful portion of the algorithm is neither constant, nor dependent on the size of the input, thus it can neither be ignored, nor related to the size of the input, thus rendering Big O as a useless tool for measuring the asymptotic complexity of this program. Of course if you *completely* change the program in question, *then* you can potentially meaningfully relate the size of the input and the number of relevant operation performed, if you don't, you can't. – Servy Dec 06 '15 at 22:30
  • @Servy I think we both understand the points the other is making now. Thanks for the feedback, which helped improve my post. – Davislor Dec 07 '15 at 00:25
3

Most people seem to be missing two very important things.

  1. The program does have an input. It is the hard-coded date/time against which the system time is being compared. The inputs are under the control of the person running the algorithm,and the system time is not. The only thing the person running this program can control is the date/time they've hard-coded into the comparison.

  2. The program varies based on the input value, but not the size of the input set, which is what big-O notation is concerned with.

Therefore, it is indeterminate, and the best 'big-O' notation for this program would probably be O(null), or possibly O(NaN).

Theo Brinkman
  • 291
  • 2
  • 10
  • 2
    (2) is flat-out wrong. Usually the "length of the input" is considered. For a list or array of fixed-size objects (like integers) it will indeed be the size of the set. To factor a number like 1395195191600333 it will be the length of its binary (or decimal, etc.) representation i.e. the number of digits. As stated your definition in (2) prohibits using big-O to discuss the complexity of "findPrimeFactors(int num)", which most every cryptographer will object to. – djechlin Dec 04 '15 at 23:25
  • I literally just got the notification that you commented on my answer. But your objection to (2) relies on altering the nature of the 'input'. There is no big-O notation for 'while x < y'. Meanwhile "the complexity of 'findPrimeFactors(int num)'" is entirely related to the *implementation* of that function, however you gave an example where the input set is the set of binary digits making up an integer value. None of that applies to the example in the original question, and is therefore immaterial to my answer. – Theo Brinkman Mar 23 '22 at 15:10
3

At this point in time, yes

This algorithm has an implicit input, namely the time that the program is started at. The execution time will vary linearly1 depending on when it is started. During the year 2035 and after, the while loop immediately exits and the program terminates after constant operations2. So it could be said that the runtime is O(max(2035 - start year, 1))3. But since our start year has a minimum value, the algorithm will never take more than 20 years to execute (i.e. a constant value).

You can make your algorithm more in keeping with your intent by defining DateTime TwentyYearsLater = DateTime.Now + new TimeSpan(365*20,0,0,0);4

1 This holds for the more technical sense of execution time measured as number of operations because there is a maximum number of operations per time unit.
2 Assuming fetching DateTime.Now is a constant operation, which is reasonable.
3 I'm somewhat abusing big O notation here because this is a decreasing function with respect to start year, but we could easily rectify this by expressing it in terms of years prior to 2035.
4 Then the algorithm no longer depends on the implicit input of the start time, but that's of no consequence.

Community
  • 1
  • 1
panofsteel
  • 195
  • 9
0

I'd argue that this is O(n). using http://www.cforcoding.com/2009/07/plain-english-explanation-of-big-o.html as a reference.

What is Big O?

Big O notation seeks to describe the relative complexity of an algorithm by reducing the growth rate to the key factors when the key factor tends towards infinity.

and

The best example of Big-O I can think of is doing arithmetic. The basic arithmetic operations we learnt in school were:

addition; subtraction; multiplication; and division. Each of these is an operation or a problem. A method of solving these is called an algorithm.

For your example,

given the input of n = 20 (with units years).

the algorithm is a mathematical function f(). where f() happens to be wait for n years, with 'debug' strings in between. The scale factor is 1. f() can be reduced/or increased by changing this scale factor.

for this case, the output is also 20 (changing the input changes the output linearly).

essentially the function is

f(n) = n*1 = n
    if  n = 20, then 
f(20) = 20 
Angel Koh
  • 12,479
  • 7
  • 64
  • 91