What does "in-place" mean exactly? - algorithm

I know there are other questions about the meaning of the "in-place" algorithm but my question is a bit different. I know it means that the algorithm changes the original input data instead of allocating new space for the output. But what I'm not sure about is whether the auxiliary memory counts. Namely:
if an algorithm allocates some additional memory in order to compute the result
if an algorithm has a non-constant number of recursive calls which take up additional space on the stack

In-place normally implies sub-linear additional space. This isn't necessarily part of the meaning of the term. It's just that an in-place algorithm that uses linear or greater space is not interesting. If you're going to allocate O(n) space to compute an output in the same space as the input, you could have equally easily produced the output in fresh memory and maintained the same memory bound. The value of computing in-place has been lost.
Wikipedia goes farther and says the amount of extra storage is constant. However, an algorithm (say mergesort) that uses log(n) additional space to write the output over the input is still in-place in usages I have seen.

I can't think of any in-place algorithm that doesn't need some additional memory. Whether an algorithm is "in-place" is characterized by the following:
in-place: To perform an algorithm on an input of size Θ(f(n)) using o(f(n)) extra space by mutating the input into the output.
Take for example an in-place implementation of the "Insertion Sort" sorting algorithm. The input is a list of numbers taking Θ(n) space. It takes Θ(n2) time to run in the worst case, but it only takes O(1) space. If you were to not do the sort in-place, you would be required to use at least Ω(n) space, because the output needs to be a list of n numbers.

Related

Space Complexity Vs Auxiliary Space Complexity

I'm kind of confused between these two terms as for example - the Auxiliary space of merge sort, heapsort and insertion sort is O(1) whereas Space complexity of merge sort, insertion sort, heapsort is O(n).
So, if someone asks me what's the Space complexity of merge sort, heapsort or insertion sort then what should I tell them O(1) or O(n)?
Also, note in case of selection sort, I've read it's Space Complexity is O(1) which is auxiliary space.
So, is it possible the algorithm which uses "in-place computation" and for those algorithms we mention auxiliary space?
Furthermore I know -
Space Complexity = Auxiliary Space + space taken by also wrt input.
Kindly help, thank you!
When looking at O(n), you need to understand what it means. It is the "IN THE WORST CASE IT WILL BE N". I use http://bigocheatsheet.com/ as a point of reference.
When you are looking at space complexity, they will want to know how much is going to be held in memory at a given point in time. This does not include the base structure. They will want to know the amount of additional space the sort will need in order to execute accordingly. The difference is structures which need to be entirely in memory.
In regards to your first question, it will at MOST take up N space, but the total amount held in memory for your operations would be O(1).
When you are dealing with SORTS, as you listed above, they are mostly only O(1) because they really just need tmp space to hold things while swaps occur. Datastructures themselves require MORE space because they have a particular size in memory for whatever manipulations need to occur.
I use the linked website a LOT..

Space complexity Merge sort, Insertion sort explained (for dummies)

I was wondering if someone could explain to me how the space complexity of both these algorithms work. I have done readings on it but they seem to be contradictive, if I understand correctly.
I'm for example interested in how a linked list would affect the space complexity and this question says it makes it faster?;
Why is mergesort space complexity O(log(n)) with linked lists?
This question however says it shouldn't matter; Merge Sort Time and Space Complexity
Now I'm a bit new to programming and would like to understand the theory a bit better so dummie language would be appreciated.
The total space complexity of merge sort is O(n) since you have to store the elements somewhere. Nevertheless, there can indeed be a difference in additional space complexity, between an array implementation and a linked-list implementation.
Note that you can implement an iterative version that only requires O(1) additional space. However, if I remember correclty, this version would perform horribly.
In the conventional recursive version, you need to account for the stack frames. That alone gives a O(log n) additional space requirement.
In a linked-list implementation, you can perform merges in-place without any auxiliary memory. Hence the O(log n) additional space complexity.
In an array implementation, merges require auxiliary memory (likely an auxiliary array), and the last merge requires the same amount of memory as that used to store the elements in the first place. Hence the O(n) additional space complexity.
Keep in mind that space complexity tells you how the space needs of the algorithm grows as the input size grows. There are details that space complexity ignores. Namely, the sizes of a stack frame and an element are probably different, and a linked-list takes up more space than an array because of the links (the references). That last detail is important for small elements, since the additional space requirement of the array implementation is likely less than the additional space taken by the links of the linked-list implementation.
Why is merge sort space complexity O(log(n)) with linked lists?
This is only true for top down merge sort for linked lists, where O(log2(n)) stack space is used due to recursion. For bottom up merge sort for linked lists, space complexity is O(1) (constant space). One example of an optimized bottom up merge sort for a linked list uses a small (26 to 32) array of pointers or references to to the first nodes of list. This would still be considered O(1) space complexity. Link to pseudo code in wiki article:
https://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation_using_lists

What is O(1) space complexity?

I am having a hard time understanding what is O(1) space complexity. I understand that it means that the space required by the algorithm does not grow with the input or the size of the data on which we are using the algorithm. But what does it exactly mean?
If we use an algorithm on a linked list say 1->2->3->4, to traverse the list to reach "3" we declare a temporary pointer. And traverse the list until we reach 3. Does this mean we still have O(1) extra space? Or does it mean something completely different. I am sorry if this does not make sense at all. I am a bit confused.
To answer your question, if you have a traversal algorithm for traversing the list which allocate a single pointer to do so, the traversal algorithms is considered to be of O(1) space complexity. Additionally, let's say that traversal algorithm needs not 1 but 1000 pointers, the space complexity is still considered to be O(1).
However, if let's say for some reason the algorithm needs to allocate 'N' pointers when traversing a list of size N, i.e., it needs to allocate 3 pointers for traversing a list of 3 elements, 10 pointers for a list of 10 elements, 1000 pointers for a list of 1000 elements and so on, then the algorithm is considered to have a space complexity of O(N). This is true even when 'N' is very small, eg., N=1.
To summarise the two examples above, O(1) denotes constant space use: the algorithm allocates the same number of pointers irrespective to the list size. In contrast, O(N) denotes linear space use: the algorithm space use grows together with respect to the input size.
It is just the amount of memory used by a program. the amount of computer memory that is the main memory required by the algorithm to complete its execution with respect to the input size.
Space Complexity(s(P)) of an algorithm is the total space taken by the algorithm to complete its execution with respect to the input size. It includes both Constant space and Auxiliary space.
S(P) = Constant space + Auxiliary space
Constant space is the one that is fixed for that algorithm, generally equals to space used by input and local variables. Auxiliary Space is the extra/temporary space used by an algorithm.
Let's say I create some data structure with a fixed size, and no matter what I do to the data structure, it will always have the same fixed size. Operations performed on this data structure are therefore O(1).
An example, let's say I have an array of fixed size 100. Any operation I do, whether that is reading from the array or updating an element, that operation will be O(1) on the array. The array's size (and thus the amount of memory it's using) is not changing.
Another example, let's say I have a LinkedList to which I add elements to it. Every time I add an element to the LinkedList, that is a O(N) operation to the list because I am growing the amount of memory required to hold all of it's elements together.
Hope this helps!

Differences between time complexity and space complexity?

I have seen that in most cases the time complexity is related to the space complexity and vice versa. For example in an array traversal:
for i=1 to length(v)
print (v[i])
endfor
Here it is easy to see that the algorithm complexity in terms of time is O(n), but it looks to me like the space complexity is also n (also represented as O(n)?).
My question: is it possible that an algorithm has different time complexity than space complexity?
The time and space complexities are not related to each other. They are used to describe how much space/time your algorithm takes based on the input.
For example when the algorithm has space complexity of:
O(1) - constant - the algorithm uses a fixed (small) amount of space which doesn't depend on the input. For every size of the input the algorithm will take the same (constant) amount of space. This is the case in your example as the input is not taken into account and what matters is the time/space of the print command.
O(n), O(n^2), O(log(n))... - these indicate that you create additional objects based on the length of your input. For example creating a copy of each object of v storing it in an array and printing it after that takes O(n) space as you create n additional objects.
In contrast the time complexity describes how much time your algorithm consumes based on the length of the input. Again:
O(1) - no matter how big is the input it always takes a constant time - for example only one instruction. Like
function(list l) {
print("i got a list");
}
O(n), O(n^2), O(log(n)) - again it's based on the length of the input. For example
function(list l) {
for (node in l) {
print(node);
}
}
Note that both last examples take O(1) space as you don't create anything. Compare them to
function(list l) {
list c;
for (node in l) {
c.add(node);
}
}
which takes O(n) space because you create a new list whose size depends on the size of the input in linear way.
Your example shows that time and space complexity might be different. It takes v.length * print.time to print all the elements. But the space is always the same - O(1) because you don't create additional objects. So, yes, it is possible that an algorithm has different time and space complexity, as they are not dependent on each other.
Time and Space complexity are different aspects of calculating the efficiency of an algorithm.
Time complexity deals with finding out how the computational time of
an algorithm changes with the change in size of the input.
On the other hand, space complexity deals with finding out how much
(extra)space would be required by the algorithm with change in the
input size.
To calculate time complexity of the algorithm the best way is to check if we increase in the size of the input, will the number of comparison(or computational steps) also increase and to calculate space complexity the best bet is to see additional memory requirement of the algorithm also changes with the change in the size of the input.
A good example could be of Bubble sort.
Lets say you tried to sort an array of 5 elements.
In the first pass you will compare 1st element with next 4 elements. In second pass you will compare 2nd element with next 3 elements and you will continue this procedure till you fully exhaust the list.
Now what will happen if you try to sort 10 elements. In this case you will start with comparing comparing 1st element with next 9 elements, then 2nd with next 8 elements and so on. In other words if you have N element array you will start of by comparing 1st element with N-1 elements, then 2nd element with N-2 elements and so on. This results in O(N^2) time complexity.
But what about size. When you sorted 5 element or 10 element array did you use any additional buffer or memory space. You might say Yes, I did use a temporary variable to make the swap. But did the number of variables changed when you increased the size of array from 5 to 10. No, Irrespective of what is the size of the input you will always use a single variable to do the swap. Well, this means that the size of the input has nothing to do with the additional space you will require resulting in O(1) or constant space complexity.
Now as an exercise for you, research about the time and space complexity of merge sort
First of all, the space complexity of this loop is O(1) (the input is customarily not included when calculating how much storage is required by an algorithm).
So the question that I have is if its possible that an algorithm has different time complexity from space complexity?
Yes, it is. In general, the time and the space complexity of an algorithm are not related to each other.
Sometimes one can be increased at the expense of the other. This is called space-time tradeoff.
There is a well know relation between time and space complexity.
First of all, time is an obvious bound to space consumption: in time t
you cannot reach more than O(t) memory cells. This is usually expressed
by the inclusion
DTime(f) ⊆ DSpace(f)
where DTime(f) and DSpace(f) are the set of languages
recognizable by a deterministic Turing machine in time
(respectively, space) O(f). That is to say that if a problem can
be solved in time O(f), then it can also be solved in space O(f).
Less evident is the fact that space provides a bound to time. Suppose
that, on an input of size n, you have at your disposal f(n) memory cells,
comprising registers, caches and everything. After having written these cells
in all possible ways you may eventually stop your computation,
since otherwise you would reenter a configuration you
already went through, starting to loop. Now, on a binary alphabet,
f(n) cells can be written in 2^f(n) different ways, that gives our
time upper bound: either the computation will stop within this bound,
or you may force termination, since the computation will never stop.
This is usually expressed in the inclusion
DSpace(f) ⊆ Dtime(2^(cf))
for some constant c. the reason of the constant c is that if L is in DSpace(f) you only
know that it will be recognized in Space O(f), while in the previous
reasoning, f was an actual bound.
The above relations are subsumed by stronger versions, involving
nondeterministic models of computation, that is the way they are
frequently stated in textbooks (see e.g. Theorem 7.4 in Computational
Complexity by Papadimitriou).
Yes, this is definitely possible. For example, sorting n real numbers requires O(n) space, but O(n log n) time. It is true that space complexity is always a lowerbound on time complexity, as the time to initialize the space is included in the running time.
Sometimes yes they are related, and sometimes no they are not related,
actually we sometimes use more space to get faster algorithms as in dynamic programming https://www.codechef.com/wiki/tutorial-dynamic-programming
dynamic programming uses memoization or bottom-up, the first technique use the memory to remember the repeated solutions so the algorithm needs not to recompute it rather just get them from a list of solutions. and the bottom-up approach start with the small solutions and build upon to reach the final solution.
Here two simple examples, one shows relation between time and space, and the other show no relation:
suppose we want to find the summation of all integers from 1 to a given n integer:
code1:
sum=0
for i=1 to n
sum=sum+1
print sum
This code used only 6 bytes from memory i=>2,n=>2 and sum=>2 bytes
therefore time complexity is O(n), while space complexity is O(1)
code2:
array a[n]
a[1]=1
for i=2 to n
a[i]=a[i-1]+i
print a[n]
This code used at least n*2 bytes from the memory for the array
therefore space complexity is O(n) and time complexity is also O(n)
The way in which the amount of storage space required by an algorithm varies with the size of the problem it is solving. Space complexity is normally expressed as an order of magnitude, e.g. O(N^2) means that if the size of the problem (N) doubles then four times as much working storage will be needed.
space complexity is the total amount of memory space used by an algorithm/program, including input value execution space. whereas the time complexity is the number of operations an algorithm performs to complete its task. These are two different concept, a single algorithm can of low time complexity but still can take up a lot of memory for example hashmaps take more memory than array but take less time.

Sorting in place

What is meant by to "sort in place"?
The idea of an in-place algorithm isn't unique to sorting, but sorting is probably the most important case, or at least the most well-known. The idea is about space efficiency - using the minimum amount of RAM, hard disk or other storage that you can get away with. This was especially relevant going back a few decades, when hardware was much more limited.
The idea is to produce an output in the same memory space that contains the input by successively transforming that data until the output is produced. This avoids the need to use twice the storage - one area for the input and an equal-sized area for the output.
Sorting is a fairly obvious case for this because sorting can be done by repeatedly exchanging items - sorting only re-arranges items. Exchanges aren't the only approach - the Insertion Sort, for example, uses a slightly different approach which is equivalent to doing a run of exchanges but faster.
Another example is matrix transposition - again, this can be implemented by exchanging items. Adding two very large numbers can also be done in-place (the result replacing one of the inputs) by starting at the least significant digit and propogating carries upwards.
Getting back to sorting, the advantages to re-arranging "in place" get even more obvious when you think of stacks of punched cards - it's preferable to avoid copying punched cards just to sort them.
Some algorithms for sorting allow this style of in-place operation whereas others don't.
However, all algorithms require some additional storage for working variables. If the goal is simply to produce the output by successively modifying the input, it's fairly easy to define algorithms that do that by reserving a huge chunk of memory, using that to produce some auxiliary data structure, then using that to guide those modifications. You're still producing the output by transforming the input "in place", but you're defeating the whole point of the exercise - you're not being space-efficient.
For that reason, the normal definition of an in-place definition requires that you achieve some standard of space efficiency. It's absolutely not acceptable to use extra space proportional to the input (that is, O(n) extra space) and still call your algorithm "in-place".
The Wikipedia page on in-place algorithms currently claims that an in-place algorithm can only use a constant amount - O(1) - of extra space.
In computer science, an in-place algorithm (or in Latin in situ) is an algorithm which transforms input using a data structure with a small, constant amount of extra storage space.
There are some technicalities specified in the In Computational Complexity section, but the conclusion is still that e.g. Quicksort requires O(log n) space (true) and therefore is not in-place (which I believe is false).
O(log n) is much smaller than O(n) - for example the base 2 log of 16,777,216 is 24.
Quicksort and heapsort are both normally considered in-place, and heapsort can be implemented with O(1) extra space (I was mistaken about this earlier). Mergesort is more difficult to implement in-place, but the out-of-place version is very cache-friendly - I suspect real-world implementations accept the O(n) space overhead - RAM is cheap but memory bandwidth is a major bottleneck, so trading memory for cache-efficiency and speed is often a good deal.
[EDIT When I wrote the above, I assume I was thinking of in-place merge-sorting of an array. In-place merge-sorting of a linked list is very simple. The key difference is in the merge algorithm - doing a merge of two linked lists with no copying or reallocation is easy, doing the same with two sub-arrays of a larger array (and without O(n) auxiliary storage) AFAIK isn't.]
Quicksort is also cache-efficient, even in-place, but can be disqualified as an in-place algorithm by appealing to its worst-case behaviour. There is a degenerate case (in a non-randomized version, typically when the input is already sorted) where the run-time is O(n^2) rather than the expected O(n log n). In this case the extra space requirement is also increased to O(n). However, for large datasets and with some basic precautions (mainly randomized pivot selection) this worst-case behaviour becomes absurdly unlikely.
My personal view is that O(log n) extra space is acceptable for in-place algorithms - it's not cheating as it doesn't defeat the original point of working in-place.
However, my opinion is of course just my opinion.
One extra note - sometimes, people will call a function in-place simply because it has a single parameter for both the input and the output. It doesn't necessarily follow that the function was space efficient, that the result was produced by transforming the input, or even that the parameter still references the same area of memory. This usage isn't correct (or so the prescriptivists will claim), though it's common enough that it's best to be aware but not get stressed about it.
In-place sorting means sorting without any extra space requirement. According to wiki , it says
an in-place algorithm is an algorithm which transforms input using a data structure with a small, constant amount of extra storage space.
Quicksort is one example of In-Place Sorting.
I don't think these terms are closely related:
Sort in place means to sort an existing list by modifying the element order directly within the list. The opposite is leaving the original list as is and create a new list with the elements in order.
Natural ordering is a term that describes how complete objects can somehow be ordered. You can for instance say that 0 is lower that 1 (natural ordering for integers) or that A is before B in alphabetical order (natural ordering for strings). You can hardly say though that Bob is greater or lower than Alice in general as it heavily depends on specific attributes (alphabetically by name, by age, by income, ...). Therefore there is no natural ordering for people.
I'm not sure these concepts are similar enough to compare as suggested. Yes, they both involve sorting, but one is about a sort ordering that is human understandable (natural) and the other defines an algorithm for efficient sorting in terms of memory by overwriting into the existing structure instead of using an additional data structure (like a bubble sort)
it can be done by using swap function , instead of making a whole new structure , we implement that algorithm without even knowing it's name :D

Resources