Reversing the Order Using Divide and Conquer [duplicate] - java

A question was asked to me today and I do not believe it is possible, but I could be wrong or am over thinking it. How can you reverse an array without using iteration in C?
My thought is that it's impossible because of the fact that the array can be any size and that no C program can be expressed with that kind of support in mind without using some form of iteration.

The answer to your question is that, yes, it is possible to reverse an array without iteration. The phrasing of the question itself might be ambiguous, however, the spirit of the question is obvious: a recursive algorithm can be used; and there is no ambiguity at all as to the meaning of recursive in this sense.
If, in an interview situation with a top-flight company, you were asked this question, then the following pseudo-code would be sufficient to demonstrate you truly understood what is meant by recursion:
function reverse(array)
if (length(array) < 2) then
return array
left_half = reverse(array[0 .. (n/2)-1])
right_half = reverse(array[(n/2) .. (n-1)])
return right_half + left_half
end
For example, if we have an array of 16 elements containing the first 16 letters of the Latin Alphabet, [A]..[P], the above reverse algorithm could be visualised as follows:
Original Input
1. ABCDEFHGIJKLMNOP Recurse
2. ABCDEFGH IJKLMNOP Recurse
3. ABCD EFGH IJKL MNOP Recurse
4. AB CD EF GH IJ KL MN OP Recurse
5. A B C D E F G H I J K L M N O P Terminate
6. BA DC FE HG JI LK NM PO Reverse
7. DCBA HGFE LKJI PONM Reverse
8. HGFEDCBA PONMLKJI Reverse
9. PONMLKJIHGFEDCBA Reverse
Reversed Output
Any problem that is solved with a recursive algorithm follows the Divide and Conquer paradigm, namely that:
The problem is divided into [two or more] sub-problems where each sub-problem is smaller than, but can be solved in a similar manner to, the original problem (Divide).
The problem is divided into [two or more] sub-problems where each sub-problem is independent and can be solved either recursively, or in a straightforward manner if small enough (Conquer).
The problem is divided into [two or more] sub-problems where the results of those sub-problems are combined to give the solution for the original problem (Combine).
The pseudo-code above for reversing an array strictly satisfies the above criteria. Thus, it can be considered a recursive algorithm and we can state without any doubt that reversing an array can be done without using iteration.
ADDITIONAL BACKGROUND INFORMATION
The difference between Iteration, Recursive Implementations and Recursive Algorithms
It is a common misunderstanding that a recursive implementation means an algorithm is recursive. They are not equivalent. Here is a definitive explanation as to why, including a detailed explanation of the above solution.
What are Iteration and Recursion?
Back in 1990, three of the most respected scholars of modern algorithm analysis in the field of computer science, Thomas H. Cormen, Charles E. Leiserson and Ronald L. Rivest, released their much acclaimed Introduction to Algorithms. In this book, which represented the coming together of over 200 respected texts in their own right, and which for over 20 years has been used as the first and only text for teaching algorithms in most of the top-flight universities around the world, Mssrs. Cormen, Leiserson, and Rivest were explicit about what constitutes Iterating and what constitutes Recursing.
In their analysis and comparison of two classic sorting algorithms, Insertion Sort and Merge Sort, they explain the fundamental properties of iterative and recursive algorithms (sometimes termed incremental algorithms to disambiguate when the classical mathematical notion of iteration is being used in the same context).
Firstly, Insertion Sort is classified as an Iterative algorithm, with its behaviour summarised as follows:
Having sorted the subarray A[1..j-1], we insert the single item A[j] into its proper place, yielding the sorted array A[1..j].
Source: Introduction to Algorithms - Cormen, Leisersen, Rivest, 1990 MIT Press
This statement classifies an Iterative algorithm as one that relies on the result or state of a previous execution ("iteration") of the algorithm, and that such results or state information are then used to solve the problem for the current iteration.
Merge Sort, on the other hand, is classified as a recursive algorithm. A recursive algorithm conforms to a processing paradigm called Divide and Conquer which is a set of three fundamental criteria that differentiate the operation of recursive algorithms from non-recursive algorithms. An algorithm can be considered recursive if, during the processing of a given problem:
The problem is divided into [two or more] sub-problems where each sub-problem is smaller than, but can be solved in a similar manner to, the original problem (Divide).
The problem is divided into [two or more] sub-problems where each sub-problem can be solved either recursively, or in a straightforward manner if small enough (Conquer).
The problem is divided into [two or more] sub-problems where the results of those sub-problems are combined to give the solution for the original problem (Combine).
Reference: Introduction to Algorithms - Cormen, Leisersen, Rivest, 1990 MIT Press
Both Iterative algorithms and Recursive algorithms continue their work until a terminating condition has been reached. The terminating condition in Insertion Sort is that the j'th item has been properly placed in the array A[1..j]. The terminating condition in a Divide and Conquer algorithm is when Criteria 2 of the paradigm "bottoms out", that is, the size of a sub-problem reaches a sufficiently small size that it can be solved without further sub-division.
It's important to note that the Divide and Conquer paradigm requires that sub-problems must be solvable in a similar manner to the original problem to allow recursion. As the original problem is a standalone problem, with no outside dependencies, it follows that the sub-problems must also be solvable as if they were standalone problems with no outside dependencies, particularly on other sub-problems. This means that sub-problems in Divide and Conquer algorithms should be naturally independent.
Conversely, it is equally important to note that input to iterative algorithms is based on previous iterations of the algorithm, and so must be considered and processed in order. This creates dependencies between iterations which prevent the algorithm dividing the problem into sub-problems that can be recursively solved. In Insertion Sort, for example, you cannot divide the items A[1..j] into two sub-sets such that the sorted position in the array of A[j] gets decided before all items A[1..j-1] have been placed, as the real proper position of A[j] may move while any of A[1..j-1] are being themselves placed.
Recursive Algorithms vs. Recursive Implementations
The general misunderstanding of the term recursion stems from the fact there is a common and wrong assumption that a recursive implementation for some task automatically means that the problem has been solved with a recursive algorithm. Recursive algorithms are not the same as recursive implementations and never have been.
A recursive implementation involves a function, or group of functions, that eventually call themselves in order to solve a sub-portion of the overall task in exactly the same manner that the overall task is being solved in. It happens that recursive algorithms (i.e, those that satisfy the Divide and Conquer paradigm), lend themselves well to recursive implementations. However, recursive algorithms can be implemented using just iterative constructs like for(...) and while(...) as all algorithms, including recursive algorithms, end up performing some task repeatedly in order to get a result.
Other contributors to this post have demonstrated perfectly that iterative algorithms can be implemented using a recursive function. In fact, recursive implementations are possible for everything that involves iterating until some terminating condition has been met. Recursive implementations where there is no Divide or Combine steps in the underlying algorithm are equivalent to iterative implementations with a standard terminating condition.
Taking Insertion Sort as an example, we already know (and it has been proven) that Insertion Sort is an iterative algorithm. However, this does not prevent a recursive implementation of Insertion Sort. In fact, a recursive implementation can be created very easily as follows:
function insertionSort(array)
if (length(array) == 1)
return array
end
itemToSort = array[length(array)]
array = insertionSort(array[1 .. (length(array)-1)])
find position of itemToSort in array
insert itemToSort into array
return array
end
As can be seen, the implementation is recursive. However, Insertion Sort is an iterative algorithm and this we know. So, how do we know that even by using the above recursive implementation that our Insertion Sort algorithm hasn't become recursive? Let us apply the three criteria of the Divide and Conquer paradigm to our algorithm and check.
The problem is divided into [two or more] sub-problems where each sub-problem is smaller than, but can be solved in a similar manner to, the original problem.
YES: Excluding an array of length one, the method for inserting an item A[j] into its proper place in the array is identical to the method used to insert all previous items A[1..j-1] into the array.
The problem is divided into [two or more] sub-problems where each sub-problem is independent and can be solved either recursively, or in a straightforward manner if small enough.
NO: Proper placement of item A[j] is wholly dependent on the array containing A[1..j-1] items and those items being sorted. Therefore, item A[j] (called itemToSort) is not put in the array before the rest of the array is processed.
The problem is divided into [two or more] sub-problems where the results of those sub-problems are combined to give the solution for the original problem.
NO: Being an iterative algorithm, only one item A[j] can be properly placed in any given iteration. The space A[1..j] is not divided into sub-problems where A[1], A[2]...A[j] are all properly placed independently and then all these properly placed elements combined to give the sorted array.
Clearly, our recursive implementation has not made the Insertion Sort algorithm recursive in nature. In fact, the recursion in the implementation in this case is acting as flow control, allowing the iteration to continue until the terminating condition has been met. Therefore, using a recursive implementation did not change our algorithm into a recursive algorithm.
Reversing an Array Without Using an Iterative Algorithm
So now that we understand what makes an algorithm iterative, and what makes one recursive, how is it that we can reverse an array "without using iteration"?
There are two ways to reverse an array. Both methods require you to know the length of the array in advance. The iteration algorithm is favoured for its efficiency and its pseudo-code looks as follows:
function reverse(array)
for each index i = 0 to (length(array) / 2 - 1)
swap array[i] with array[length(array) - i]
next
end
This is a purely iterative algorithm. Let us examine why we can come to this conclusion by comparing it to the Divide and Conquer paradigm which determines an algorithm's recursiveness.
The problem is divided into [two or more] sub-problems where each sub-problem is smaller than, but can be solved in a similar manner to, the original problem.
YES: Reversal of the array is broken down to its finest granularity, elements, and processing for each element is identical to all other processed elements.
The problem is divided into [two or more] sub-problems where each sub-problem is independent and can be solved either recursively, or in a straightforward manner if small enough.
YES: Reversal of element i in the array is possible without requiring that element (i + 1) (for example) has been reversed or not. Furthermore, reversal of element i in the array does not require the results of other element reversals in order to be able to complete.
The problem is divided into [two or more] sub-problems where the results of those sub-problems are combined to give the solution for the original problem.
NO: Being an iterative algorithm, only one calculation stage is performed at every algorithm step. It does not divide problems into subproblems and there is no merge of the results of two or more sub-problems to get a result.
The above analsys of our first algorithm above confirmed that it does not fit the Divide and Conquer paradigm, and therefore cannot be considered to be a recursive algorithm. However, as both criteria (1) and criteria (2) were satisifed, it is apparent that a recursive algorithm could be possible.
The key lies in the fact that the sub-problems in our iterative solution are of the smallest possible granularity (i.e. elements). By dividing the problem into successively smaller and smaller sub-problems (instead of going for the finest granularity from the start), and then merging the results of the sub-problems, the algorithm can be made recursive.
For example, if we have an array of 16 elements containing the first 16 letters of the Latin Alphabet (A..P), a recursive algorithm would visually look as follows:
Original Input
1. ABCDEFHGIJKLMNOP Divide
2. ABCDEFGH IJKLMNOP Divide
3. ABCD EFGH IJKL MNOP Divide
4. AB CD EF GH IJ KL MN OP Divide
5. A B C D E F G H I J K L M N O P Terminate
6. BA DC FE HG JI LK NM PO Conquer (Reverse) and Merge
7. DCBA HGFE LKJI PONM Conquer (Reverse) and Merge
8. HGFEDCBA PONMLKJI Conquer (Reverse) and Merge
9. PONMLKJIHGFEDCBA Conquer (Reverse) and Merge
Reversed Output
From top level, the 16 elements are progressively broken into smaller sub-problem sizes of exactly equal size (levels 1 to 4) until we reach the finest granularity of sub-problem; unit-length arrays in forward order (step 5, individual elements). At this point, our 16 array elements still appear to be in order. However, they are at the same time also reversed as a single element array is also a reversed array in its own right. The results of the single-element arrays are then merged to get eight reversed arrays of length two (step 6), then merged again to get four reversed arrays of length four (step 7), and so on until our original array has been reconstructed in reverse (steps 6 to 9).
The pseudo-code for the recursive algorithm to reverse an array looks as follows:
function reverse(array)
/* check terminating condition. all single elements are also reversed
* arrays of unit length.
*/
if (length(array) < 2) then
return array
/* divide problem in two equal sub-problems. we process the sub-problems
* in reverse order so that when combined the array has been reversed.
*/
return reverse(array[(n/2) .. (n-1)]) + reverse(array[0 .. ((n/2)-1)])
end
As you can see, the algorithm breaks the problem into sub-problems until it reaches the finest granularity of sub-problem that gives an instant result. It then reverses the results while they are being merged to give a reversed result array. Although we think that this algorithm is recursive, let us apply the three critera for Divide and Conquer algorithms to confirm.
The problem is divided into [two or more] sub-problems where each sub-problem is smaller than, but can be solved in a similar manner to, the original problem.
YES: Reversing the array at level one can be done using exactly the same algorithm as at level 2, 3, 4, or five.
The problem is divided into [two or more] sub-problems where each sub-problem is independent and can be solved either recursively, or in a straightforward manner if small enough.
YES: Every sub-problem that is not unit length is solved by splitting the problem into two independent sub-arrays and recursively reversing those sub-arrays. Unit length arrays, the smallest arrays possible, are themselves reversed so providing a terminating condition and a guaranteed first set of combine results.
The problem is divided into [two or more] sub-problems where the results of those sub-problems are combined to give the solution for the original problem.
YES: Every problem at levels 6, 7, 8, and 9 are composed only of results from the level immediately above; i.e. of their sub-problems. Reversal of the array at each level results in a reversed result overall.
As can be seen, our recursive algorithm passed the three criteria for the Divide and Conquer paradigm and so can be considered a truly recursive algorithm. Therefore, it is possible to reverse an array without using an iterative algorithm.
It is interesting to note that our original iterative algorithm for array reversal can be implemented using a recursive function. The pseudo code for such an implementation is as follows:
function reverse(array)
if length(array) < 2
return
end
swap array[0] and array[n-1]
reverse(array[1..(n-1)])
end
This is similar to solutions proposed by other posters. This is a recursive implementation as the defined function eventually calls itself to repeatedly perform the same task over all the elements in the array. However, this does not make the algorithm recursive, as there is no division of the problems into sub-problems, and there is no merging of the results of sub-problems to give the final result. In this case, the recursion is simply being used as a flow-control construct, and algorithmically the overall result can be proved to be performing the same sequence of steps, in exactly the same order, as the original iterative algorithm that was proposed for the solution.
That is the difference between an Iterative Algorithm, a Recursive Algorithm, and a Recursive Implementation.

As people have said in the comments, it depends on the definition of iteration.
Case 1. Iteration as a programming style, different from recursion
If one takes recursion (simply) as an alternative to iteration, then the recursive solution presented by Kalai is the right answer.
Case 2. Iteration as lower bound linear time
If one takes iteration as "examining each element," then the question becomes one of whether array reversal requires linear time or can be done in sublinear time.
To show there is no sublinear algorithm for array reversal, consider an array with n elements. Assume an algorithm A exists for reversal which does not need to read each element. Then there exists an element a[i] for some i in 0..n-1 that the algorithm never reads, yet is still able to correctly reverse the array. (EDIT: We must exclude the middle element of an odd-length array -- see the comments below from this range -- see the comments below -- but this does not impact whether the algorithm is linear or sublinear in the asymptotic case.)
Since the algorithm never reads element a[i] we can change its value. Say we do this. Then the algorithm, having never read this value at all, will produce the same answer for reversal as it did before we changed its value. But this answer will not be correct for the new value of a[i]. Hence a correct reversal algorithm which does not at least read every input array element (save one) does not exist. Hence array reversal has a lower bound of O(n) and thus requires iteration (according to the working definition for this scenario).
(Note that this proof is only for array reversal and does not extend to algorithms that truly have sublinear implementations, like binary search and element lookup.)
Case 3. Iteration as a looping construct
If iteration is taken as "looping until a condition is met" then this translates into machine code with conditional jumps, known to require some serious compiler optimization (taking advantage of branch prediction, etc.) In this case, someone asking if there is a way to do something "without iteration" may have in mind loop unrolling (to straight line code). In this case you can in principle write straight-line (loop-free) C code. But this technique is not general; it only works if you know the size of the array beforehand. (Sorry to add this more-or-less flippant case to the answer, but I did so for completeness and because I have heard the term "iteration" used in this way, and loop unrolling is an important compiler optimization.)

Use recursive function.
void reverse(int a[],int start,int end)
{
int temp;
temp = a[start];
a[start] = a[end];
a[end] = temp;
if(start==end ||start==end-1)
return;
reverse(a, start+1, end-1);
}
Just call the above method as reverse(array,0,lengthofarray-1)

Implement a recursive function to reverse a sorted array. Ie, given the array [ 1, 2, 3, 4, 5] your
procedure should return [5, 4, 3, 2, 1].

void reverse(int a[], int start, int end )
{
std::cout<<a[end] <<std::endl;
if(end == start)
return;
reverse(a, start, end-1);
}
This looks better way as even we don't require loop to print the array values .

Here's a neat solution using recursion in a javascript function. It does not require any parameters other than the array itself.
/* Use recursion to reverse an array */
function reverse(a){
if(a.length==undefined || a.length<2){
return a;
}
b=[];
b.push(reverse(copyChop(a)));
b.push(a[0]);
return b;
/* Return a copy of an array minus the first element */
function copyChop(a){
b=a.slice(1);
return b;
}
}
Call it as follows;
reverse([1,2,3,4]);
Note that if you don't use the nested function copyChop to do the array slicing you end up with an array as the first element in your final result. Not quite sure why this should be so

#include<stdio.h>
void rev(int *a,int i,int n)
{
if(i<n/2)
{
int temp = a[i];
a[i]=a[n-i-1];
a[n-i-1]=temp;
rev(a,++i,n);
}
}
int main()
{
int array[] = {3,2,4,5,6,7,8};
int len = (sizeof(array)/sizeof(int));
rev(array,0,len);
for(int i=0;i<len;i++)
{
printf("\n array[%d]->%d",i,array[i]);
}
}

One solution could be:
#include <stdio.h>
#include <stdlib.h>
void swap(int v[], int v_start, int v_middle, int v_end) {
int *aux = calloc(v_middle - v_start, sizeof(int));
int k = 0;
for(int i = v_start; i <= v_middle; i++) {
aux[k] = v[i];
k = k + 1;
}
k = v_start;
for(int i = v_middle + 1; i <= v_end; i++) {
v[k] = v[i];
k = k + 1;
}
for(int i = 0; i <= v_middle - v_start; i++) {
v[k] = aux[i];
k = k + 1;
}
}
void divide(int v[], int v_start, int v_end) {
if(v_start < v_end) {
int v_middle = (v_start + v_start)/2;
divide(v, v_start, v_middle);
divide(v, v_middle + 1, v_end);
swap(v, v_start, v_middle, v_end);
}
}
int main() {
int v[10] = {4, 20, 12, 100, 50, 9}, n = 6;
printf("Array: \n");
for (int i = 0; i < n; i++) {
printf("%d ", v[i]);
}
printf("\n\n");
divide(v, 0, n - 1);
printf("Reversed: \n");
for (int i = 0; i < n; i++) {
printf("%d ", v[i]);
}
return 0;
}

Related

Behind the scenes of recursion? [duplicate]

Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
One of the topics that seems to come up regularly on mailing lists and online discussions is the merits (or lack thereof) of doing a Computer Science Degree. An argument that seems to come up time and again for the negative party is that they have been coding for some number of years and they have never used recursion.
So the question is:
What is recursion?
When would I use recursion?
Why don't people use recursion?
There are a number of good explanations of recursion in this thread, this answer is about why you shouldn't use it in most languages.* In the majority of major imperative language implementations (i.e. every major implementation of C, C++, Basic, Python, Ruby,Java, and C#) iteration is vastly preferable to recursion.
To see why, walk through the steps that the above languages use to call a function:
space is carved out on the stack for the function's arguments and local variables
the function's arguments are copied into this new space
control jumps to the function
the function's code runs
the function's result is copied into a return value
the stack is rewound to its previous position
control jumps back to where the function was called
Doing all of these steps takes time, usually a little bit more than it takes to iterate through a loop. However, the real problem is in step #1. When many programs start, they allocate a single chunk of memory for their stack, and when they run out of that memory (often, but not always due to recursion), the program crashes due to a stack overflow.
So in these languages recursion is slower and it makes you vulnerable to crashing. There are still some arguments for using it though. In general, code written recursively is shorter and a bit more elegant, once you know how to read it.
There is a technique that language implementers can use called tail call optimization which can eliminate some classes of stack overflow. Put succinctly: if a function's return expression is simply the result of a function call, then you don't need to add a new level onto the stack, you can reuse the current one for the function being called. Regrettably, few imperative language-implementations have tail-call optimization built in.
* I love recursion. My favorite static language doesn't use loops at all, recursion is the only way to do something repeatedly. I just don't think that recursion is generally a good idea in languages that aren't tuned for it.
** By the way Mario, the typical name for your ArrangeString function is "join", and I'd be surprised if your language of choice doesn't already have an implementation of it.
Simple english example of recursion.
A child couldn't sleep, so her mother told her a story about a little frog,
who couldn't sleep, so the frog's mother told her a story about a little bear,
who couldn't sleep, so the bear's mother told her a story about a little weasel...
who fell asleep.
...and the little bear fell asleep;
...and the little frog fell asleep;
...and the child fell asleep.
In the most basic computer science sense, recursion is a function that calls itself. Say you have a linked list structure:
struct Node {
Node* next;
};
And you want to find out how long a linked list is you can do this with recursion:
int length(const Node* list) {
if (!list->next) {
return 1;
} else {
return 1 + length(list->next);
}
}
(This could of course be done with a for loop as well, but is useful as an illustration of the concept)
Whenever a function calls itself, creating a loop, then that's recursion. As with anything there are good uses and bad uses for recursion.
The most simple example is tail recursion where the very last line of the function is a call to itself:
int FloorByTen(int num)
{
if (num % 10 == 0)
return num;
else
return FloorByTen(num-1);
}
However, this is a lame, almost pointless example because it can easily be replaced by more efficient iteration. After all, recursion suffers from function call overhead, which in the example above could be substantial compared to the operation inside the function itself.
So the whole reason to do recursion rather than iteration should be to take advantage of the call stack to do some clever stuff. For example, if you call a function multiple times with different parameters inside the same loop then that's a way to accomplish branching. A classic example is the Sierpinski triangle.
You can draw one of those very simply with recursion, where the call stack branches in 3 directions:
private void BuildVertices(double x, double y, double len)
{
if (len > 0.002)
{
mesh.Positions.Add(new Point3D(x, y + len, -len));
mesh.Positions.Add(new Point3D(x - len, y - len, -len));
mesh.Positions.Add(new Point3D(x + len, y - len, -len));
len *= 0.5;
BuildVertices(x, y + len, len);
BuildVertices(x - len, y - len, len);
BuildVertices(x + len, y - len, len);
}
}
If you attempt to do the same thing with iteration I think you'll find it takes a lot more code to accomplish.
Other common use cases might include traversing hierarchies, e.g. website crawlers, directory comparisons, etc.
Conclusion
In practical terms, recursion makes the most sense whenever you need iterative branching.
Recursion is a method of solving problems based on the divide and conquer mentality.
The basic idea is that you take the original problem and divide it into smaller (more easily solved) instances of itself, solve those smaller instances (usually by using the same algorithm again) and then reassemble them into the final solution.
The canonical example is a routine to generate the Factorial of n. The Factorial of n is calculated by multiplying all of the numbers between 1 and n. An iterative solution in C# looks like this:
public int Fact(int n)
{
int fact = 1;
for( int i = 2; i <= n; i++)
{
fact = fact * i;
}
return fact;
}
There's nothing surprising about the iterative solution and it should make sense to anyone familiar with C#.
The recursive solution is found by recognising that the nth Factorial is n * Fact(n-1). Or to put it another way, if you know what a particular Factorial number is you can calculate the next one. Here is the recursive solution in C#:
public int FactRec(int n)
{
if( n < 2 )
{
return 1;
}
return n * FactRec( n - 1 );
}
The first part of this function is known as a Base Case (or sometimes Guard Clause) and is what prevents the algorithm from running forever. It just returns the value 1 whenever the function is called with a value of 1 or less. The second part is more interesting and is known as the Recursive Step. Here we call the same method with a slightly modified parameter (we decrement it by 1) and then multiply the result with our copy of n.
When first encountered this can be kind of confusing so it's instructive to examine how it works when run. Imagine that we call FactRec(5). We enter the routine, are not picked up by the base case and so we end up like this:
// In FactRec(5)
return 5 * FactRec( 5 - 1 );
// which is
return 5 * FactRec(4);
If we re-enter the method with the parameter 4 we are again not stopped by the guard clause and so we end up at:
// In FactRec(4)
return 4 * FactRec(3);
If we substitute this return value into the return value above we get
// In FactRec(5)
return 5 * (4 * FactRec(3));
This should give you a clue as to how the final solution is arrived at so we'll fast track and show each step on the way down:
return 5 * (4 * FactRec(3));
return 5 * (4 * (3 * FactRec(2)));
return 5 * (4 * (3 * (2 * FactRec(1))));
return 5 * (4 * (3 * (2 * (1))));
That final substitution happens when the base case is triggered. At this point we have a simple algrebraic formula to solve which equates directly to the definition of Factorials in the first place.
It's instructive to note that every call into the method results in either a base case being triggered or a call to the same method where the parameters are closer to a base case (often called a recursive call). If this is not the case then the method will run forever.
Recursion is solving a problem with a function that calls itself. A good example of this is a factorial function. Factorial is a math problem where factorial of 5, for example, is 5 * 4 * 3 * 2 * 1. This function solves this in C# for positive integers (not tested - there may be a bug).
public int Factorial(int n)
{
if (n <= 1)
return 1;
return n * Factorial(n - 1);
}
Recursion refers to a method which solves a problem by solving a smaller version of the problem and then using that result plus some other computation to formulate the answer to the original problem. Often times, in the process of solving the smaller version, the method will solve a yet smaller version of the problem, and so on, until it reaches a "base case" which is trivial to solve.
For instance, to calculate a factorial for the number X, one can represent it as X times the factorial of X-1. Thus, the method "recurses" to find the factorial of X-1, and then multiplies whatever it got by X to give a final answer. Of course, to find the factorial of X-1, it'll first calculate the factorial of X-2, and so on. The base case would be when X is 0 or 1, in which case it knows to return 1 since 0! = 1! = 1.
Consider an old, well known problem:
In mathematics, the greatest common divisor (gcd) … of two or more non-zero integers, is the largest positive integer that divides the numbers without a remainder.
The definition of gcd is surprisingly simple:
where mod is the modulo operator (that is, the remainder after integer division).
In English, this definition says the greatest common divisor of any number and zero is that number, and the greatest common divisor of two numbers m and n is the greatest common divisor of n and the remainder after dividing m by n.
If you'd like to know why this works, see the Wikipedia article on the Euclidean algorithm.
Let's compute gcd(10, 8) as an example. Each step is equal to the one just before it:
gcd(10, 8)
gcd(10, 10 mod 8)
gcd(8, 2)
gcd(8, 8 mod 2)
gcd(2, 0)
2
In the first step, 8 does not equal zero, so the second part of the definition applies. 10 mod 8 = 2 because 8 goes into 10 once with a remainder of 2. At step 3, the second part applies again, but this time 8 mod 2 = 0 because 2 divides 8 with no remainder. At step 5, the second argument is 0, so the answer is 2.
Did you notice that gcd appears on both the left and right sides of the equals sign? A mathematician would say this definition is recursive because the expression you're defining recurs inside its definition.
Recursive definitions tend to be elegant. For example, a recursive definition for the sum of a list is
sum l =
if empty(l)
return 0
else
return head(l) + sum(tail(l))
where head is the first element in a list and tail is the rest of the list. Note that sum recurs inside its definition at the end.
Maybe you'd prefer the maximum value in a list instead:
max l =
if empty(l)
error
elsif length(l) = 1
return head(l)
else
tailmax = max(tail(l))
if head(l) > tailmax
return head(l)
else
return tailmax
You might define multiplication of non-negative integers recursively to turn it into a series of additions:
a * b =
if b = 0
return 0
else
return a + (a * (b - 1))
If that bit about transforming multiplication into a series of additions doesn't make sense, try expanding a few simple examples to see how it works.
Merge sort has a lovely recursive definition:
sort(l) =
if empty(l) or length(l) = 1
return l
else
(left,right) = split l
return merge(sort(left), sort(right))
Recursive definitions are all around if you know what to look for. Notice how all of these definitions have very simple base cases, e.g., gcd(m, 0) = m. The recursive cases whittle away at the problem to get down to the easy answers.
With this understanding, you can now appreciate the other algorithms in Wikipedia's article on recursion!
A function that calls itself
When a function can be (easily) decomposed into a simple operation plus the same function on some smaller portion of the problem. I should say, rather, that this makes it a good candidate for recursion.
They do!
The canonical example is the factorial which looks like:
int fact(int a)
{
if(a==1)
return 1;
return a*fact(a-1);
}
In general, recursion isn't necessarily fast (function call overhead tends to be high because recursive functions tend to be small, see above) and can suffer from some problems (stack overflow anyone?). Some say they tend to be hard to get 'right' in non-trivial cases but I don't really buy into that. In some situations, recursion makes the most sense and is the most elegant and clear way to write a particular function. It should be noted that some languages favor recursive solutions and optimize them much more (LISP comes to mind).
A recursive function is one which calls itself. The most common reason I've found to use it is traversing a tree structure. For example, if I have a TreeView with checkboxes (think installation of a new program, "choose features to install" page), I might want a "check all" button which would be something like this (pseudocode):
function cmdCheckAllClick {
checkRecursively(TreeView1.RootNode);
}
function checkRecursively(Node n) {
n.Checked = True;
foreach ( n.Children as child ) {
checkRecursively(child);
}
}
So you can see that the checkRecursively first checks the node which it is passed, then calls itself for each of that node's children.
You do need to be a bit careful with recursion. If you get into an infinite recursive loop, you will get a Stack Overflow exception :)
I can't think of a reason why people shouldn't use it, when appropriate. It is useful in some circumstances, and not in others.
I think that because it's an interesting technique, some coders perhaps end up using it more often than they should, without real justification. This has given recursion a bad name in some circles.
Recursion is an expression directly or indirectly referencing itself.
Consider recursive acronyms as a simple example:
GNU stands for GNU's Not Unix
PHP stands for PHP: Hypertext Preprocessor
YAML stands for YAML Ain't Markup Language
WINE stands for Wine Is Not an Emulator
VISA stands for Visa International Service Association
More examples on Wikipedia
Recursion works best with what I like to call "fractal problems", where you're dealing with a big thing that's made of smaller versions of that big thing, each of which is an even smaller version of the big thing, and so on. If you ever have to traverse or search through something like a tree or nested identical structures, you've got a problem that might be a good candidate for recursion.
People avoid recursion for a number of reasons:
Most people (myself included) cut their programming teeth on procedural or object-oriented programming as opposed to functional programming. To such people, the iterative approach (typically using loops) feels more natural.
Those of us who cut our programming teeth on procedural or object-oriented programming have often been told to avoid recursion because it's error prone.
We're often told that recursion is slow. Calling and returning from a routine repeatedly involves a lot of stack pushing and popping, which is slower than looping. I think some languages handle this better than others, and those languages are most likely not those where the dominant paradigm is procedural or object-oriented.
For at least a couple of programming languages I've used, I remember hearing recommendations not to use recursion if it gets beyond a certain depth because its stack isn't that deep.
A recursive statement is one in which you define the process of what to do next as a combination of the inputs and what you have already done.
For example, take factorial:
factorial(6) = 6*5*4*3*2*1
But it's easy to see factorial(6) also is:
6 * factorial(5) = 6*(5*4*3*2*1).
So generally:
factorial(n) = n*factorial(n-1)
Of course, the tricky thing about recursion is that if you want to define things in terms of what you have already done, there needs to be some place to start.
In this example, we just make a special case by defining factorial(1) = 1.
Now we see it from the bottom up:
factorial(6) = 6*factorial(5)
= 6*5*factorial(4)
= 6*5*4*factorial(3) = 6*5*4*3*factorial(2) = 6*5*4*3*2*factorial(1) = 6*5*4*3*2*1
Since we defined factorial(1) = 1, we reach the "bottom".
Generally speaking, recursive procedures have two parts:
1) The recursive part, which defines some procedure in terms of new inputs combined with what you've "already done" via the same procedure. (i.e. factorial(n) = n*factorial(n-1))
2) A base part, which makes sure that the process doesn't repeat forever by giving it some place to start (i.e. factorial(1) = 1)
It can be a bit confusing to get your head around at first, but just look at a bunch of examples and it should all come together. If you want a much deeper understanding of the concept, study mathematical induction. Also, be aware that some languages optimize for recursive calls while others do not. It's pretty easy to make insanely slow recursive functions if you're not careful, but there are also techniques to make them performant in most cases.
Hope this helps...
I like this definition:
In recursion, a routine solves a small part of a problem itself, divides the problem into smaller pieces, and then calls itself to solve each of the smaller pieces.
I also like Steve McConnells discussion of recursion in Code Complete where he criticises the examples used in Computer Science books on Recursion.
Don't use recursion for factorials or Fibonacci numbers
One problem with
computer-science textbooks is that
they present silly examples of
recursion. The typical examples are
computing a factorial or computing a
Fibonacci sequence. Recursion is a
powerful tool, and it's really dumb to
use it in either of those cases. If a
programmer who worked for me used
recursion to compute a factorial, I'd
hire someone else.
I thought this was a very interesting point to raise and may be a reason why recursion is often misunderstood.
EDIT:
This was not a dig at Dav's answer - I had not seen that reply when I posted this
1.)
A method is recursive if it can call itself; either directly:
void f() {
... f() ...
}
or indirectly:
void f() {
... g() ...
}
void g() {
... f() ...
}
2.) When to use recursion
Q: Does using recursion usually make your code faster?
A: No.
Q: Does using recursion usually use less memory?
A: No.
Q: Then why use recursion?
A: It sometimes makes your code much simpler!
3.) People use recursion only when it is very complex to write iterative code. For example, tree traversal techniques like preorder, postorder can be made both iterative and recursive. But usually we use recursive because of its simplicity.
Here's a simple example: how many elements in a set. (there are better ways to count things, but this is a nice simple recursive example.)
First, we need two rules:
if the set is empty, the count of items in the set is zero (duh!).
if the set is not empty, the count is one plus the number of items in the set after one item is removed.
Suppose you have a set like this: [x x x]. let's count how many items there are.
the set is [x x x] which is not empty, so we apply rule 2. the number of items is one plus the number of items in [x x] (i.e. we removed an item).
the set is [x x], so we apply rule 2 again: one + number of items in [x].
the set is [x], which still matches rule 2: one + number of items in [].
Now the set is [], which matches rule 1: the count is zero!
Now that we know the answer in step 4 (0), we can solve step 3 (1 + 0)
Likewise, now that we know the answer in step 3 (1), we can solve step 2 (1 + 1)
And finally now that we know the answer in step 2 (2), we can solve step 1 (1 + 2) and get the count of items in [x x x], which is 3. Hooray!
We can represent this as:
count of [x x x] = 1 + count of [x x]
= 1 + (1 + count of [x])
= 1 + (1 + (1 + count of []))
= 1 + (1 + (1 + 0)))
= 1 + (1 + (1))
= 1 + (2)
= 3
When applying a recursive solution, you usually have at least 2 rules:
the basis, the simple case which states what happens when you have "used up" all of your data. This is usually some variation of "if you are out of data to process, your answer is X"
the recursive rule, which states what happens if you still have data. This is usually some kind of rule that says "do something to make your data set smaller, and reapply your rules to the smaller data set."
If we translate the above to pseudocode, we get:
numberOfItems(set)
if set is empty
return 0
else
remove 1 item from set
return 1 + numberOfItems(set)
There's a lot more useful examples (traversing a tree, for example) which I'm sure other people will cover.
Well, that's a pretty decent definition you have. And wikipedia has a good definition too. So I'll add another (probably worse) definition for you.
When people refer to "recursion", they're usually talking about a function they've written which calls itself repeatedly until it is done with its work. Recursion can be helpful when traversing hierarchies in data structures.
An example: A recursive definition of a staircase is:
A staircase consists of:
- a single step and a staircase (recursion)
- or only a single step (termination)
To recurse on a solved problem: do nothing, you're done.
To recurse on an open problem: do the next step, then recurse on the rest.
In plain English:
Assume you can do 3 things:
Take one apple
Write down tally marks
Count tally marks
You have a lot of apples in front of you on a table and you want to know how many apples there are.
start
Is the table empty?
yes: Count the tally marks and cheer like it's your birthday!
no: Take 1 apple and put it aside
Write down a tally mark
goto start
The process of repeating the same thing till you are done is called recursion.
I hope this is the "plain english" answer you are looking for!
A recursive function is a function that contains a call to itself. A recursive struct is a struct that contains an instance of itself. You can combine the two as a recursive class. The key part of a recursive item is that it contains an instance/call of itself.
Consider two mirrors facing each other. We've seen the neat infinity effect they make. Each reflection is an instance of a mirror, which is contained within another instance of a mirror, etc. The mirror containing a reflection of itself is recursion.
A binary search tree is a good programming example of recursion. The structure is recursive with each Node containing 2 instances of a Node. Functions to work on a binary search tree are also recursive.
This is an old question, but I want to add an answer from logistical point of view (i.e not from algorithm correctness point of view or performance point of view).
I use Java for work, and Java doesn't support nested function. As such, if I want to do recursion, I might have to define an external function (which exists only because my code bumps against Java's bureaucratic rule), or I might have to refactor the code altogether (which I really hate to do).
Thus, I often avoid recursion, and use stack operation instead, because recursion itself is essentially a stack operation.
You want to use it anytime you have a tree structure. It is very useful in reading XML.
Recursion as it applies to programming is basically calling a function from inside its own definition (inside itself), with different parameters so as to accomplish a task.
"If I have a hammer, make everything look like a nail."
Recursion is a problem-solving strategy for huge problems, where at every step just, "turn 2 small things into one bigger thing," each time with the same hammer.
Example
Suppose your desk is covered with a disorganized mess of 1024 papers. How do you make one neat, clean stack of papers from the mess, using recursion?
Divide: Spread all the sheets out, so you have just one sheet in each "stack".
Conquer:
Go around, putting each sheet on top of one other sheet. You now have stacks of 2.
Go around, putting each 2-stack on top of another 2-stack. You now have stacks of 4.
Go around, putting each 4-stack on top of another 4-stack. You now have stacks of 8.
... on and on ...
You now have one huge stack of 1024 sheets!
Notice that this is pretty intuitive, aside from counting everything (which isn't strictly necessary). You might not go all the way down to 1-sheet stacks, in reality, but you could and it would still work. The important part is the hammer: With your arms, you can always put one stack on top of the other to make a bigger stack, and it doesn't matter (within reason) how big either stack is.
Recursion is the process where a method call iself to be able to perform a certain task. It reduces redundency of code. Most recurssive functions or methods must have a condifiton to break the recussive call i.e. stop it from calling itself if a condition is met - this prevents the creating of an infinite loop. Not all functions are suited to be used recursively.
hey, sorry if my opinion agrees with someone, I'm just trying to explain recursion in plain english.
suppose you have three managers - Jack, John and Morgan.
Jack manages 2 programmers, John - 3, and Morgan - 5.
you are going to give every manager 300$ and want to know what would it cost.
The answer is obvious - but what if 2 of Morgan-s employees are also managers?
HERE comes the recursion.
you start from the top of the hierarchy. the summery cost is 0$.
you start with Jack,
Then check if he has any managers as employees. if you find any of them are, check if they have any managers as employees and so on. Add 300$ to the summery cost every time you find a manager.
when you are finished with Jack, go to John, his employees and then to Morgan.
You'll never know, how much cycles will you go before getting an answer, though you know how many managers you have and how many Budget can you spend.
Recursion is a tree, with branches and leaves, called parents and children respectively.
When you use a recursion algorithm, you more or less consciously are building a tree from the data.
In plain English, recursion means to repeat someting again and again.
In programming one example is of calling the function within itself .
Look on the following example of calculating factorial of a number:
public int fact(int n)
{
if (n==0) return 1;
else return n*fact(n-1)
}
Any algorithm exhibits structural recursion on a datatype if basically consists of a switch-statement with a case for each case of the datatype.
for example, when you are working on a type
tree = null
| leaf(value:integer)
| node(left: tree, right:tree)
a structural recursive algorithm would have the form
function computeSomething(x : tree) =
if x is null: base case
if x is leaf: do something with x.value
if x is node: do something with x.left,
do something with x.right,
combine the results
this is really the most obvious way to write any algorith that works on a data structure.
now, when you look at the integers (well, the natural numbers) as defined using the Peano axioms
integer = 0 | succ(integer)
you see that a structural recursive algorithm on integers looks like this
function computeSomething(x : integer) =
if x is 0 : base case
if x is succ(prev) : do something with prev
the too-well-known factorial function is about the most trivial example of
this form.
function call itself or use its own definition.

Fastest way to find number of elements in a range

Given an array with n elements, how to find the number of elements greater than or equal to a given value (x) in the given range index i to index j in O(log n) or better complexity?
my implementation is this but it is O(n)
for(a=i;a<=j;a++)
if(p[a]>=x) // p[] is array containing n elements
count++;
If you are allowed to preprocess the array, then with O(n log n) preprocessing time, we can answer any [i,j] query in O(log n) time.
Two ideas:
1) Observe that it is enough to be able to answer [0,i] and [0,j] queries.
2) Use a persistent* balanced order statistics binary tree, which maintains n versions of the tree, version i is formed from version i-1 by adding a[i] to it. To answer query([0,i], x), you query the version i tree for the number of elements > x (basically rank information). An order statistics tree lets you do that.
*: persistent data structures are an elegant functional programming concept for immutable data structures and have efficient algorithms for their construction.
If the array is sorted you can locate the first value less than X with a binary search and the number of elements greater than X is the number of items after that element. That would be O(log(n)).
If the array is not sorted there is no way of doing it in less than O(n) time since you will have to examine every element to check if it's greater than or equal to X.
Impossible in O(log N) because you have to inspect all the elements, so a O(N) method is expected.
The standard algorithm for this is based on quicksort's partition, sometimes called quick-select.
The idea is that you don't sort the array, but rather just partition the section containing x, and stop when x is your pivot element. After the procedure is completed you have all elements x and greater to the right of x. This is the same procedure as when finding the k-th largest element.
Read about a very similar problem at How to find the kth largest element in an unsorted array of length n in O(n)?.
The requirement index i to j is not a restriction that introduces any complexity to the problem.
Given your requirements where the data is not sorted in advance and constantly changing between queries, O(n) is the best complexity you can hope to achieve, since there's no way to count the number of elements greater than or equal to some value without looking at all of them.
It's fairly simple if you think about it: you cannot avoid inspecting every element of a range for any type of search if you have no idea how it's represented/ordered in advance.
You could construct a balanced binary tree, even radix sort on the fly, but you're just pushing the overhead elsewhere to the same linear or worse, linearithmic O(NLogN) complexity since such algorithms once again have you inspecting every element in the range first to sort it.
So there's actually nothing wrong with O(N) here. That is the ideal, and you're looking at either changing the whole nature of the data involved outside to allow it to be sorted efficiently in advance or micro-optimizations (ex: parallel fors to process sub-ranges with multiple threads, provided they're chunky enough) to tune it.
In your case, your requirements seem rigid so the latter seems like the best bet with the aid of a profiler.

how to find the maximum subsequence sum in a circular linked list

I am aware of the maximum subarray sum problem and its O(n) algorithm. This questions modifies that problem by using a circular linked list:
Find the sequence of numbers in a circular linked list with maximum sum.
Now what if sum of all entries is zero?
To me, the only approach is to modify the array solution and have the algorithm loop around and start over at the beginning at the list once the first iteration is done. Then do the same thing for up to 2 times the entire list and find the max. The down side is that there might be many very tricky to handle if I do it this way, for example, if the list looks like:
2 - 2 - 2 - 2 back to front
Then it's very tricky to not include the same element twice
Is there a better algorithm?
Thanks!!
First of all, it doesn't matter if the datastructure is a linked list or an array, so I will use array for simplicity.
I don't really understand your algorithm, but it seems that you are going to do something like duplicate the array at the back of the original, and run the Kadane's algorithm on this doubled-array. This is a wrong approach, and a counter example has been given by #RanaldLam.
To solve it, we need to discuss it in three cases:
All negative. In this case, the maximum of the array is the answer, and an O(N) scan will do the job;
The maximum sub-array does not require a wrapping, for example a = {-1, 1, 2, -3}. In this case, a normal Kadane's algorithm will do the job, time complexity O(N);
The maximum sub-array requires a wrapping, for example a = {1, -10, 1}. Actually, this case implies another fact: since elements inside the maximum sub-array requires a wrapping, then the elements that are not inside the maximum sub-array does not require a wrapping. Therefore, as long as we know the sum of these non-contributing elements, we can calculate the correct sum of contributing elements by subtracting max_non_contributing_sum from total sum of the array.
But how to calculate max_non_contributing_sum in case 3? This is a bit tricky: since these non-contributing elements do not require wrapping, so we can simply invert the sign of every elements and run Kadane's algorithm on this inverted array, which requires O(N).
Finally, we should compare the sum of non-wrapping (case 2) and wrapping (case 3), the answer should be the bigger one.
As a summary, all cases require O(N), thus the total complexity of the algorithm is O(N).
Your absolutly right. There is no better algorithm.

A[j] = 2∗A[i] in list with better than O(n^2) runtime

below I've listed a problem I'm having some trouble with. This problem is a simple nested loop away from an O(n^2) solution, but I need it to be O(n). Any ideas how this should be tackled? Would it be possible to form two equations?
Given an integer array A, check if there are two indices i and j such that A[j] = 2∗A[i]. For example, on the array (25, 13, 16, 7, 8) the algorithm should output “true” (since 16 = 2 * 8), whereas on the array (25, 17, 44, 24) the algorithm should output “false”. Describe an algorithm for this problem with worst-case running time that is better than O(n^2), where n is the length of A.
Thanks!
This is a great spot to use a hash table. Create a hash table and enter each number in the array into the hash table. Then, iterate across the array one more time and check whether 2*A[i] exists in the hash table for each i. If so, then you know a pair of indices exists with this property. If not, you know no such pair exists.
On expectation, this takes time O(n), since n operations on a hash table take expected amortized O(1) time.
Hope this helps!
templatetypedef's suggestion to use a hash table is a good one. I want to explain a little more about why.
The key here is realizing that you are essentially searching for some value in a set. You have a set of numbers you are searching in (2 * each value in the input array), and a set of numbers you are searching for (each value in the input array). Your brute-force naive case is just looking up values directly in the search-in array. What you want to do is pre-load your "search-in" set into something with faster lookups than an array (like a hash table), then you can search from there.
You can also further prune your results by not searching for A[i] where A[i] is odd; because you know that A[i] = 2 * A[j] can never be true if A[i] is odd. You can also compute the minimum and maximum values in the "search-in" array on the fly during initialization and prune all A[i] outside that range.
The performance there is hard to express in big O form since it depends on the nature of the data, but you can calculate a best- and worst- case and an amortized case as well.
However, a proper choice of hash table size (if your value range is small you can simply choose a capacity that is larger than your value range, where the hash function is the value itself) may actually make pruning more costly than not in some cases, you'd have to profile it to find out.

Performance of array analysis using Arrays.sort

I have a code that uses Arrays.sort(char[]) in the following manner:
void arrayAnalysis(String[] array){
for(int a=0; a<array.length;a++){
char[] letters = array[a].toCharArray();
Arrays.sort(letters);
...
for(int b=a+1; b<array.length;b++){
char[] letters2 = array[b].toCharArray();
Arrays.sort(letters2);
if(Arrays.equals(letters, letters2)
print("equal");
}
}
}
In this case, n is equal to the array size. Due to the nested for loops, performance is automatically O(n^2). However, I think Arrays.sort (with O(nlog(n))) also affects the performance and makes it worse than O(n^2). Is this thinking correct?
Would the final performance be O(n*nlog(n)*(n*nlog(n))? Or am I way off?
Thanks.
Edit: I should add that while n is related to the array size, Arrays.sort is working with the number of letters in the array element. That is part of my confusion if this should be added to the performance analysis.
Edit2: It would be cool if the down-voter left a comment as to why it was deemed as a bad question.
If n is the length of the array, and m is the length of each array[i], then you will, on each of n^2 iterations, perform an O(m log m) sort, so overall it's O(n^2 (m log m)) (Or O(n^3 log n) if n == m. [EDIT: now that I think more about this, your guess is right, and this is the wrong complexity. But what I say below is still correct!]]
This is not really necessary, though. You could just make a sorted copy of the array, and do your nested for-loop using that one. Look at what happens when a is 0: first you sort array[0], then in the inner for loop you sort array[1] through array[n].
Then when a is 1, you first sort array[1], then in the inner for loop array[2] through array[n]. But you already sorted all that, and it's not as if it will have changed in the interim.
You run n outer loops, each of which runs n inner loops, each of which calls an O(n log n) algorithm, so the final result — absent any interaction between the levels — is O(n3 log n).

Categories