【Theory】前缀和及其应用

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/meiqi0538/article/details/100101138

1 什么是前缀和

假设我们有一个数组 x = [ x 0 , x 1 ,   , x n ] x=[x_0, x_1, \cdots,x_n] ,那么对应的前缀和数组y满足下式:
y j = i = 0 j x i y_j=\sum_{i=0}^jx_i
例如 x = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 ] x=[0,1,2,3,4,5,6] ,那么前缀和数组就是: y = [ 0 , 1 , 3 , 6 , 10 , 15 , 21 ] y=[0,1,3,6,10,15,21]
程序实现

def oneDimen(src):
	result = [0]
	for i in range(len(src)):
		result.append(src[i] + result[-1])
	result.pop(0)
	return result

if __name__ == '__main__':
	tmp = [i for i in range(1, 7)]
	print(oneDimen(tmp))

2 二维前缀和

前面通过是一维数组介绍了什么是前缀和数组,那么二维的前缀和数组又是什么样的呢?其实也就是一句话的事:前缀和数组中每一个位置的数字都表示当前index左上方数字的和,如下图所示:
二维前缀和
原理很简单,但是如何快速计算出来前缀和也是一个问题,当然你也会想到我每一次计算都遍历一下二维数组不就行了?当然,这个是没问题的,但是每次都会有大量的重复计算,如果我们充分利用上几个计算的前缀和项,那么就会大大降低计算步骤,降低计算时间。在实际计算中有3种情况,在进行介绍之前我们需要假设一些内容:行数、列数用j,i表示并且都是从0开始,原数组、前缀和数组用src、prefixSum表示,则有:

  • i==0 并且 j==0,也就是起始计算的前缀和,直接赋值即可:prefixSum[0,0]=src[0,0]
  • 第0行、第0列,也就是上图中最上面一行和最左边一列,这个时候,我们可以借鉴一维前缀和的计算方式计算有分别为:prefixSum[0,j]=prefixSum[0,j-1]+src[0,j],prefixSum[i,0]=prefixSum[i-1,0]+src[i,0]
  • 其他情况,i!=0 或者 j!=0,观察由下图所示,prefixSum[i][j] = prefixSum[i - 1][j] + prefixSum[i][j - 1] + src[i][j] - prefixSum[i - 1][j - 1];(注:别忘了减去重合的部分),下图的第二个图,你会发现prefixSum[2,2] = prefixSum[1, 2] + prefixSum[2, 1]=33+21=54,这里纯属巧合,仔细想一下也不难发现。
    其他情况

代码实现

def twoDimen(src):
	result = np.zeros_like(src)
	row, col = src.shape
	for i in range(row):
		for j in range(col):
			if i== 0 and j == 0:
				result[0][0] = src[0][0]
			elif i == 0:
				result[i][j] = result[i][j - 1] + src[i][j]
			elif j == 0:
				result[i][j] = result[i - 1][j] + src[i][j]
			else:
				result[i][j] = result[i - 1][j] + result[i][j - 1] + src[i][j]  - result[i - 1][j - 1]

	return result

if __name__ == '__main__':
	tmp = np.arange(0,25).reshape(-1, 5)
	print(twoDimen(tmp))

3 前缀和的应用

我们看一道题目:

输入n个数的数列,所有相邻m数的和有n-m+1个,求其中的最小值。
比如:
数组为:[10, 4, 1, 5, 5, 2]
m为:3
结果为:10

解题思路:题目给出一个有6个元素的数列,那么相邻3个数字的和的数列就有4个,当然这也有其他的解题方式,例如窗口法。这里我们可以使用前缀和的方式进行解答,根据这个数列,我们可以得到一个前缀和的数组,我们直接可以从第m个前缀值开始遍历比较,到下一个的时候前缀值时,我们减去第一个值再进行比较,依次类推即可。
程序实现

def example(src, m):
	prefixSum = oneDimen(src)
	result = prefixSum[m - 1]  # 初始化最小值为第一个值
	for i in range(m, len(src)):
		cur = prefixSum[i] - prefixSum[i - m] 
		result = result if result < cur else cur
	return result
if __name__ == '__main__':
	tmp = [10, 4, 1, 5, 5, 2]
	print(example(tmp, 3))

4 本文全部代码如下:

import numpy as np
def oneDimen(src):
	result = [0]
	for i in range(len(src)):
		result.append(src[i] + result[-1])
	result.pop(0)
	return result

def twoDimen(src):
	result = np.zeros_like(src)
	row, col = src.shape
	for i in range(row):
		for j in range(col):
			if i== 0 and j == 0:
				result[0][0] = src[0][0]
			elif i == 0:
				result[i][j] = result[i][j - 1] + src[i][j]
			elif j == 0:
				result[i][j] = result[i - 1][j] + src[i][j]
			else:
				result[i][j] = result[i - 1][j] + result[i][j - 1] + src[i][j]  - result[i - 1][j - 1]

	return result
def example(src, m):
	prefixSum = oneDimen(src)
	result = prefixSum[m - 1]  # 初始化最小值为第一个值
	for i in range(m, len(src)):
		cur = prefixSum[i] - prefixSum[i - m] 
		result = result if result < cur else cur
	return result


if __name__ == '__main__':
	# tmp = [i for i in range(1, 7)]
	# print(oneDimen(tmp))
	# tmp = np.arange(0,25).reshape(-1, 5)
	# print(twoDimen(tmp))
	tmp = [10, 4, 1, 5, 5, 2]
	print(example(tmp, 3))

reference

前缀和

个人订阅号

我的订阅号

猜你喜欢

转载自blog.csdn.net/meiqi0538/article/details/100101138