I have an assignment for a c++ programming class to write a recursive function without the use of static variables, with the following prototype:
int findmin(const int a[], int n);
My solution works (for very small arrays), however I think ~2^n complexity is excessive and could be improved.
Are there any improvements that could be made within the specified criteria that would make this more efficient?
int findmin(const int a[], int n)
{
if(n == 0)
return a[0];
else
{
if(a[n-1] < findmin(a,(n-1)))
return a[n-1];
else
return findmin(a,(n-1));
}
}
It's a little silly to worry about efficiency, given that there is an obvious, non-recursive way to do it in O(n), one pass. There is even an STL algorithm std::min_element. But then, it's a silly assignment. FIrst be sure your solution is correct. When n==0, will a[0] be valid? Generally, such an n indicates the length of the array, not the lowest index.
To go from O[n^2] to O[n], be sure to compare each element only once. That implies not starting at the beginning of the array on every pass.
#include <algorithm>
#include <cassert>
int findmin(const int a[], int n)
{
assert(n>0);
if(n == 1) // See heyah.
return a[0];
else
{
return std::min(a[0], findmin(a + 1, n - 1));
}
}
In for-real C++ code, if for some reason we were saddled with the old fashion function signature, we would do something like this:
int findmin(const int a[], int n) {
if(n<=0) { throw std::length_error("findmin called on empty array");}
return *std::min_element(a, a+n);
}
You could do conditional operator ?: to get rid of bunch if else statements, to make function cleaner. And instead of calling findmin() twice you could assign return value to variable inside of the statement, this is main advantage of this code vs. original one.
int findmin(const int a[], int n) {
if (n == 0) // base case
return a[0];
return a[n] < (int min = findmin(a, n - 1)) ? a[n] : min;
}
This (a[n] < (int min = findmin(a, n - 1)) ? a[n] : min;) could be done using if statement as well:
if (a[n] < (int min = findmin (a, n - 1))
return a[n];
else
return min;
EDIT:
Per many reputable sources, this is O(n) time. O (n^2) would be if we are comparing each element with all the other elements.
Related
I wrote a recursive solution for the longest increasing subsequence and it worked perfectly fine. But when I applied dp on the same code it gives different answers.
Problem Link: https://practice.geeksforgeeks.org/problems/longest-increasing-subsequence-1587115620/1
Recursive code:
int LISrecursive(int arr[], int n, int currIndex, int maxVal) {
if (currIndex == n) {
return 0;
}
int included = 0, notIncluded = 0;
if (arr[currIndex] > maxVal) {
included = 1 + LISrecursive(arr, n, currIndex + 1, arr[currIndex]);
}
notIncluded = LISrecursive(arr, n, currIndex + 1, maxVal);
return max(notIncluded, included);
}
DP Code:
int LISdp(int arr[], int n, int currIndex, int maxVal, vector<int> &dp) {
if (currIndex == n) {
return 0;
}
if (dp[currIndex] != -1) return dp[currIndex];
int included = 0, notIncluded = 0;
if (arr[currIndex] > maxVal) {
included = 1 + LISdp(arr, n, currIndex + 1, arr[currIndex], dp);
}
notIncluded = LISdp(arr, n, currIndex + 1, maxVal, dp);
return dp[currIndex] = max(notIncluded, included);
}
int32_t main() {
int n;
cin >> n;
int arr[n];
vector<int> dp(n, -1);
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
cout << LISrecursive(arr,n,0,-1);
cout << LISdp(arr, n, 0 , -1, dp);
return 0;
}
I cannot figure out what I did wrong?
For this test case
6 (n)
6 3 7 4 6 9 (arr[])
Recursive code gives 4 answer(correct)
But DP code gives 3 answer(incorrect)
When I think of dynamic programming, I usually break it down into two steps:
Solve the recursion with "including the current element before recursing
again" compared to "not including the current element before recursing again". This is exactly what you did with your recursive solution.
Take the recursive solution from step 1 and add a cache of previous computed results to avoid repetitive recursion. The cache, can be conceptualized as a multidimension matrix that maps all the non-const variable parameters passed to the recursive function to the final result.
In your case, each recursive step has two variables, currIndex, and maxVal. a and n are actually constants throughout the entire recursion. The number of non-const parameters of the recursive step is the number of dimensions in your cache. So you need a two dimensional table. We could use a big 2-d int array, but that would take a lot of memory. We can achieve the same efficiency with a nested pair of hash tables.
Your primary mistake is that your cache is only 1 dimension - caching the result compared to currIndex irrespective of the value of maxVal. The other mistake is using a vector instead of a hash table. The vector technique you have works, but doesn't scale. And when we add a second dimension, the scale in terms of memory use are even worse.
So let's defined a cache type as an unordered_map (hash table) that maps currIndex to another hash table that maps maxVal to the result of the recursion. You could also use tuples, but the geeksforgeeks coding site doesn't seem to like that. No bother, we can just define this:
typedef std::unordered_map<int, std::unordered_map<int, int>> CACHE;
Then your DP solution is effectively just inserting a lookup into the CACHE at the top of the recursive function and an insertion into the CACHE at the bottom of the function.
int LISdp(int arr[], int n, int currIndex, int maxVal, CACHE& cache) {
if (currIndex == n) {
return 0;
}
// check our cache if we've already solved for currIndex and maxVal together
auto itor1 = cache.find(currIndex);
if (itor1 != cache.end())
{
// itor1->second is a reference to cache[currIndex]
auto itor2 = itor1->second.find(maxVal);
if (itor2 != itor1->second.end())
{
// itor2->second is a reference to cache[n][maxVal];
return itor2->second;
}
}
int included = 0, notIncluded = 0;
if (arr[currIndex] > maxVal) {
included = 1 + LISdp(arr, n, currIndex + 1, arr[currIndex], cache);
}
notIncluded = LISdp(arr, n, currIndex + 1, maxVal, cache);
// cache the final result into the 2-d map before returning
int finalresult = std::max(notIncluded, included);
cache[currIndex][maxVal] = finalresult; // cache the result
return finalresult;
}
Then the initial invocation with the input set to solve for is effectively passing INT_MIN as the intial maxVal and an empty cache:
int N = 16
int A[N]={0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
CACHE cache;
int result = LISdp(A, N, 0, INT_MIN, cache);
A minor optimization is to make a, n, and cache a member variable of the C++ class encapsulating your solution so that they don't have to be pushed onto the stack for each step of the recursion. The cache is getting passed by reference, so it's not that big of a deal.
You have 2 problems in your code:
Mistake 1
First in C++, the size of an array must be a compile-time constant.So, take for example the following code snippets:
int n = 10;
int arr[n]; //INCORRECT because n is not a constant expression
The correct way to write the above would be:
const int n = 10;
int arr[n]; //CORRECT
Similarly, the following(which you did in your code example) is incorrect:
int n;
cin >> n;
int arr[n];// INCORRECT because n is not a constant expression
Mistake 2
Second in your function LISdp, it seems to me that there is no need of the statement
if (dp[currIndex] != -1) return dp[currIndex];//no need for this statement
You should just remove this(the above) statement and the program produces expected output 4 as can be seen here. Basically you have not thought this(LISdp's working) through. You can use the debugger to see where you're going wrong.
There might be other problems in your code but so far i am able to spot these 2.
I am solving Maximum Subarray Sum with One Deletion on LeetCode:
Given an array of integers, return the maximum sum for a non-empty subarray (contiguous elements) with at most one element deletion. For input arr = [1,-2,0,3], output should be 4.
I came up with a recursive solution as below:
class Solution {
public:
int helper(vector<int>& n, vector<int>& cache, int startIndex) {
if(startIndex>=n.size()) return INT_MIN;
if(cache[startIndex]!=-1) return cache[startIndex];
int allInclusiveSum=0, sumWithOneDel=0, lowestVal=INT_MAX, maxVal=INT_MIN;
for(int i=startIndex; i<n.size(); i++) {
allInclusiveSum+=n[i];
maxVal=max(maxVal, allInclusiveSum);
if(i!=startIndex) {
lowestVal=min(lowestVal, n[i]);
sumWithOneDel=allInclusiveSum-lowestVal;
maxVal=max(maxVal, sumWithOneDel);
}
}
maxVal=max(maxVal, helper(n, cache, startIndex+1));
return cache[startIndex]=maxVal;
}
int maximumSum(vector<int>& arr) {
int i=0, first=arr[0];
for(i=1; i<arr.size(); i++)
if(arr[i]!=first) break;
if(i==arr.size()) return first;
vector<int> cache(arr.size(), -1);
return helper(arr, cache, 0);
}
};
Unfortunately, this TLEs. Since I call recursively with startIndex+1, I don't really think I am encountering overlapping sub-problems.
Is there a way I could memoize my solution? If no, why?
With dynamic programming, we would just define a std::vector with N rows and two columns, then run through our arr in one pass, and use std::max to find max_sum:
#include <vector>
#include <algorithm>
class Solution {
public:
static inline int maximumSum(const std::vector<int> &arr) {
int length = arr.size();
std::vector<std::vector<int>> dynamic_sums(length, std::vector<int>(2, 0));
dynamic_sums[0][0] = arr[0];
int max_sum = arr[0];
for (unsigned int row = 1; row < length; row++) {
dynamic_sums[row][0] = std::max(arr[row], dynamic_sums[row - 1][0] + arr[row]);
dynamic_sums[row][1] = std::max(arr[row], std::max(dynamic_sums[row - 1][1] + arr[row], dynamic_sums[row - 1][0]));
max_sum = std::max(max_sum, std::max(dynamic_sums[row][0], dynamic_sums[row][1]));
}
return max_sum;
}
};
It's similarly O(N) time and O(N) memory.
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions with a variety of languages and explanations, efficient algorithms, as well as asymptotic time/space complexity analysis1, 2 in there.
I wrote some code to perform descending Merge Sort.
void Merge(int a[], int low, int high, int mid)
{
int i=low,j=mid+1,k=0;
int temp[high-low+1];
while(i<=mid && j<= high)
{
if(a[i]>a[j]) //comparison step
temp[k++]=a[i++];
else
temp[k++]=a[j++];
}
while(i<=mid)
{
temp[k++]=a[i++];
}
while(j<=high)
{
temp[k++]=a[j++];
}
for(i=low;i<=high;i++)
{
a[i]=temp[i-low];
}
return;
}
void MergeSort(int a[],int low, int high)
{
int mid;
if(low<high)
{
mid=(low+high)/2;
MergeSort(a,low,mid);
MergeSort(a,mid+1,high);
Merge(a,low,high,mid);
}
return;
}
void output(int *a,int n)
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<"\t";
}
}
int main()
{
int n=12;
int a[n]={23,45,1,2,8,0,9,56,73,5070,20,16};
MergeSort(a,0,n);
output(a,n);
}
This code works perfectly when the order is ascending, ie. comparsion is a[i] < a[j]
However by using a[j] > a[i] MergeSort beings sorting in descending order, but it will include some random large number right at the beginning of the array. I really can't figure out why this is happening.
Arrays in C++ are zero based. However, your code happily access a[high] which gets passed the value 12. Thus, you got an out of bounds access. The same error happens if you sort your array ascending but since you don't print a[12] (you output() function uses a range exclusive of n) you don't see it.
I strongly recommend to adopt a programming style where ranges are inclusive of the start value and exclusive of the end value. Of course, I'd also recommend using iterators for ranges rather than indices anyway. For these the convention is more obvious.
The quick fix (which doesn't change how your MergeSort() to be exclusive of the last element) is to apply two changes:
Change the termination check in MergeSort() to become
if (1 < high - low)
Call MergeSort() with the last element of the array
MergeSort(a, 0, n - 1);
Note that variable sized built-in arrays like int temp[high - low + 1] are not part of standard C++ even though they are supported by some compilers as an extension (e.g., g++). For bigger arrays they also cause problems as they are bound to overflow the stack. You are much better off using std::vector<int>:
std::vector<int> temp(high - low + 1);
For the test array you can use a static sized array for which the compiler determines the size and have the compiler also figure out the array:
int a[]={23,45,1,2,8,0,9,56,73,5070,20,16};
int n = std::end(a) - std::begin(a); // need <iterator> to be included
#include<iostream>
#include<string>
#include<sstream>
#include<conio.h>
#include<vector>
#define MAX 100
using namespace std;
int size;
int getlargest(int arr[ ]){
static int max = -1000;
static int i = 0;
if(i < size){
if(arr[i] >= max){
max = arr[i];
i++;
}
getlargest(arr);
}
return max;
}
int main(){
int res;
cout << "enter the size please: ";
cin >> size;
int arr[MAX];
for(int i=0;i<size;i++){
cin >> arr[i];
}
res = getlargest(arr);
cout << res;
getch();
return 0;
}
I am not experienced with the concept of recursive functions. This code was written to find the maximum element of an array. However, I am getting a stack overflow error. Could anyone correct it? Also, I don't know exactly where to insert recursion.
You have several problems, all of them small.
First, you make no progression through the array: you always call with the same argument, that being the entire array. The strategy of recursion is to do something simple, and then reduce the problem to something smaller when you call again.
In this case, you have the concept right: check one element against the largest of the rest of the list. You do recur to find the maximum, but you don't reduce the list. You also don't actually work well with the list max. For instance, note that (on each call) you're returning the max to the previous level ... but you don't save it at all.
Try this instead:
take the first element off the list;
find the max of the remainder;
return the larger of those two.
The code might look like this:
if(arr.size() == 1) {
return arr[0]
else {
max = getlargest(++arr);
if (arr[0] >= max)
max = arr[0]
}
return max;
Note the little C trick here: ++arr increments arr, as an array reference, to the next element. You may have seen this already as a character pointer and string variable.
Well, it seems you're trying to do something that is easier to do with a loop than recursion. You can implement getlargest() like this:
int getlargest(int arr[]) {
int max = arr[0]; // This is safer than initializing max with 0. What if arr[0] is the largest element and all elements are below 0?
for (int i = 0; i < size; ++i)
if (arr[i] > max)
max = arr[i];
return max;
}
I am not experienced with the concept of recursive functions.
Assuming you want to learn how to use recursion, you should take a look at factorial. It's a no-brainer to find the factorial of an integer i using a recursive function.
What is the fastest method to check if all elements of an array(preferable integer array) are equal. Till now I have been using the following code:
bool check(int array[], int n)
{
bool flag = 0;
for(int i = 0; i < n - 1; i++)
{
if(array[i] != array[i + 1])
flag = 1;
}
return flag;
}
int check(const int a[], int n)
{
while(--n>0 && a[n]==a[0]);
return n!=0;
}
Here is a solid solution which is valid C++11.
The advantages is that you do not need to manually play with the indexes or iterators. It is a best practice to
prefer algorithm calls to handwritten loops [Herb Sutter - C++ Coding Standards]
I think this will equally efficient as Paul R's solution.
bool check(const int a[], int n)
{
return !std::all_of(a, a+n, [a](int x){ return x==a[0]; });
}
Once you have found a mismatching element you can break out of the loop:
bool check(const int array[], int n)
{
for (int i = 0; i < n - 1; i++)
{
if (array[i] != array[i + 1])
return true;
}
return false;
}
If this is performance-critical then it can be further optimised slightly as:
bool check(const int array[], int n)
{
const int a0 = array[0];
for (int i = 1; i < n; i++)
{
if (array[i] != a0)
return true;
}
return false;
}
Recast the array to a larger data type. Eg, operate on 64bit ints, or use SSE or AVX intrinsics for 128 or 256 bit operation. For example, the SSE2 intrinsic is _mm_cmpeq_epi32, whose result you'll use with _mm_or_si128. Check the result with repeated application of _mm_srli_si128 and _mm_cvtsi128_si32. Check the result every few hundred iterations for early exit.
Make sure to operate on aligned memory, check the unaligned start and end as ints, and check the first packed element with itself.
For programmer efficiency you may try the following all in one line.
vector<int> v{1, 1, 1, 1};
all_of(v.cbegin(), v.cend(), [&r=v[0]](int value){ return value == r; }->bool);
I did not test run this code, let me know if there is syntax error.
Find a library that's available on your platform that supports threading or parallel-for loops, and split the computation out such that different cores test different ranges of the array.
Some available libraries are listed here:
http://parallel-for.sourceforge.net/parallelfor.html
Or possibly, you can make use of the parallism that many GPU's offer.
bool check(int array[],int n)
{
// here 1st element is checked with others. This decreases the number of iteration by 1.
// also it returns immediately.
// The requirement is to check if all the elements are equal.
// So if 1st element is equal to others then all elements are equal.
// Otherwise the elements are not equal.
for(int i=1;i<n;i++)
{
if(array[0]!=array[i])
return false;
}
return true;
}
We'll it's basically an O(n) operation so you can't do much better than what you have, other than dispensing with the flag and just return false; on the first failure and return true; after the iteration.
In theory, I would propose this:
bool check_single(const int a[], int n)
{
for (int i = 1; i < n; ++i) {
if (a[0] != a[n]) { return false; }
}
return true;
}
Compared to other (already proposed) versions:
a[0] will be hoisted outside the loop by the compiler, meaning a single array access within the loop
we loop from 0 to n, which is better (access-wise) than loading a[0] and then looping from a[n]
Obviously, it still checks N elements and thus is O(N).
fast hash mapping technique:
bool areSame(int a[],int n)
{
unordered_map<int,int> m; //hash map to store the frequency od every
for(int i=0;i<n;i++)
m[a[i]]++;
if(m.size()==1)
return true;
else
return false;
}
I think the following is more readable than the highest rated answer and I would wager more efficient too (but havent benchmarked)
bool check(int a[], int n)
{
if (n)
{
auto first = a[0];
for(int i = 1; i < n; i++)
{
if(array[i] != first) return false;
}
return true;
}
return true; //change to false for the OPs logic. I prefer logical true here
}
bool check_identity (int a[], int b[], const int size)
{
int i;
i = 0;
while ((i < size-1) && (a[i] == b[i])) i++;
return (a[i] == b[i]);
}