PreComputation Technique on Arrays
Last Updated :
21 Dec, 2023
Precomputation refers to the process of pre-calculating and storing the results of certain computations or data structures(array in this case) in advance, in order to speed up the execution time of a program. This can be useful in situations where the same calculations are needed multiple times, as it avoids the need to recalculate them every time.
This technique relies on fast retreival of already stored data rather than recalculation.
Some Common Pre-Computation Techniques used on Array:
Though pre-computation can be done in various ways over a wide range of questions, in this section we will look at some of the popular techniques of pre-computation:
Prefix/Suffix on 1-D Array:
A prefix array PRE[] is defined on an array ARR[], such that for each index ‘i‘ PRE[i] stores the information about the values from ARR[0] to ARR[i]. This information can be in the form of prefix sum, prefix max, prefix gcd, etc.
Let’s construct prefix array for ARR[] = {4, 2, 1, -1, 3}, to do this we can simply iterate ARR[] from 0 to size-1, and store PRE[i]=ARR[i]+PRE[i-1], after doing this we have PRE[] = {4, 6, 7, 6, 9}
Why do we need this Prefix Array?
- For simplicity consider the example for Prefix Sum Array. Suppose we have an array ARR[] of size N, and Q queries and for each query, we have to output the sum of values between the index L to R.
- The Brute Force way of doing this will result in a time complexity of O(N*Q) where we iterate from L to R in each query to know the sum. In order to improve this time complexity we can use Pre-Computation i.e. calculate the Prefix sum array before processing the queries, and then for each query simply return PRE[R]-PRE[L-1] as the result in O(1), the overall complexity then would be O(max(N, Q))
- The Suffix array is similar to the Prefix array the only difference is that the Suffix array stores the information about the values from index ‘i’ to the end of the array rather that start of the array i..e. SUFFIX[i] stores information about values from ARR[i] to ARR[size-1].
Below is the code to construct a Prefix Sum and Suffix Sum array:
C++
#include <iostream>
using namespace std;
void print( int arr[], int N)
{
for ( int i = 0; i < N; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
void PrefixSum( int ARR[], int N)
{
int PRE[N];
for ( int i = 0; i < N; i++) {
if (i == 0)
PRE[i] = ARR[i];
else
PRE[i] = PRE[i - 1] + ARR[i];
}
cout << "Prefix Sum: ";
print(PRE, N);
}
void SuffixSum( int ARR[], int N)
{
int SUF[N];
for ( int i = N - 1; i >= 0; i--) {
if (i == N - 1)
SUF[i] = ARR[i];
else
SUF[i] = SUF[i + 1] + ARR[i];
}
cout << "Suffix Sum: ";
print(SUF, N);
}
int main()
{
int N = 5;
int ARR[N] = { 4, 2, 1, -1, 3 };
cout << "Given Array: ";
print(ARR, N);
PrefixSum(ARR, N);
SuffixSum(ARR, N);
return 0;
}
|
Java
public class PrefixSuffixSum {
static void print( int [] arr, int N) {
for ( int i = 0 ; i < N; i++) {
System.out.print(arr[i] + " " );
}
System.out.println();
}
static void prefixSum( int [] arr, int N) {
int [] PRE = new int [N];
for ( int i = 0 ; i < N; i++) {
if (i == 0 )
PRE[i] = arr[i];
else
PRE[i] = PRE[i - 1 ] + arr[i];
}
System.out.print( "Prefix Sum: " );
print(PRE, N);
}
static void suffixSum( int [] arr, int N) {
int [] SUF = new int [N];
for ( int i = N - 1 ; i >= 0 ; i--) {
if (i == N - 1 )
SUF[i] = arr[i];
else
SUF[i] = SUF[i + 1 ] + arr[i];
}
System.out.print( "Suffix Sum: " );
print(SUF, N);
}
public static void main(String[] args) {
int N = 5 ;
int [] ARR = { 4 , 2 , 1 , - 1 , 3 };
System.out.print( "Given Array: " );
print(ARR, N);
prefixSum(ARR, N);
suffixSum(ARR, N);
}
}
|
Python3
def print_array(arr, N):
for i in range (N):
print (arr[i], end = " " )
print ()
def PrefixSum(ARR, N):
PRE = [ 0 ] * N
for i in range (N):
if (i = = 0 ):
PRE[i] = ARR[i]
else :
PRE[i] = PRE[i - 1 ] + ARR[i]
print ( "Prefix Sum: " , end = "")
print_array(PRE, N)
def SuffixSum(ARR, N):
SUF = [ 0 ] * N
for i in range (N - 1 , - 1 , - 1 ):
if (i = = N - 1 ):
SUF[i] = ARR[i]
else :
SUF[i] = SUF[i + 1 ] + ARR[i]
print ( "Suffix Sum: " , end = "")
print_array(SUF, N)
if __name__ = = "__main__" :
N = 5
ARR = [ 4 , 2 , 1 , - 1 , 3 ]
print ( "Given Array: " , end = "")
print_array(ARR, N)
PrefixSum(ARR, N)
SuffixSum(ARR, N)
|
C#
using System;
class Program
{
static void Print( int [] arr, int N)
{
for ( int i = 0; i < N; i++)
{
Console.Write(arr[i] + " " );
}
Console.WriteLine();
}
static void PrefixSum( int [] ARR, int N)
{
int [] PRE = new int [N];
for ( int i = 0; i < N; i++)
{
if (i == 0)
PRE[i] = ARR[i];
else
PRE[i] = PRE[i - 1] + ARR[i];
}
Console.Write( "Prefix Sum: " );
Print(PRE, N);
}
static void SuffixSum( int [] ARR, int N)
{
int [] SUF = new int [N];
for ( int i = N - 1; i >= 0; i--)
{
if (i == N - 1)
SUF[i] = ARR[i];
else
SUF[i] = SUF[i + 1] + ARR[i];
}
Console.Write( "Suffix Sum: " );
Print(SUF, N);
}
static void Main()
{
int N = 5;
int [] ARR = { 4, 2, 1, -1, 3 };
Console.Write( "Given Array: " );
Print(ARR, N);
PrefixSum(ARR, N);
SuffixSum(ARR, N);
}
}
|
Javascript
function print(arr, N) {
for (let i = 0; i < N; i++) {
console.log(arr[i] + " " );
}
console.log();
}
function PrefixSum(ARR, N) {
let PRE = [];
for (let i = 0; i < N; i++) {
if (i === 0)
PRE[i] = ARR[i];
else
PRE[i] = PRE[i - 1] + ARR[i];
}
console.log( "Prefix Sum: " );
print(PRE, N);
}
function SuffixSum(ARR, N) {
let SUF = [];
for (let i = N - 1; i >= 0; i--) {
if (i === N - 1)
SUF[i] = ARR[i];
else
SUF[i] = SUF[i + 1] + ARR[i];
}
console.log( "Suffix Sum: " );
print(SUF, N);
}
let N = 5;
let ARR = [4, 2, 1, -1, 3];
console.log( "Given Array: " );
print(ARR, N);
PrefixSum(ARR, N);
SuffixSum(ARR, N);
|
Output
Given Array: 4 2 1 -1 3
Prefix Sum: 4 6 7 6 9
Suffix Sum: 9 5 3 2 3
Prefix on 2-D Array:
The above idea of Prefix on 1-D array can be expanded to 2-D array, where we have a 2-D array ARR[][] of size NxM and PRE[i][j] will store the information about the values from row 0 to ‘i’ and and column 0 to ‘j’ . Let’s say we want to retrieve the sum of values of submatrice [i1, j1] to [i2, j2], we can simply use our 2-D prefix sum array to achieve this by simple Inclusion Exclusion principle PRE[i2][j2] – PRE[i2][j1-1] – PRE[i1-1][j2] + PRE[i1-1][j1-1].
Below is the code construct prefix sum for 2-D array:
C++
#include <bits/stdc++.h>
using namespace std;
void print(vector<vector< int > >& arr, int N, int M)
{
for ( int i = 0; i < N; i++) {
for ( int j = 0; j < M; j++) {
cout << arr[i][j] << " ";
}
cout << endl;
}
}
void PrefixSum(vector<vector< int > >& ARR, int N, int M)
{
vector<vector< int > > PRE(N, vector< int >(M));
for ( int i = 0; i < N; i++) {
for ( int j = 0; j < M; j++) {
int x = 0;
int y = 0;
int z = 0;
if (i - 1 >= 0)
x = PRE[i - 1][j];
if (j - 1 >= 0)
y = PRE[i][j - 1];
if (i - 1 >= 0 && j - 1 >= 0)
z = PRE[i - 1][j - 1];
PRE[i][j] = ARR[i][j] + x + y - z;
}
}
cout << "Give Array:" << endl;
print(ARR, N, M);
cout << "Prefix Sum Array:" << endl;
print(PRE, N, M);
}
int main()
{
int N = 4;
int M = 4;
vector<vector< int > > ARR = { { 1, 1, 1, 1 },
{ 1, 1, 1, 1 },
{ 1, 1, 1, 1 },
{ 1, 1, 1, 1 } };
PrefixSum(ARR, N, M);
return 0;
}
|
Java
import java.util.Arrays;
public class PrefixSum {
static void print( int [][] arr, int N, int M)
{
for ( int i = 0 ; i < N; i++) {
for ( int j = 0 ; j < M; j++) {
System.out.print(arr[i][j] + " " );
}
System.out.println();
}
}
static void prefixSum( int [][] arr, int N, int M)
{
int [][] pre = new int [N][M];
for ( int i = 0 ; i < N; i++) {
for ( int j = 0 ; j < M; j++) {
int x = 0 , y = 0 , z = 0 ;
if (i - 1 >= 0 )
x = pre[i - 1 ][j];
if (j - 1 >= 0 )
y = pre[i][j - 1 ];
if (i - 1 >= 0 && j - 1 >= 0 )
z = pre[i - 1 ][j - 1 ];
pre[i][j] = arr[i][j] + x + y - z;
}
}
System.out.println( "Given Array:" );
print(arr, N, M);
System.out.println( "Prefix Sum Array:" );
print(pre, N, M);
}
public static void main(String[] args)
{
int N = 4 ;
int M = 4 ;
int [][] arr = { { 1 , 1 , 1 , 1 },
{ 1 , 1 , 1 , 1 },
{ 1 , 1 , 1 , 1 },
{ 1 , 1 , 1 , 1 } };
prefixSum(arr, N, M);
}
}
|
Python3
def print_matrix(matrix):
for row in matrix:
print ( " " .join( map ( str , row)))
def prefix_sum(matrix, n, m):
prefix = [[ 0 ] * m for _ in range (n)]
for i in range (n):
for j in range (m):
x = 0 if i - 1 < 0 else prefix[i - 1 ][j]
y = 0 if j - 1 < 0 else prefix[i][j - 1 ]
z = 0 if i - 1 < 0 or j - 1 < 0 else prefix[i - 1 ][j - 1 ]
prefix[i][j] = matrix[i][j] + x + y - z
print ( "Given Array:" )
print_matrix(matrix)
print ( "Prefix Sum Array:" )
print_matrix(prefix)
if __name__ = = "__main__" :
N = 4
M = 4
matrix = [
[ 1 , 1 , 1 , 1 ],
[ 1 , 1 , 1 , 1 ],
[ 1 , 1 , 1 , 1 ],
[ 1 , 1 , 1 , 1 ]
]
prefix_sum(matrix, N, M)
|
C#
using System;
class Program {
static void Print( int [, ] arr, int N, int M)
{
for ( int i = 0; i < N; i++) {
for ( int j = 0; j < M; j++) {
Console.Write(arr[i, j] + " " );
}
Console.WriteLine();
}
}
static void PrefixSum( int [, ] ARR, int N, int M)
{
int [, ] PRE = new int [N, M];
for ( int i = 0; i < N; i++) {
for ( int j = 0; j < M; j++) {
int x = 0;
int y = 0;
int z = 0;
if (i - 1 >= 0)
x = PRE[i - 1, j];
if (j - 1 >= 0)
y = PRE[i, j - 1];
if (i - 1 >= 0 && j - 1 >= 0)
z = PRE[i - 1, j - 1];
PRE[i, j] = ARR[i, j] + x + y - z;
}
}
Console.WriteLine( "Given Array:" );
Print(ARR, N, M);
Console.WriteLine( "Prefix Sum Array:" );
Print(PRE, N, M);
}
static void Main()
{
int N = 4;
int M = 4;
int [, ] ARR = { { 1, 1, 1, 1 },
{ 1, 1, 1, 1 },
{ 1, 1, 1, 1 },
{ 1, 1, 1, 1 } };
PrefixSum(ARR, N, M);
}
}
|
Javascript
function print(arr) {
for (let i = 0; i < arr.length; i++) {
console.log(arr[i].join( ' ' ));
}
console.log();
}
function calculatePrefixSum(ARR) {
const N = ARR.length;
const M = ARR[0].length;
const PRE = new Array(N);
for (let i = 0; i < N; i++) {
PRE[i] = new Array(M).fill(0);
}
for (let i = 0; i < N; i++) {
for (let j = 0; j < M; j++) {
let x = 0;
let y = 0;
let z = 0;
if (i - 1 >= 0)
x = PRE[i - 1][j];
if (j - 1 >= 0)
y = PRE[i][j - 1];
if (i - 1 >= 0 && j - 1 >= 0)
z = PRE[i - 1][j - 1];
PRE[i][j] = ARR[i][j] + x + y - z;
}
}
console.log( "Given Array:" );
print(ARR);
console.log( "Prefix Sum Array:" );
print(PRE);
}
const N = 4;
const M = 4;
const ARR = [
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]
];
calculatePrefixSum(ARR);
|
Output
Give Array:
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
Prefix Sum Array:
1 2 3 4
2 4 6 8
3 6 9 12
4 8 12 16
Hashing the Arrays data:
There are times when Hashing can be used for pre-computation and drastically reduce the time complexity of program. Suppose for various query we would require the first index of an element ‘e‘ in the array. Instead of calculating this again and again, we can simply iterate on array once and hash the element to the first index it occurs to and finally retrieve this value in O(1) for each query.
Moreover Hashing can be combined with other precomputation techniques like prefix_sum in order to enhance the usability.
Practice Problems:
Share your thoughts in the comments
Please Login to comment...