Алгоритм «разделяй и властвуй» для суммы массива целых чисел

У меня возникли проблемы с алгоритмами «разделяй и властвуй», и я искал помощи. Я пытаюсь написать функцию с именем sumArray, которая вычисляет сумму массива целых чисел.

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

Ниже приведен код, который я написал для sumArray, который компилируется, но не возвращает правильный результат.

int sumArray(int anArray[], int size)
{
    int total = 0;
    //base case
    if (size == 0)
    {
        return 0;
    }
    else if (size == 1)
    {
        return anArray[0];
    }

    //divide and conquer
    int mid = size / 2;
    int lsum = anArray [mid] + sumArray(anArray, --mid);
    int rsize = size - mid;
    int rsum = anArray[size - mid] + sumArray(anArray + mid, --rsize);
    return lsum + rsum;
}

Я определил проблему в том, что функция включает значение lsum в вычисление rsum. Я знаю, что проблема заключается в моем рекурсивном вызове sumArray с использованием rsize (переменная, равная размеру исходного массива минус средняя точка). Однако по какой-то причине я не могу найти исправление.

Я чувствую себя глупо спрашивать, так как знаю, что ответ смотрит мне прямо в лицо, но как мне исправить мою функцию, чтобы она возвращала точный результат?

ОБНОВЛЕНИЕ. Благодаря всем полезным ответам я исправил свой код, теперь он компилируется и работает нормально. Я оставлю свой исходный код здесь на случай, если другие борются с разделяй и властвуй и могут совершать аналогичные ошибки. Чтобы узнать о функции, которая правильно решает проблему, см. ответ @Laura M. Ответ @haris также дает хорошее объяснение того, где в моем коде возникали ошибки.


person Nea    schedule 13.10.2014    source источник
comment
Пробовали ли вы свою программу с небольшой выборкой, скажем, из 4 элементов? Вот как вы должны в конечном итоге понять это (также используйте отладчик, чтобы выполнить код).   -  person PaulMcKenzie    schedule 13.10.2014
comment
int lsum = anArray [mid] + sumArray(anArray, --mid); Попахивает неопределенным поведением. Вы меняете mid и используете его как индекс массива, все в той же последовательности.   -  person PaulMcKenzie    schedule 13.10.2014
comment
@ldgorman, спасибо, что напомнили! Я был так поглощен работой над остальной частью задачи, что забыл вернуться и принять ответ.   -  person Nea    schedule 15.10.2014


Ответы (4)


В вашем коде:

int mid = size / 2;
int lsum = anArray [mid] + sumArray(anArray, --mid);
int rsize = size - mid;
int rsum = anArray[size - mid] + sumArray(anArray + mid, --rsize);

мы можем проиллюстрировать ошибку на примере, где массив равен { 2, 3, 4, 5, 6, 9} и size = 6.

теперь, когда вы делаете mid = size / 2, а затем:

int lsum = anArray [mid] + sumArray(anArray, --mid);
int rsize = size - mid;
int rsum = anArray[size - mid] + sumArray(anArray + mid, --rsize);

число 5 добавляется дважды (один раз в lsum, а затем в rsum), потому что mid == (size - mid).

Кроме того, вызов sumArray() в rsum должен иметь параметры sumArray(anArray + (mid + 1), --rsize), так как элемент mid уже добавлен в lsum.

С другой стороны, вы можете использовать гораздо более простой код для рекурсии, например...

int add(int low,int high,int *a)
{
    int mid;
    if(high==low)
        return a[low];
    mid=(low+high)/2;
    return add(low,mid,a)+add(mid+1,high,a);
}
person Haris    schedule 13.10.2014
comment
Спасибо за ваш ответ, объяснение очень помогло понять, где я ошибся. - person Nea; 15.10.2014

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

Вы всегда передаете один и тот же массив своим рекурсивным вызовам lsum и rsum. Сначала я думал, что это всего лишь часть вашей реализации и что об этом позаботится параметр размера. Однако параметр размера не работает так, как вы, возможно, предполагали. Все, что делает ваш алгоритм, — это уменьшает параметр size до тех пор, пока он не достигнет 1. Затем запускается базовый случай, и в результате всегда возвращается первый элемент исходного массива. Почему? Вы никогда не разбиваете массив в своем коде, поэтому один и тот же массив сохраняется при каждом рекурсивном вызове (даже в базовом случае).

Чтобы решить эту проблему, все, что sumarray() должен сделать, это разделить массив на левую половину и правую половину на основе промежуточного вычисления и рекурсивно передать этот новый массив до тех пор, пока размер массива не станет равным 1 (базовый случай), и вы вернете элемент в массиве. Это эффективно разбивает массив на отдельные элементы, и все, что функция должна сделать на этом этапе, — это сложить lsum и rsum.

Псевдокод:

sumArray(array[]){
    if size(array) is 1
          return array[0]

    mid = size(array) / 2 
    leftArray = splitArrayUpto(mid, array)
    rightArray = splitArrayAfter(mid+1, array)

    leftArraySum = sumArray(leftArray)
    rightArraySum = sumArray(rightArray)

    return leftArraySum + rightArraySum
 }   
person Wenqin Ye    schedule 13.10.2014
comment
он разделяет массив с помощью этой операции: anArray + mid - person Laura Maftei; 13.10.2014
comment
Разве mid не целое число? Как добавление целого числа к массиву целых чисел разделяет массив целых чисел? - person Wenqin Ye; 13.10.2014
comment
Переменная anArray указывает на первый элемент массива, anArray + mid укажет на средний элемент - person Laura Maftei; 13.10.2014

person    schedule
comment
да, int anArray[] = { 0, 1, 2, 3, 4, 5}; //15 int size = 6; дает 19 - person ldgorman; 13.10.2014
comment
я просто редактирую его, чтобы получить одинаковый результат во всех компиляторах. я тестировал его только с VS2012 в начале - person Laura Maftei; 13.10.2014
comment
Это было действительно простое и понятное решение моей проблемы. Спасибо за Ваш ответ! - person Nea; 15.10.2014
comment
Пожалуйста, я рад, что это решение, которое вам нужно - person Laura Maftei; 15.10.2014
comment
@LauraMaftei Возможно ли создать эту рекурсивную функцию, подобную этой, но иметь в качестве параметра только массив, а не размер массива? Это действительно похоже на задание, над которым я работаю - person Alex Tănăsescu; 07.04.2020
comment
Какова временная сложность этого решения? - person Boobalan; 14.05.2020

person    schedule
comment
каждый ответ должен быть самостоятельным, если он специально не связан с другим. вот почему за вас проголосовали, хотя вы предоставили решение раньше всех. заметьте, я не проголосовал за вас - person ldgorman; 13.10.2014
comment
Спасибо за Ваш ответ. Ранее я обнаружил этот способ решения проблемы, но, к сожалению, с тех пор мне сказали, что я могу использовать только int anArray[] и int size в качестве параметров - person Nea; 13.10.2014