728x90
728x90
부분합(Partial Sum) ; 누적합(Prefix Sum)
연속된 구간 $start$ 부터 $end$ 까지의 합 구하기
- 일차원 배열 a가 아래와 같이 초기화되어 있다.
X | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100 |
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] | a[8] | a[9] | a[10] |
- 배열 a에서 연속된 구간 $start$ 부터 $end$ 까지의 합을 $\displaystyle \sum_{k=\text{start}}^{\text{end}} a[k] = a[start] + a[start + 1] + \cdots + a[end -1] + a[end]$ 로 정의한다면 ($start ≤ end$), 구간 5부터 구간 9까지의 합 $\displaystyle \sum_{k=5}^{9}a[k] (= a[5] + a[6] + a[7] + a[8] + a[9] = 50 + 60 + 70 + 80 + 90)$ 는 $350$ 이 된다.
- 연속된 구간의 합을 단 한 번만 구하고자 한다면, 다음과 같이 for 문을 이용하여 코드를 작성하여도 전혀 무리는 없을 것이다.
for (int i = start; i <= end; i++) {
sum += a[i];
}
- 그러나 필요에 따라서 연속된 구간의 합을 여러 번 구해야할 때가 있는데, 그럴 때마다 매번 for 문의 순환이 발생하게 된다면 시간적인 효율성이 상당히 떨어지는 코드가 될 것이다.
- 이런 경우에 부분합(Partial Sum)을 이용한다면 연속된 특정 구간의 합을 쉽게 구할 수 있다.
부분합 구하기
X | 10 | 30 | 60 | 100 | 150 | 210 | 280 | 360 | 450 | 550 |
s0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] | s[9] | s[10] |
- 우선 또 다른 배열 $s$ 를 선언하고, $s[1]$ 에는 $\displaystyle \sum_{k=1}^{1} a[k]$ 의 값 $a[1]$ 를, $s[2]$ 에는 $\displaystyle \sum_{k=1}^{2} a[k]$ 의 값 $a[1] + a[2]$ 를, $s[3]$ 에는 $\displaystyle \sum_{k=1}^{3} a[k]$ 의 값 $a[1] + a[2] + a[3]$ 을, …, 마지막으로 $s[10]$ 에는 $\displaystyle \sum_{k=1}^{10} a[k]$ 의 값 $a[1] + a[2] + \cdots + a[10]$ 을 구한다.
- 즉, $s[n]$ 번째는 $\displaystyle \sum_{k=1}^{n} a[k]$ 의 값 $a[1] + a[2] + \cdots + a[n]$ 을 구한다.
for (int i = 1; i <= 10; i++) {
s[i] = s[i - 1] + a[i];
}
- $s[0]$ 의 값이 0으로 초기화되어 있고, $s[1]$ 은 $s[0]$ 과 $a[1]$ 를 더해서 $s[0] + a[1] = 0 + 10 = 10$ 이 된다.
- $s[2]$ 는 $\displaystyle \sum_{k=1}^{1} a[k]$ 에다 $a[2]$ 를 더해서 $s[1] + a[2] = 10 + 20 = 30$ 이 된다.
- $s[3]$ 는 $\displaystyle \sum_{k=1}^{2} a[k]$ 에다 $a[3]$ 를 더해서 $s[2] + a[3] = 30 + 30 = 60$ 이 된다.
- 이와 같은 방법으로 구간 1부터 n까지의 합 $\displaystyle s[n](=\sum_{k=1}^{n} a[k])$ 을 구하기 위해서 이것을 일반화시키면 $\displaystyle \sum_{k=1}^{n-1}a[k]$ 에다 $a[n]$ 를 더해서 $s[1]$ 부터 $s[n]$ 까지 값들을 단 한 번의 순환으로 모두 구할 수 있다.
cout << s[end] - s[start - 1] << endl;
- 연속된 구간 $start$ 부터 $end$ 까지의 합 $\displaystyle \sum_{k=\text{start}}^{\text{end}} a[k]$ 를 구하기 위해서는 구간 1부터 구간 $end$ 까지의 합 $\displaystyle \sum_{k = 1}^{\text{end}} a[k]$ 에서 구간 1부터 구간 $start$ - 1까지의 합 $\displaystyle \sum_{k = 1}^{\text{start} - 1} a[k]$ 의 값을 빼면$\displaystyle \sum_{k=\text{start}}^{\text{end}} a[k]$ 의 값을 구할 수 있다.
a[1]+a[2]+a[3]+ ... +a[start - 1]+ a[start] + ... + a[end - 1] + a[end]
-a[1]+a[2]+a[3]+ ... +a[start - 2]+a[start - 1]
------------------------------------------------------------------------------------------------
= a[start] + a[start + 1] + ... + a[end - 1] + a[end]
예제 : 구간 5부터 9까지의 합 $\displaystyle \sum_{k=5}^{9} a[k]$ 구하기
- 구간 1부터 구간 9까지의 합 $\displaystyle \sum_{k=1}^{9} a[k] = s[9] = 450$ 이 된다.
- 450은 처음부터 구간 9까지의 합 $a[1] + a[2] + a[3] + \cdots + a[9]$ 이므로, 여기서 처음부터 구간 4까지의 합 $\displaystyle \sum_{k=1}^{4} a[k] = s[4] = 100$, 즉 $a[1] + a[2] +a[3] + a[4]$ 를 빼면 $\displaystyle \sum_{k=5}^{9} a[k] = a[5] + a[6] + a[7] + a[8] + a[9] = 450 - 100 = 350$ 이 된다.
알고리즘
#include <iostream>
using namespace std;
int main() {
int a[11] = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }, s[11];
int start, end;
s[0] = 0;
for (int i = 1; i <= 10; i++) {
s[i] = s[i - 1] + a[i];
}
// 구간 5부터 구간 9까지의 부분합 구하기
start = 5, end = 9;
cout << s[end] - s[start - 1] << endl;
return 0;
}
350
누적 합(Prefix Sum) vs. 부분 합(Partial Sum)
누적 합(Prefix Sum)
- 수들의 나열에서 특정 구간의 합
- 구간 합이라고도 불린다.
- 보통 1차원 배열에서 @i ~ k@ 인덱스 사이의 값들의 합을 구하는데 사용한다.
부분 합(Partial Sum)
- 처음부터 특정 인덱스까지의 합
- @0 ~ k@ 인덱스 사이의 값들의 합을 구하는데 사용한다.
728x90
728x90
'Computer Science > 알고리즘' 카테고리의 다른 글
[Algorithm] 최대 공약수(GCD)와 최소 공배수(LCM) ; 유클리드 호제법(Euclidean Algorithm) (0) | 2022.11.13 |
---|---|
[Algorithm] 브루트 포스(Brute Force) (0) | 2022.11.03 |
[Algorithm] 하노이 탑(Tower of Hanoi) (0) | 2022.11.03 |
[Algorithm] 스캐닝 메소드(Scanning Method) (0) | 2022.10.26 |
[Algorithm] 형상수(Figulate Number) (0) | 2022.10.26 |
[Algorithm] 에라토스테네스의 체(Sieve of Erathosthenes) (0) | 2022.10.25 |
[Algorithm] 소인수 분해(Prime/Integer Factorization) (0) | 2022.10.25 |
[Algorithm] 피보나치 수열(Fibonacci Sequence) (1) | 2022.10.06 |