Search code examples
c++recursiondivide-and-conquer

Divide and Conquer - Buy or Sell? (Maximum Consecutive Subarray)


The gist of the program is to find the maximum sub array of a 1 dimensional array which has fluctuating stock prices using both a brute force method (Which is working!) and a divide and conquer method (which is not working). The intent of the program is to find the set of days (hence the lsub and rsub in the struct) and the maximum profit of those days.

Everywhere I have looked online such as this powerpoint shows that my code should work. I have also seen something similar to this, but in Java on StackOverflow, the implementation of that code also does not work. Here is the link to that Java program.

My code is as follows:

#include <stdio.h>
#include <iostream>
#include <climits>
#include <cstring>

struct funOut{
int lsub; //Left subscript
int rsub; //Right subscript
int maximum; //Contains the max value
 //This way, max has the absolute smallest value and only needs to be done once rather than at the beginning of the algorithm calls.
};

void load(int arr[], int n);
void brute(int arr[],  int n, funOut &out);
funOut dac(int arr[], int low, int high, funOut &out);
void findMax(int sumLval, int sumRval, funOut &sumMval, int low, int mid, int high, funOut &out);
funOut crossSum(int arr[], int low, int mid, int high);
void print(funOut out);

using namespace std;


int main(void)
{
   funOut out;

   string alg;
   cin >> alg;

   int n;
   cin >> n;

   int arr[n];

   // cout << n << endl;

   load(arr, n);
   if(alg[0] == 'b')
      brute(arr, n, out); //PARAMETERS NEEDED
   else if(alg[0] == 'd')
   {
      out.maximum = 0;
      out = dac(arr, 1, n-1, out);
   }

   else
   {
      cout << "\nERROR: No algorithm chosen, aborting program." << endl;
      return 0;
   }

   cout << "Before Print" << endl;
   print(out);
   return 0;
}

void load(int arr[], int n)
{
   cout << "Loading" << endl;
   for(int i=0; i<n; i++)
      cin >> arr[i];
}

void brute(int arr[], int n, funOut &out) //THIS WORKS!!!
{
   out.maximum = 0;
   int change;
   int temp = 0;
   for(int i=1; i<n-1; i++)
   {
      for(int j=i; j<n; j++)
      {
         change = arr[j] - arr[j-1];
         temp += change;
         if(temp > out.maximum){
            out.lsub = i;
            out.rsub = j;
            out.maximum = temp;
         }     
      }
      temp = 0;
   }
}

funOut dac(int arr[], int low, int high, funOut &out)
{
   cout << "DAC Start!" << endl;
   if(low == high)
   {
      out.lsub = out.rsub = low;
      out.maximum = arr[low];
      return out;
   }
   else
   {
      // cout << "DAC IF" << endl;
      int mid = (low + high)/2;

      funOut sumLval = dac(arr, low, mid, out);
      funOut sumRval = dac(arr, mid+1,high, out);
      funOut sumMval = crossSum(arr, low, mid, high);

      cout << "\nsumLval = " << sumLval.maximum << endl; 
      cout << "\nsumRval = " << sumRval.maximum << endl;
      cout << "\nsumMval = " << sumMval.maximum << endl;
      //FindMax
      if(sumLval.maximum >= sumRval.maximum && sumLval.maximum >= sumMval.maximum)
         return sumLval;         
      else if(sumRval.maximum >= sumLval.maximum && sumRval.maximum >= sumMval.maximum)
         return sumRval;
      else
         return sumMval;
   }
}



funOut crossSum(int arr[], int low, int mid, int high)
{
funOut sumMval;
int lsum = 0;
int rsum = 0;
int sum = 0;
int maxl, maxr;
//For loop finding lsum
for(int i=mid; i>=low; i--)
{
   cout << "DAC For I = " << i << endl;
   sum += arr[i];
   if(sum > lsum)
   {
      lsum = sum;
      maxl = i;
   }
}

sum = 0;

for(int j=mid+1; j<=high; j++)
{
   cout << "DAC For J = "<< j << endl;
   sum += arr[j];
   if(sum > rsum)
   {
      rsum = sum;
      maxr = j;
   }
}

sumMval.lsub = maxl;
sumMval.rsub = maxr;
sumMval.maximum = lsum + rsum;

return sumMval;
}

void print(funOut out)
{
   cout << "The max value is: ";
   cout << out.maximum << endl;
   cout << "The left subscript is: ";
   cout << out.lsub << endl;
   cout << "The right subscript is: ";
   cout << out.rsub << endl;
}

Sample data set: (They are supposed to be on seperate lines but it isn't letting me do so.)

d
17
100
113
110
85
105
102
86
63
81
101
94
106
101
79
94
90
97

The intended output is:

The max value is: 43

The left subscript is: 8

The right subscript is: 11


Solution

  • Conceptually, your brute-force method is doing a whole lot of redundant work.

    When you add up all the point-to-point differences in an array, all you get is the difference between the ends:

    int diff = (x[1] - x[0]) + (x[2] - x[1]) + (x[3] - x[2]) + (x[4] - x[3]);
    

    The above is the same as this (because all the other values cancel out):

    int diff = x[4] - x[0];
    

    So all your brute-force algorithm seems to be doing is searching for the i and j with the greatest difference constrained by i < j. This is equivalent to finding the min and max of the array, where min appears before the max.

    Looks to me like a problem for dynamic programming. I don't see how divide-and-conquer is useful for this problem.

    [edit]

    So, I think the problem is crossSum. It's doing something utterly different from brute. Surely you just want to find the index of the minimum value in the left (minl) and the index of the maximum value in right (maxr). The 'maximum' in that output struct is simply arr[maxr] - arr[minl].

    Funny thing is that the divide-and-conquer approach is still needlessly inefficient for this problem. Consider this:

    struct funOut f;
    f.lsub = 0;
    f.rsub = 0;
    f.maximum = 0;
    
    int minval = arr[0];
    int minidx = 0;
    
    for( int i = 1; i < n; i++ ) {
        if( arr[i] < minval ) {
            minval = arr[i];
            minidx = i;
            continue;
        }
    
        int delta = arr[i] - minval;
        if( delta > f.maximum ) {
            f.lsub = minidx;
            f.rsub = i;
            f.maximum = delta;
        }
    }