Разделяй и властвуй - покупай или продавай? (Максимальный последовательный подмассив)

Суть программы заключается в том, чтобы найти максимальный подмассив одномерного массива с колеблющимися ценами на акции, используя как метод грубой силы (который работает!), так и метод «разделяй и властвуй» (который не работает). Цель программы — найти набор дней (отсюда lsub и rsub в структуре) и максимальную прибыль за эти дни.

Везде, где я искал в Интернете, например, " Я тоже видел нечто подобное, но в Java на StackOverflow реализация того кода тоже не работает. Вот ссылка на эту программу Java.

Мой код выглядит следующим образом:

#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;
}

Пример набора данных: (Они должны быть на отдельных строках, но это не позволяет мне сделать это.)

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

Предполагаемый результат:

Максимальное значение: 43

Левый нижний индекс: 8

Правый нижний индекс: 11


person messem10    schedule 16.09.2012    source источник
comment
Пробовали пройти через отладчик?   -  person Tony The Lion    schedule 17.09.2012
comment
Откуда ты знаешь, что он не работает? Можете ли вы привести пример? Не могли бы вы вставить его в main, чтобы нам не пришлось гадать?   -  person Beta    schedule 17.09.2012
comment
Я скомпилировал его через g++, и он проходит, но результаты при выполнении алгоритма «разделяй и властвуй» неверны.   -  person messem10    schedule 17.09.2012
comment
@pyCthon Я отлаживал его с помощью cout во всей программе, но не могу найти источник, из-за которого он выходит из строя. Сейчас я нахожусь в точке, когда мой мозг мертв, что означает, что я смотрел на это так долго, что это вызывает головную боль.   -  person messem10    schedule 17.09.2012


Ответы (1)


Концептуально, ваш метод грубой силы выполняет много избыточной работы.

Когда вы складываете все различия между точками в массиве, все, что вы получаете, — это разница между концами:

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

Вышеупомянутое то же самое, что и это (поскольку все остальные значения компенсируются):

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

Таким образом, кажется, что весь ваш алгоритм грубой силы ищет i и j с наибольшей разницей, ограниченной i < j. Это эквивалентно нахождению минимума и максимума массива, где минимум стоит перед максимумом.

Мне кажется, это проблема для динамического программирования. Я не понимаю, насколько метод «разделяй и властвуй» полезен для решения этой проблемы.

[править]

Итак, я думаю, проблема в crossSum. Он делает что-то совершенно отличное от brute. Наверняка вы просто хотите найти индекс минимального значения слева (minl) и индекс максимального значения справа (maxr). «Максимум» в этой выходной структуре равен просто arr[maxr] - arr[minl].

Забавно, что подход «разделяй и властвуй» все еще излишне неэффективен для решения этой проблемы. Учти это:

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;
    }
}
person paddy    schedule 16.09.2012
comment
Изначально у меня была грубая сила, как вы сказали, но я не мог заставить ее работать, поэтому я выбрал ту, которая сработала. Разделяй и властвуй — это не то, что я добавил туда для развлечения, но это требование для задания. Я работаю над этой программой около 15+ часов. Шесть из этих часов были потрачены на разделы «Разделяй и властвуй». (Функции dac() и crossSum()) - person messem10; 17.09.2012
comment
Я не могу получить «предполагаемый результат» из вашего отредактированного вопроса, выполнив функцию brute. Вы что-то упустили? Для меня значения «17» и «113» в вашем наборе данных показывают максимальную прибыль. Если вы согласны с тем, что я сказал о том, как на самом деле работает ваша функция грубой силы, я могу предложить вам простой алгоритм для вычисления того же самого за линейное время. Если вы не согласны, то показанный вами код не делает того, что вы думаете. - person paddy; 17.09.2012
comment
О, моя ошибка!! 17 - это размер массива, не так ли =) - person paddy; 17.09.2012
comment
Итак, я думаю, что проблема в crossSum. Он делает что-то совершенно отличное от brute. Наверняка вы просто хотите найти индекс минимального значения слева и индекс максимального значения справа. «Максимум» — это просто arr[maxr] - arr[minl]. Забавно, что этот конкретный подход «разделяй и властвуй» по-прежнему неэффективен. - person paddy; 17.09.2012
comment
Единственная проблема с этим решением для crossSum заключается в том, что бывают случаи, когда это не так просто, как переход от минимума слева к наибольшему справа, он также подсчитывает изменения значений между ними. Я понимаю, что проще всего применить грубую силу, но мне также было поручено найти решение по принципу «разделяй и властвуй». Теперь, когда я смотрю на код грубой силы, которым вы поделились, он намного проще, чем я первоначально представлял. - person messem10; 17.09.2012
comment
Почему это не так просто? Это именно то, что делает ваша функция brute, и вы сказали, что она выдает правильный результат. Кстати, код, который я вам предоставил, не является брутфорсом. Это динамическое программирование, основанное на понимании того, что если появится лучший минимум, то никакое ранее возникшее минимальное значение не приведет к оптимальному решению. Напротив, алгоритм грубой силы рассматривает все возможности, включая те, которые гарантированно дадут худшие ответы, чем он уже вычислил. Мое решение O (N); разделяй и властвуй — O(NlogN); грубая сила O (N ^ 2) - person paddy; 17.09.2012
comment
Сейчас мы знакомимся с динамическим программированием, но с вашим объяснением кода это имеет смысл! Большое вам спасибо за вашу помощь. Я чувствую себя глупо из-за того, что не понимаю, что если появляется лучший минимум, то это лучший! - person messem10; 17.09.2012