1 什么是前缀和
假设我们有一个数组
x
=
[
x
0
,
x
1
,
⋯
 
,
x
n
]
x=[x_0, x_1, \cdots,x_n]
x=[x0,x1,⋯,xn],那么对应的前缀和数组y满足下式:
y
j
=
∑
i
=
0
j
x
i
y_j=\sum_{i=0}^jx_i
yj=i=0∑jxi
例如
x
=
[
0
,
1
,
2
,
3
,
4
,
5
,
6
]
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]
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
个人订阅号