数组的n个连续元素的最大总和

时间:2015-08-08 08:01:49

标签: arrays algorithm optimization

如何找到数组n连续数的最大总和?例如,如果我们的数组是{2,5,3,4,6}n == 2,则输出应为10(即6 + 4)。

我能够为数组大小的小值和n的小值获得正确的逻辑。但是当数组大小和n太大,大约10 5 时,我的代码需要花费很多时间。请建议优化方法。

我的代码剪断了:

for(int i = 0; i <= n - h; i++) {
  int count = 0;
  for(int k = i; k < i + h; k++) {
    count = count + arr[k];
  }
  if(i == 0) {
    ans[z] = count;
  } else if(i != 0) {
    if(count < ans[z]) {
      ans[z] = count;
    }
  }
  count = 0;
}

11 个答案:

答案 0 :(得分:5)

这是我的想法:将数组从0遍历到(数组长度 - N),并使用以下表达式确定下一个N项的总和:
    下一个N项目的总和=前一个总和 - 前一个子阵列中的第一个项目+下一个子阵列中的最后一个项目

实施例:

数组= {2,5,3,4,6}

当i = 0时,sum =(2 + 5)= 7,max sum = 7

当i = 1时,sum = 7-2 + 3 = 8,因为8> 1。 7,所以max sum = 8

当i = 2时,sum = 8 - 5 + 4 = 7,因为7

当i = 3时,sum = 7-3 + 6 = 10,因为10> 8,所以max sum = 10

下面的

是c#

中的示例代码
static int GetLargestSum(int[] array, int n)
{
    int largestSum = 0;
    int previousSum = 0;

    for (int i = 0; i <= array.Length - n; i++)
    {
        if (i == 0)
        {
            for (int j = 0; j < n; j++)
            {
                largestSum += array[j];
            }

            previousSum = largestSum;
        }
        else
        {
            int currentSum = previousSum - array[i - 1] + array[i + n - 1];
            if (currentSum > largestSum)
            {
                largestSum = currentSum;
            }
            previousSum = currentSum;
        }
    }

    return largestSum;
}

答案 1 :(得分:1)

使用Java 8。

int[] numArr = {1,3,5,6,7,12,14,2,3,4}; 
List<Integer> intList = Arrays.stream(numArr).boxed().collect(Collectors.toList());         
List<Integer> intSumList = new ArrayList<>();
for(int i=0; i<=numArr.length-3; i++)
{
    int intSum = intList.stream().skip(i).limit(3).mapToInt(Integer::intValue).sum();       
    intSumList.add(Integer.valueOf(intSum));
}       
int maxConsecutiveSum = intSumList.stream().reduce(Integer::max).get(); 
System.out.println("Max sum using 3 consecutive integers :"+maxConsecutiveSum);

答案 2 :(得分:0)

您的技术可能的主要改进是滑动窗口。整数加法有一个倒数减法。这使得可以从总和中删除元素并添加新元素,而不是从头开始重新计算。 (Terryfkjc's answer demonstrates this)。

正如Terryfkjc评论他的回答,那就是有线程级并行性。您可以让不同的线程检查数组的不同部分。如果滑动窗口的大小超过数组大小的一半,那么应该对第一个n进行求和。当n大约是数组大小的一半时,在线程之间划分工作是最棘手的。更大,它不能滑到目前为止。更小,大部分工作只是滑动。

如果您使用矢量指令定位CPU,我们可以使用SIMD轻松地对第一个w(窗口大小/宽度)元素的初始总和进行矢量化。例如,使用x86 SSE(C / C ++内在函数):

#include <immintrin.h>
const __m128i *array_vec = (__m128i*)array;
__m128i sums = _mm_loadu_si128(array_vec);  // or start with _mm_setzero_si128()

 // careful to get the loop start/end and increment correct,
 // whether you count by vectors (i++) or by elements (i+=4)
 // You may need a scalar cleanup at the end if window width isn't a multiple of the vector width
for (int i=1; i < width/4 ; i+=1) {
    sums = _mm_add_epi32(sums, _mm_loadu_si128(array_vec+i));
    // if you know the input is aligned, and you're going to compile without AVX/AVX2, then do:
    // sums = _mm_add_epi32(sums, array_vec[i]);
}
__m128i hisums = _mm_bsrli_si128(sums, 8);  // get the high 2 elements into the low 2 elements of a separate vector
sums = _mm_add_epi32(sums, hisums);  // one step of horizontal merging.    
hisums = _mm_bsrli_si128(sums, 4);  // actually, pshufd would be faster for this, since it doesn't need a mov to preserve sums.
sums = _mm_add_epi32(sums, hisums);

int window_sum = _mm_cvtsi128_si32(sums);  // get the low 32bit element 

我认为不可能对滑动窗部件进行矢量化。我们无法有效地滑动4个单独的窗口,因为每个窗口(向量元素)都需要查看每个数组元素。

然而,如果我们对4/8/16不同的窗口大小(最好是连续的)感兴趣,那么我们可以用向量来做。因此,在每次循环迭代中,我们都有一个带有当前滑动窗口总和的向量。使用SSE4.1 pmaxsd(对于带符号的32位整数),我们可以做

window_max = _mm_max_epi32(window_max, window_sums);

滑动窗口操作变为:添加{ a[i], a[i+1], a[i+2], a[i+3] },同时从所有4个向量元素中删除相同的数组元素。 (或者,广播要添加的元素,并减去不同元素的向量。)

__m128i add_elements = _mm_loadu_si128((__m128i*)&array[i]);  // load i, i+1, etc.
__m128i sub_element = _mm_cvtsi32_si128(array[i-width-1]);
  sub_element = _mm_shuffle_epi32(sub_element, 0); // broadcast the low element of the vector to the other positions.
  // AVX2: use _mm_broadcastd_epi32
  // AVX512 has a broadcast mode for memory references that can be used with most instructions.  IDK how to use it with intrinsics.
__m128i diff = _mm_sub_epi32(add_elements, sub_element);
window_sums = _mm_add_epi32(window_sums, diff);

正确地获取循环开始/结束条件,并正确地计算最后几个元素,这始终是向量的挑战。

有关如何在x86上使用向量的更多信息,请参阅https://stackoverflow.com/tags/x86/info

答案 3 :(得分:0)

使用滑动窗口,其中窗口大小为2

arr = [2,5,3,4,6];

sum = 0;
temp_sum = 0;

const add = (a,b) => a+b;

for(let i=0;i<arr.length-1;i++){
  console.log(i, i+2,arr.slice(i,i+2));
  temp_sum = add.apply(null, arr.slice(i,i+2));
  if(sum < temp_sum){
    sum = temp_sum;
  }
}

console.log(sum);

答案 4 :(得分:0)

这是一个简短的快速解决方案:

    s = 0
    arrayMaxConsecutiveSum = (a, k) =>
    Math.max(...
        a.map(x => s += x - ~~a[~--k])
    )

答案 5 :(得分:0)

function maxSubelementsSum(inputArray, count) {
  let maxSum = 0;
  for (let i = 0; i < count; i++) {
    maxSum += inputArray[i];
  }
  let tempSum = maxSum;
  for (let i = count; i < inputArray.length; i++) {
    tempSum = tempSum + inputArray[i] - inputArray[i - count];
    maxSum = Math.max(tempSum, maxSum);

  }
  return maxSum;
}

答案 6 :(得分:0)

def maxSubarraySum(array, noOfelements):
    start = 0
    sum = 0
    nextElement = 1
    maxSum = []
    while noOfelements < len(array)+1:
        for i in range(start, noOfelements):
            sum += array[i]
        maxSum.append(sum)
        sum = 0
        start = nextElement
        nextElement += 1
        noOfelements += 1
    return max(maxSum) if maxSum else None


if __name__ == '__main__':
    print(maxSubarraySum([-1, -2, -5, -3, -8, -1, -5], 4) == -11)
    print(maxSubarraySum([1, -2, 5, 3, -8, 1, 5], 4) == 7)
    print(maxSubarraySum([1, 2, 5, 3, 8, 1, 5], 4) == 18)
    print(maxSubarraySum([1, 2, 5, 3, 8, 1, 5], 2) == 11)
    print(maxSubarraySum([], 4) == None)

复杂度 = O(n*n) 这适用于所有给定的数组,包括负数。

答案 7 :(得分:0)

有很多方法可以解决这个问题。
到目前为止,我能想到的最优化的是 O(n)。
您可以遍历数组一次的地方。

我使用了滑动窗口模式来解决这个问题

这是解决方案背后的主要思想:

  • 获取前 n 的总和并存储在变量中作为临时最大值
  • 将您的最大值设置为临时最大值
  • 遍历数组
  • 在循环中的任何一点,从临时最大值中减去前一个元素
  • 并将下一个n索引中的元素添加到临时max
  • 最后将您的主要最大值临时最大值
  • 进行比较
  • 如果最大值较小,则将最大值设置为临时最大值
  • function maxSubarraySum(array, num){
    
        let tempMax = 0
        
        for(let i=0; i<num; i++){
            tempMax += array[i] 
        }
        
        let max = tempMax
    
    
    
        for(i=1; i<array.length - (num-1); i++){
    
            // Substract the element you are at from the max
            // Add the element at the ith + num
            // compare with the max and reinitialize
    
            let subs = tempMax - array[i-1]
    
            tempMax = subs + array[i + num - 1]
    
            if(max < tempMax){
               max = tempMax
            }
              
        }
    
        return(max)
        
    }
    
    

    答案 8 :(得分:-1)

    最简单的

    A=list(map(int,input().split(" ")))
    n=len(A)
    max=0
    for i in range(n-1):
        if(A[i]+A[i+1]>max):
            max=A[i]+A[i+1]
    print(max)
    

    答案 9 :(得分:-1)

    简单地我们可以迭代数组并找到两个连续数字之和

    let array = [2,5,3,4,6];
    let sum=0;
    let maxNumber = 0;
    for(let i=0; i<array.length;i++){
        sum = array[i] + array[i+1];
        if(sum >= maxNumber){
            maxNumber = sum;
        }
        
    }
    console.log("max of two digit :", maxNumber);
    

    答案 10 :(得分:-2)

    我建议你首先使用mergesort对数组进行排序,其复杂时间为O(n log(n))然后你只需找到前n个连续数字,从较高的数字迭代数组到较低的一个保持当前连续数字的总和,这将花费复杂时间O(n)。问候