整数数组和的分治算法

Posted

技术标签:

【中文标题】整数数组和的分治算法【英文标题】:Divide and conquer algorithm for sum of integer array 【发布时间】:2014-12-08 06:19:02 【问题描述】:

我在使用分而治之的算法时遇到了一些麻烦,正在寻求帮助。我正在尝试编写一个名为 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;

我发现问题在于函数在计算 rsum 时包含了 lsum 的值。我知道问题在于我使用 rsize (一个等于原始数组大小减去中点的变量)对 sumArray 的递归调用。但是,出于某种原因,我似乎无法确定修复方法。

我觉得这个问题很愚蠢,因为我知道答案就在眼前,但是如何修复我的函数以使其返回准确的结果?

更新:感谢所有有用的回复,我已经修复了我的代码,以便它可以很好地编译和运行。我将把我的原始代码留在这里,以防其他人在分而治之的情况下苦苦挣扎,并且可能会犯类似的错误。有关正确解决问题的功能,请参阅@Laura M 的答案。 @haris 的回复也很好地解释了我的代码在哪里出错。

【问题讨论】:

您是否尝试过使用小样本(例如 4 个项目)的程序?这就是你最终应该如何解决这个问题(也使用你的调试器来单步调试你的代码)。 int lsum = anArray [mid] + sumArray(anArray, --mid); 这有未定义行为的味道。您正在更改 mid 并将其用作数组下标,所有这些都以相同的顺序进行。 @ldgorman 谢谢你提醒我!我忙于解决剩下的问题,以至于忘记了回来接受答案 【参考方案1】:
int sumArray(int anArray[], int size)

    //base case
    if (size == 0)
    
        return 0;
    
    else if (size == 1)
    
        return anArray[0];
    

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

【讨论】:

是的,int anArray[] = 0, 1, 2, 3, 4, 5; //15 int size = 6; 给出 19 我只是编辑它以在所有编译器中提供相同的输出。我一开始只用VS2012测试过 对于我遇到的问题,这是一个非常简单明了的解决方案。感谢您的回答! 不客气,我很高兴这是您需要的解决方案 @LauraMaftei 是否可以创建这样的递归函数,但只有数组作为参数而不是数组的大小?这与我正在处理的任务非常相似【参考方案2】:

在您的代码中:

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, 9size = 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)

此外,rsum 中对sumArray() 的调用应该有参数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);

【讨论】:

感谢您的回答,解释对找出我哪里出错很有帮助。【参考方案3】:
int sumArray(int anArray[],int start,int end)
     if(start==end)
        return anArray[start];
     if(start<end)
         int mid=(start+end)/2;
         int lsum=sumArray(anArray,start,mid-1);
         int rsum=sumArray(anArray,mid+1,end);
         return lsum+rsum+anArray[mid];

     
     return 0;


【讨论】:

每个答案都应该独立,除非特别链接到另一个。这就是为什么您被否决的原因,即使您在其他人之前提供了解决方案。注意我没有投票给你 感谢您的回答。我之前确实发现了这种解决问题的方法,但不幸的是,我被告知我只能使用 int anArray[] 和 int size 作为参数【参考方案4】:

正如哈里斯所说,在您的代码中,您将相同的数字添加到右和左和;但是,您的代码存在更大的问题。

你总是将相同的数组传递给你对 lsum 和 rsum 的递归调用。起初我认为这只是您实现的一部分,它会由 size 参数处理。但是,size 参数似乎无法正常工作,因为您可能希望它正常工作。 您的算法所做的只是减小 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
    

【讨论】:

它确实通过这个操作拆分了数组:anArray + mid mid不是整数吗?向整数数组添加一个整数如何拆分整数数组? 变量anArray指向数组的第一个元素,anArray + mid会指向中间元素【参考方案5】:
#include<iostream>
using namespace std;
int sum(int a[], int l, int r)

    if(l==r) return a[l];
    int mid = (l+r)/2;
    int lsum = sum(a,l,mid);
    int rsum = sum(a,mid+1,r);
    return lsum+rsum;


int main() 
    int b[] = 9,7,2,6,5,3;
    int fsum = sum(b,0,5);
    cout<<fsum;
    return 0;

【讨论】:

【参考方案6】:
int a[mx], tree[mx*3];

void sum(int node, int s, int e)
  if(s == e)
  
     tree[node] = a[s];
     return;
  

  int left = node*2;
  int right = node*2 + 1;
  int mid = (s+e)/2;

  sum(left, s, mid);
  sum(right, mid+1, e);

  tree[node] = tree[left] + tree[right];


cin >> n;
for(int i=1;i<=n;i++)
    cin >> a[i];

sum(1, 1, n);

Full Code Here

【讨论】:

请在您的回答中提供更多详细信息。正如目前所写的那样,很难理解您的解决方案。

以上是关于整数数组和的分治算法的主要内容,如果未能解决你的问题,请参考以下文章

暴力+分治+贪心+DP:最大子序列和

在数组中添加连续整数对的分治算法时遇到问题

硬件递归分治算法

分治法求解最大子段和问题

算法设计与分析--求最大子段和问题(蛮力法分治法动态规划法) C++实现

0.分治永远大于顺序?关于最大子序列和问题的思考