I have written the following randomized quicksort code using tail recursion. I wanted to see the effect of not using tail recursion and wanted to see how the time of execution and run time are affected. How can we remove tail recursion from the below randomized quicksort code?
#include<iostream>
#include<cstdlib>
using namespace std;
int partition(int low,int high,int a[])
{
int index=(rand()%(high-low))+low;
//cout<<endl<<index<<"----"<<endl;
int temp1=a[index];
a[index]=a[low];
a[low]=temp1;
int left,right,pivot_item=a[low];
left=low;
right=high;
while(left<right)
{
while(a[left]<=pivot_item)
left++;
while(a[right]>pivot_item)
right--;
if(left<right)
{
int temp=a[right];
a[right]=a[left];
a[left]=temp;
}
}
a[low]=a[right];
a[right]=pivot_item;
return right;
}
void quicksort(int low,int high,int a[])
{
int pivot;
if(low<high)
{
pivot=partition(low,high,a);
quicksort(low,pivot-1,a);
quicksort(pivot+1,high,a);
}
}
int main()
{
srand(time(NULL));
int n;
n=50;
int a[n];
int i;
for(i=0;i<n;i++)
a[i]=(rand()%(1000-1));
quicksort(0,n-1,a);
cout<<endl;
for(i=0;i<n;i++)
cout<<a[i]<<endl;
return 0;
}
EDIT:
Is there any way to altogether remove the second recursive call in the quicksort function. This would mean removing tail recursion and would also significantly affect the time.
A simple way to remove tail recursion is to do something else in the function after making the recursive call. Below I've changed the function to return a value instead of void, and added a return statement to rturn something after the recursive call is finished. This should have minimal performance impact.
int quicksort(int low,int high,int a[])
{
int pivot;
if(low<high)
{
pivot=partition(low,high,a);
quicksort(low,pivot-1,a);
quicksort(pivot+1,high,a);
}
return 0;
}
Compilers typically have an option to enable and disable particular optimizations.
In case of g++ and related compilers, you can enable and disable tail call optimization using -foptimize-sibling-calls and -fno-optimize-sibling-calls options. If you use another compiler, see the manual.
I would recommend using these to compare the effect of tail recursion, because it allows you to use the same algorithm.
Quicksort is naturally tail recursive, so it's not simple to make non-tail-recurive. One one hand, it's easy: simply do something after the recursion call. VoilĂ , the algorithm is now no longer tail recursive. But, what you do after the recursion is important. It must have a side-effect, or else it can be optimized away. But then it would affect the performance and you're no longer measuring only the effect of the tail call optimization.
Here is an idea. Implement two identical functions that call each other instead if themselves in the tail call:
void quicksort1(int low,int high,int a[])
{
int pivot;
if(low<high)
{
pivot=partition(low,high,a);
quicksort1(low,pivot-1,a);
quicksort2(pivot+1,high,a);
}
}
void quicksort2(int low,int high,int a[])
{
int pivot;
if(low<high)
{
pivot=partition(low,high,a);
quicksort2(low,pivot-1,a);
quicksort1(pivot+1,high,a);
}
}
It's still possible to do tail call optimization on mutually recursive functions, so I won't guarantee that a smart compiler won't figure out what's going on. But the optimization will be harder to implement, so some compilers might not attempt to do it. Check the assembly if you want to be sure. Also, this may have some effect on instruction cache, so it may have an effect on performance other than just disabling the tail call optimization.
Related
I was trying to write code for vertical order traversal of a tree and my code prints the result on code::blocks however it is not printing the result when the same thing is run in the geekforgeeks online ide. Why is it doing this?
void getvert(Node* root,int hd,map<int,vector<int>>m){
if(root==NULL)return;
m[hd].push_back(root->data);
getvert(root->left,hd-1,m);
getvert(root->right,hd+1,m);
}
void verticalOrder(Node *root)
{
map<int,vector<int>>m;
int hd=0;
getvert(root,hd,m);
auto it=m.begin();
for(;it!=m.end();it++)
{
for (int i=0; i<it->second.size(); ++i)
cout<<it->second[i];
cout<<endl;
}
}
The function getvert accepts the last argument, m, as a value. Changes made to it in the function are made to a local copy of the object. Hence, you don't see any changes in verticalOrder.
Change getvert so that it accepts m as a reference.
void getvert(Node* root,int hd, map<int,vector<int>>& m) // Need reference argument
{
...
}
i did exactly what the pseudo code told me in the book introduction to algorithms and it didn't work
there are two parts that i dont really understand
1-how do u make a recursive function that is a void i mean shouldnt a recursive function always return the last step then what is before it .. it's a void so how would it preform the task
2-merg_sor() was called twice in one function .. do u call that nested recursion ?or what ?? .. and how does it affect the merg function
#include <iostream>
#include <vector>
using namespace std;
void Merg(vector<int> Arr,int start,int middle,int end)
{
std::vector<int> left;
std::vector<int> right;
for(int i =start;i<(end-start);i++)
{
if (i <middle)
{
right.push_back(Arr.at(i));
}
else
{
left.push_back(Arr.at(i));
}
}
int j=0;
int k=0;
for(int i =start;i<(end-start);i++)
{
if(right.at(j)<=Arr.at(i))
{
Arr.at(i)=right.at(j);
j++;
}
else
{
Arr.at(i)=left.at(k);
k++;
}
}
}
void Merg_sort(vector<int> Arr,int start,int end)
{
if (start <end)
{
int middle = (start+end)/2;
Merg_sort(Arr,start,middle);
Merg_sort(Arr,middle+1,end);
Merg(Arr,start,middle,end);
}
}
int main()
{
vector<int> x;
for (int i =0;i<8;i++){x.push_back(i);}
x.at(2)=8;
Merg_sort(x,0,7);
}
1-how do u make a recursive function that is a void i mean shouldnt a recursive function always return the last step then what is before it .. it's a void so how would it preform the task
A function can have side effects. It means, that a function can modify the state of the program. A void function always works by side effects. A void function that doesn't have side effects is completely useless. Whether the function is recursive makes no difference to this question.
2-merg_sor() was called twice in one function .. do u call that nested recursion ?or what ??
I'm not sure if it is commonly used term, but I would describe it as multi-branched recursion.
2 .. and how does it affect the merg function
It doesn't, because it has no side effects.
i did exactly what the pseudo code told me
Perhaps you didn't interpret the pseudo code correctly. Perhaps the arguments are implicitly references in the pseudo-language that you're reading such as they are in languages like python.
int findpow(int n1,int k, int count){ //while calling, k=1, count=0
if(k<n1)
return findpow(n1,k*2,count+1);
if(k==n1)
return count;
if(k>n1)
return --count;
}
This is a function that returns the largest power of two less than n. When I run it in my ubuntu terminal (g++ 4.8.4), it works fine. But when I am running it on www.hackerrank.com, it gives an error(control reaches end of non void function). The problem is, I participate in many contests on this website and I have come across this problem multiple times.
Please tell if you know how I can fix it.
You can use else if statement like this:
int findpow(int n1,int k, int count){ //while calling, k=1, count=0
if(k<n1)
return findpow(n1,k*2,count+1);
else if(k==n1)
return count;
else // Eliminate compiler errors (warnings)
return --count;
}
or as said #juanchopanza:
int findpow(int n1,int k, int count){ //while calling, k=1, count=0
if(k<n1)
return findpow(n1,k*2,count+1);
if(k==n1)
return count;
// Eliminate compiler errors (warnings)
return --count;
}
It will do the same thing as your code, but will not give a doubt to compiler that can be no return points from function.
'control reaches end of non void function' is a warning not an error, it's safe to ignore in this case but if you want to suppress the warning there are multiple ways:
put a return after the last condition
as Mykola suggested restructure the conditions to be explicit
set the -Wno-return-type flag
Following code for max-heap implementation
#include<iostream>
#include<math.h>
using namespace std;
#define maxn 1000
int x[maxn];
int parent(int i){
return int(i/2);
}
int left(int i){
return 2*i;
}
int right(int i){
return 2*i+1;
}
void max_heap(int x[],int i,int size){
int largest;
int l=left(i);
int r=right(i);
if (l<=size && x[l]>x[i]){
largest=l;
}
else
{
largest=i;
}
if (r<=size && x[r]>x[largest]){
largest=r;
}
if (largest!=i) { int s=x[i];x[i]=x[largest];x[largest]=s;}
max_heap(x,largest,size);
}
int main(){
x[1]=16;
x[2]=4;
x[3]=10;
x[4]=14;
x[5]=7;
x[6]=9;
x[7]=3;
x[8]=2;
x[9]=8;
x[10]=1;
int size=10;
max_heap(x,2,size);
for (int i=1;i<=10;i++)
cout<<x[i]<<" ";
return 0;
}
When I run it, it writes such kind of warning:
1>c:\users\datuashvili\documents\visual studio 2010\projects\heap_property\heap_property\heap_property.cpp(36): warning C4717: 'max_heap' : recursive on all control paths, function will cause runtime stack overflow
Please tell me what is wrong?
The message tells you exactly what's wrong. You haven't implemented any checks to stop the recursion. One smart compiler.
max_heap function doesn't have base case, i.e., a return statement. You are just recursively calling the function but never saying when to break another successive call to the max_heap.
Also, in your example you are just calling the function with out satisfying any condition. Usually recursion is done or not done when a case is satisfied.
please tell me what is wrong?
Another problem that I see is that the size of your array x is 10. But the indices that you are using to set values are 1-10.
Put
max_heap(x,largest,size);
inside last check, like this:
if (largest!=i)
{
int s=x[i];
x[i]=x[largest];
x[largest]=s;
max_heap(x,largest,size);
}
and you're done!
There are many other problems with your code, but to answer your specific question, above change would do!
I've implemented Kruskal's algorithm in C++ using the disjoint-set data structure according to Wikipedia like this:
#include <stdio.h>
#include <algorithm>
#define MAX_EDGES 10000000
#define MAX_VERTICES 200001
using namespace std;
int num_edges,num_vertices;
int total_cost=0;
struct edge{
int v1,v2;
int cost;
};
struct comp{
bool operator()(const edge& e1,const edge& e2){
return e1.cost<e2.cost;
}
};
edge edges[MAX_EDGES];
int parent[MAX_VERTICES];
int rank[MAX_VERTICES];
int findset(int x){
if(x!=parent[x]){
parent[x]=findset(parent[x]);
}
return parent[x];
}
void merge(int x,int y){
int px=findset(x),py=findset(y);
if(rank[px]>rank[py]){
parent[py]=px;
}else{
parent[px]=py;
}
if(rank[px]==rank[py]){
++rank[py];
}
}
int main(){
FILE* in=fopen("input","r");
FILE* out=fopen("output","w");
fscanf(in,"%d %d\n",&num_vertices,&num_edges);
for(int i=1;i<=num_vertices;++i){
parent[i]=i;
rank[i]=0;
}
for(int i=0;i<num_edges;++i){
fscanf(in,"%d %d %d\n",&edges[i].v1,&edges[i].v2,&edges[i].cost);
}
sort(edges,edges+num_edges,comp());
for(int i=0;i<num_edges;++i){
int s1=findset(edges[i].v1),s2=findset(edges[i].v2);
if(s1!=s2){
merge(s1,s2);
total_cost+=edges[i].cost;
}
}
fprintf(out,"%d\n",total_cost);
}
My question is: Do I need these two lines of code? If so, what's their importance?
int px=findset(x),py=findset(y);
in merge instead of int px=parent[x],py=parent[y];
parent[x]=findset(parent[x]); in
findset instead of return
findset(parent[x]);
1) findset(x) returns the canonical representative of the set that x is in (the root of its ancestry tree). You need this to be able to compare whether two elements are in the same set or not (they have the same representative), parent[x] just returns the parent of x in the tree, which may not be the root.
1a) You forgot to test for px and py being identical in merge.
2) It's an optimization so that future calls to findset will run faster. If parent[x] used to point to its parent which pointed to the root of its set's tree, after this call parent[x] will point directly to the root.
You need this to because x.parent is not necessarily the representative of the class to which x belongs, so the algorithm wouldn't be correct without it.
Without the assignment, the algorithm would be suboptimal. This is the path compression optimization, also discussed in the Wikipedia.