Count the value of 2d Matrix - c++

I need to count the value of the matrix. Let's see the example, because its bit hard to explain.
There is a 2d quadratic matrix ( rows == columns ) let's called it int n and it must be odd ( 3x3, 5x5, 7x7,.. ) and we have int x. We need to find the sum of the value based on x. The value of x is start from 0 and less than n/2.
Example:
Matrix 3x3
1 1 1
2 2 2
3 3 3
if x is 0, we need to sum 1 + 1 + 1 + 2 + 2 + 3 + 3 + 3 = 16.
if x is 1, output is 2.
x cant be more than 1, because 2 > n/2.
Matrix 5x5
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
2 2 2 2 2
1 1 1 1 1
if x is 0, then output will be 24 ( 1 + 1 + 1 + 1 + 1 + 2 + 2 + 3 + 3 + 2 + 2 + 1 + 1 + 1 + 1 + 1 )
if x is 1, then output will be 18 ( 2 + 2 + 2 + 3 + 3 + 2 + 2 + 2 )
if x is 2, then output will be 3.
as you can see, the bold number is if x is 0, and the italic number is if x is 1.
Then, i came up with this code
int test( int A[100][100], int n, int x){
if( x == ( n - 1 ) / 2 )
return A[((n-1)/2)][((n-1)/2)];
if(x >= 0 && x < ( n - 1 )/2 )
return A[((n-1)/2)-x-1][((n-1)/2)-x-1] + A[((n-1)/2)-x-1][((n-1)/2)] + A[((n-1)/2)-x-1][((n-1)/2)+x+1] + A[((n-1)/2)][((n-1)/2)-x-1] + A[((n-1)/2)][((n-1)/2)+x+1]
+ A[((n-1)/2)+x+1][((n-1)/2)-x-1] + A[((n-1)/2)+x+1][((n-1)/2)] + A[((n-1)/2)+x+1][((n-1)/2)+x+1];
}
The problem is, if the matrix is only 3x3, its ok. but if the matrix is greater than 3x3 will come up with wrong output.
Also, I tried with loop. but didn't work.
int test(int A[100][100], int n, int x ) {
for ( int i = 0; i < n; i ++ ) {
for ( int j = 0; j < n; j ++ ) {
int count = 0;
if ( i < n - 1 ) // bottom
count = count + A[((n-1)/2)+1+x][(n-1)/2];
if ( j < n - 1 ) // right
count = count + A[(n-1)/2][((n-1)/2)+1+x];
if ( j >= 1 ) // left
count = count + A[(n-1)/2][((n-1)/2)-1-x];
if ( i >= 1 ) // top
count = count + A[((n-1)/2)-1-x][(n-1)/2];
if ( j >= 1 && i >= 1 ) // left top
count = count + A[((n-1)/2)-1-x][((n-1)/2)-1-x];
if ( j < n - 1 && i >= 1 ) // right top
count = count + A[((n-1)/2)-1-x][((n-1)/2)+1+x];
if ( j >= 1 && i < n - 1 ) // left bottom
count = count + A[((n-1)/2)+1+x][((n-1)/2)-1-x];
if ( j < n - 1 && i < n - 1 ) // right bottom
count = count + A[((n-1)/2)+1+x][((n-1)/2)+1+x];
return count;
}
}
}

You want to sum up a ring. You only need one loop, not two nested loops:
It doesn't make sense to unconditionally return in a loop. It will break out of the loop in the first iteration. You should return after the loop.
It doesn't make sense to initialize count in the loop. It will reset it in each iteration. You should initialize count before the loop.
You don't use the loop variables in your calculation. You should access different elements using i and j.
You can make your life easier if you set the loop to
int test(int A[100][100], int n, int x) {
int count = 0;
for (int i = x; i < n - x - 1; ++i) {
count += A[i][x];
count += A[i + 1][n - x - 1];
count += A[x][i];
count += A[n - x - 1][i + 1];
}
return count;
}
or use std::accumulate
int test(int A[100][100], int n, int x) {
return std::accumulate(A[x] + x, A[x] + n - x - 1, 0)
+ std::accumulate(A[n - x - 1] + x + 1, A[n - x - 1] + n - x, 0)
+ std::accumulate(A + x + 1, A + n - x, 0, [x, n](auto acc, auto el) {
return acc + el[x];
})
+ std::accumulate(A + x, A + n - x - 1, 0, [x, n](auto acc, auto el) {
return acc + el[n - x - 1];
});
}
It's unclear for me why you need parameter n. A has a fixed size 100x100. n has to be 100. You could rewrite both codes to
constexpr int n = 100;
int test(int A[n][n], int x) {
int count = 0;
for (int i = x; i < n - x - 1; ++i) {
count += A[i][x];
count += A[i + 1][n - x - 1];
count += A[x][i];
count += A[n - x - 1][i + 1];
}
return count;
}
resp.
constexpr int n = 100;
int test(int A[n][n], int x) {
return std::accumulate(A[x] + x, A[x] + n - x - 1, 0)
+ std::accumulate(A[n - x - 1] + x + 1, A[n - x - 1] + n - x, 0)
+ std::accumulate(A + x + 1, A + n - x, 0, [x](auto acc, auto el) {
return acc + el[x];
})
+ std::accumulate(A + x, A + n - x - 1, 0, [x](auto acc, auto el) {
return acc + el[n - x - 1];
});
}
or using templates
template<int n>
int test(int A[n][n], int x) {
int count = 0;
for (int i = x; i < n - x - 1; ++i) {
count += A[i][x];
count += A[i + 1][n - x - 1];
count += A[x][i];
count += A[n - x - 1][i + 1];
}
return count;
}
resp.
template<int n>
int test(int A[n][n], int x) {
return std::accumulate(A[x] + x, A[x] + n - x - 1, 0)
+ std::accumulate(A[n - x - 1] + x + 1, A[n - x - 1] + n - x, 0)
+ std::accumulate(A + x + 1, A + n - x, 0, [x](auto acc, auto el) {
return acc + el[x];
})
+ std::accumulate(A + x, A + n - x - 1, 0, [x](auto acc, auto el) {
return acc + el[n - x - 1];
});
}

template <int N>
int SumRing (const int A[N][N], const int x)
{
// x represents the lower end, XX the upper:
const int XX = N - x - 1;
// special case: no "ring", only middle element:
if (x == XX)
return A[x][x];
// edges of the ring:
const int r_top = x;
const int c_left = x;
const int r_bottom = XX;
const int c_right = XX;
// We add clockwise around the ring,
// each time leaving out the last element
// to avoid its double-count:
int sum = 0;
// top row:
for (int c = c_left; c < c_right; ++c)
sum += A[r_top][c];
// right column:
for (int r = r_top; r < r_bottom; ++r)
sum += A[r][c_right];
// bottom row:
for (int c = c_right; c > c_left; --c)
sum += A[r_bottom][c];
// left column:
for (int r = r_bottom; r > r_top; --r)
sum += A[r][c_left];
return sum;
}
If you don't know the array size before run-time, then you may want to consider a matrix wrapper class around std::vector.

Related

Why is C++ not changing my code's numbers' condition according to pow (-1,i)?

I have C++ code here:
#include <iostream>
#include <cmath>
#include <time.h>
using namespace std;
int main()
{
double n,x;
cin>>n>>x;
double b=1,c=0.0;
for (int i=1;i<=n;i++){
x=pow(x,i);
x=x*pow((-1),(i+1));
cout<<x<<endl;
b=i;
c=c+x/b;
}
cout<<c<<endl;
return 0;
}
I am creating this to calculate x^1-x^2/2+x^3/3-.....+(-1)^(n-1)*(x^n)/n. The user inputs n. The problem appears to be in this line: x=x*pow((-1),(i+1));.
I am creating this to calculate x^1 - x^2/2 + x^3/3 - ... + (-1)^(n-1)*(x^n)/n.
That seems to be the Maclaurin series of ln(1 + x), but it's not what the posted code evaluates, though:
for (int i=1;i<=n;i++)
{
x = pow(x,i);
// ^ This is updated at each iteration! It should be const.
x = x * pow((-1),(i+1));
// ^^^^^^^^^^^^^^^ Please don't (see later).
b=i;
c=c+x/b;
// ^ Okish, but why not use i directly?
}
At the very least, a variable different from x should be introduced to store the results of the powers.
The use of pow((-1),(i+1)) to generate the simple sequence {1, -1, 1, -1, ...} is also questionable, if not prone to rounding errors. I'll show two different ways to accomplish the same task.
// Evaluates the Mclaurin series of ln(1 + x) using n terms.
// Noting that (e.g. with n == 4):
// p(4) = x -x^2 / 2 + x^3 / 3 - x^4 / 4
// p(4) = x - x*x/2 + x*x*x/3 - x*x*x*x/4
// p(4) = k(1) -x*k(1)/2 + x*x*x/3 - x*x*x*x/4 k(1) = x
// p(4) = k(1) -x*k(1)/2 -x*k(2)/3 - x*x*x*x/4 k(2) = -x*k(1)
// p(4) = k(1) -x*k(1)/2 -x*k(2)/3 -x*k(3)/4 k(3) = -x*k(2)
// Preconditions: n >= 1 and -1 < x <= 1
double fn(int n, double x)
{
double k{ x };
double sum{ x };
for (int i{ 2 }; i <= n; ++i)
{
k *= -x;
sum += k / i;
}
return sum;
}
Note that, in the interval of convergence, abs(k / i) tends to zero, while outside it grows. Eventually, due to the limited precision of floating-point types like double, sum += k/i won't change the value of sum.
Another approach may be based on Horner's rule.
// Evaluates the Mclaurin series of ln(1 + x) using n terms.
// Applying Horner's rule:
// p(4) = x -x^2 / 2 + x^3 / 3 - x^4 / 4
// = x*(1 + x*(-1/2 + x*(1/3 + x*(-1/4))))
// = x*(1 + x*(-1/2 + x*( 1/3 + x*k(4) ))) k(4) = 1/4
// = x*(1 + x*( -1/2 + x*k(3) )) k(3) = 1/3 + x*k(4)
// = x*( 1 + x*k(2) ) k(2) = -1/2 + x*k(3)
// = x * k(1) k(1) = 1 + x*k(2)
// Preconditions: n >= 1 and -1 < x <= 1
double fn(int n, double x)
{
double sign{ n % 2 == 0? -1.0 : 1.0 };
double k{ sign / n };
while ( --n > 0 )
{
sign = -sign;
k = sign / n + x * k;
}
return k * x;
}

MaxDoubleSliceSum Codility Algorithm

I stumbled upon this problem on Codility Lessons, here is the description:
A non-empty zero-indexed array A consisting of N integers is given.
A triplet (X, Y, Z), such that 0 ≤ X < Y < Z < N, is called a double slice.
The sum of double slice (X, Y, Z) is the total of A[X + 1] + A[X + 2] + ... + A[Y − 1] + A[Y + 1] + A[Y + 2] + ... + A[Z − 1].
For example, array A such that:
A[0] = 3
A[1] = 2
A[2] = 6
A[3] = -1
A[4] = 4
A[5] = 5
A[6] = -1
A[7] = 2
contains the following example double slices:
double slice (0, 3, 6), sum is 2 + 6 + 4 + 5 = 17,
double slice (0, 3, 7), sum is 2 + 6 + 4 + 5 − 1 = 16,
double slice (3, 4, 5), sum is 0.
The goal is to find the maximal sum of any double slice.
Write a function:
int solution(vector &A);
that, given a non-empty zero-indexed array A consisting of N integers, returns the maximal sum of any double slice.
For example, given:
A[0] = 3
A[1] = 2
A[2] = 6
A[3] = -1
A[4] = 4
A[5] = 5
A[6] = -1
A[7] = 2
the function should return 17, because no double slice of array A has a sum of greater than 17.
Assume that:
N is an integer within the range [3..100,000];
each element of array A is an integer within the range [−10,000..10,000].
Complexity:
expected worst-case time complexity is O(N);
expected worst-case space complexity is O(N), beyond input storage (not counting >the storage required for input arguments).
Elements of input arrays can be modified.
I have already read about the algorithm with counting MaxSum starting at index i and ending at index i, but I don't know why my approach sometimes gives bad results. The idea is to compute MaxSum ending at index i, ommiting the minimum value at range 0..i. And here is my code:
int solution(vector<int> &A) {
int n = A.size();
int end = 2;
int ret = 0;
int sum = 0;
int min = A[1];
while (end < n-1)
{
if (A[end] < min)
{
sum = max(0, sum + min);
ret = max(ret, sum);
min = A[end];
++end;
continue;
}
sum = max(0, sum + A[end]);
ret = max(ret, sum);
++end;
}
return ret;
}
I would be glad if you could help me point out the loophole!
My solution based on bidirectional Kadane's algorithm. More details on my blog here. Scores 100/100.
public int solution(int[] A) {
int N = A.length;
int[] K1 = new int[N];
int[] K2 = new int[N];
for(int i = 1; i < N-1; i++){
K1[i] = Math.max(K1[i-1] + A[i], 0);
}
for(int i = N-2; i > 0; i--){
K2[i] = Math.max(K2[i+1]+A[i], 0);
}
int max = 0;
for(int i = 1; i < N-1; i++){
max = Math.max(max, K1[i-1]+K2[i+1]);
}
return max;
}
Here is my code:
int get_max_sum(const vector<int>& a) {
int n = a.size();
vector<int> best_pref(n);
vector<int> best_suf(n);
//Compute the best sum among all x values assuming that y = i.
int min_pref = 0;
int cur_pref = 0;
for (int i = 1; i < n - 1; i++) {
best_pref[i] = max(0, cur_pref - min_pref);
cur_pref += a[i];
min_pref = min(min_pref, cur_pref);
}
//Compute the best sum among all z values assuming that y = i.
int min_suf = 0;
int cur_suf = 0;
for (int i = n - 2; i > 0; i--) {
best_suf[i] = max(0, cur_suf - min_suf);
cur_suf += a[i];
min_suf = min(min_suf, cur_suf);
}
//Check all y values(y = i) and return the answer.
int res = 0;
for (int i = 1; i < n - 1; i++)
res = max(res, best_pref[i] + best_suf[i]);
return res;
}
int get_max_sum_dummy(const vector<int>& a) {
//Try all possible values of x, y and z.
int res = 0;
int n = a.size();
for (int x = 0; x < n; x++)
for (int y = x + 1; y < n; y++)
for (int z = y + 1; z < n; z++) {
int cur = 0;
for (int i = x + 1; i < z; i++)
if (i != y)
cur += a[i];
res = max(res, cur);
}
return res;
}
bool test() {
//Generate a lot of small test cases and compare the output of
//a brute force and the actual solution.
bool ok = true;
for (int test = 0; test < 10000; test++) {
int size = rand() % 20 + 3;
vector<int> a(size);
for (int i = 0; i < size; i++)
a[i] = rand() % 20 - 10;
if (get_max_sum(a) != get_max_sum_dummy(a))
ok = false;
}
for (int test = 0; test < 10000; test++) {
int size = rand() % 20 + 3;
vector<int> a(size);
for (int i = 0; i < size; i++)
a[i] = rand() % 20;
if (get_max_sum(a) != get_max_sum_dummy(a))
ok = false;
}
return ok;
}
The actual solution is get_max_sum function(the other two are a brute force solution and a tester functions that generates a random array and compares the output of a brute force and actual solution, I used them for testing purposes only).
The idea behind my solution is to compute the maximum sum in a sub array that that starts somewhere before i and ends in i - 1, then do the same thing for suffices(best_pref[i] and best_suf[i], respectively). After that I just iterate over all i and return the best value of best_pref[i] + best_suf[i]. It works correctly because best_pref[y] finds the best x for a fixed y, best_suf[y] finds the best z for a fixed y and all possible values of y are checked.
def solution(A):
n = len(A)
K1 = [0] * n
K2 = [0] * n
for i in range(1,n-1,1):
K1[i] = max(K1[i-1] + A[i], 0)
for i in range(n-2,0,-1):
K2[i] = max(K2[i+1]+A[i], 0)
maximum = 0;
for i in range(1,n-1,1):
maximum = max(maximum, K1[i-1]+K2[i+1])
return maximum
def main():
A = [3,2,6,-1,4,5,-1,2]
print(solution(A))
if __name__ == '__main__': main()
Ruby 100%
def solution(a)
max_starting =(a.length - 2).downto(0).each.inject([[],0]) do |(acc,max), i|
[acc, acc[i]= [0, a[i] + max].max ]
end.first
max_ending =1.upto(a.length - 3).each.inject([[],0]) do |(acc,max), i|
[acc, acc[i]= [0, a[i] + max].max ]
end.first
max_ending.each_with_index.inject(0) do |acc, (el,i)|
[acc, el.to_i + max_starting[i+2].to_i].max
end
end

Partition function based on Euler's formula

I am trying to find the partitions of a number using the Euler's formula for that:
It produces results like:
P(3) = P(2) + P(1) = 3
P(4) = P(3) + P(2) = 3+ 2 = 5
P(5) = P(4) + P(3) - P(0) = 5 + 3 - 1 = 7
P(6) = P(5) + P(4) - P(1) = 7 + 5 - 1 = 11 and so on..
* P(0) = 1
It produces two positive and then two negative values and so on.
I am using recursion for that but the code goes into an infinite loop without producing any result.
long result = 0;
long counter = 0;
class Euler
{
public:
long Partition(long n)
{
int exponent = 0;
if (n < 0)
{
return 0;
}
else
{
counter = counter + 1;
exponent = pow(-1, counter - 1) ;
if (n == 0)
{
n = 1;
}
return Partition((exponent * (n - ( (counter * ( (3 * counter) - 1)) / 2)))) +
Partition(((exponent * (n - ( (counter * ( (3 * counter) + 1)) / 2)) )));
}
}
};
int main(int argc, char** argv)
{
long result= 0;
long a = 3;
Euler * obj = new Euler();
long s = obj->Partition(a);
std::cout << s;
return 0;
}
Your global counter is modified by the first call to Partition, so the second one operates on a different one; in fact, the counter changes more or less unpredictably.
Do not use globals.

Convert this recursive function to iterative

How can I convert this recursive function to an iterative function?
#include <cmath>
int M(int H, int T){
if (H == 0) return T;
if (H + 1 >= T) return pow(2, T) - 1;
return M(H - 1, T - 1) + M(H, T - 1) + 1;
}
Well it's a 3-line code but it's very hard for me to convert this to an iterative function. Because it has 2 variables. And I don't know anything about Stacks so I couldn't convert that.
My purpose for doing this is speed of the function. This function is too slow. I wanted to use map to make this faster but I have 3 variables M, H and T so I couldn't use map
you could use dynamic programming - start from the bottom up when H == 0 and T == 0 calculate M and iterate them. here is a link explaining how to do this for Fibonacci numbers, which are quite similar to your problem.
Check this,recursive and not recursive versions gave equal results for all inputs i gave so far. The idea is to keep intermediate results in matrix, where H is row index, T is col index, and the value is M(H,T). By the way, you can calculate it once and later just obtain the result from the matrix, so you will have performance O(1)
int array[10][10]={{0}};
int MNR(int H, int T)
{
if(array[H][T])
return array[H][T];
for(int i =0; i<= H;++i)
{
for(int j = 0; j<= T;++j)
{
if(i == 0)
array[i][j] = j;
else if( i+1 > j)
array[i][j] = pow(2,j) -1;
else
array[i][j] = array[i-1][j-1] + array[i][j-1] + 1;
}
}
return array[H][T];
}
int M(int H, int T)
{
if (H == 0) return T;
if (H + 1 >= T) return pow(2, T) - 1;
return M(H - 1, T - 1) + M(H, T - 1) + 1;
}
int main()
{
printf("%d\n", M(6,3));
printf("%d\n", MNR(6,3));
}
Unless you know the formula for n-th (in your case, (m,n)-th) element of the sequence, the easiest way is to simulate the recursion using a stack.
The code should look like the following:
#include <cmath>
#include <stack>
struct Data
{
public:
Data(int newH, int newT)
: T(newT), H(newH)
{
}
int H;
int T;
};
int M(int H, int T)
{
std::stack<Data> st;
st.push(Data(H, T));
int sum = 0;
while (st.size() > 0)
{
Data top = st.top();
st.pop();
if (top.H == 0)
sum += top.T;
else if (top.H + 1 >= top.T)
sum += pow(2, top.T) - 1;
else
{
st.push(Data(top.H - 1, top.T - 1));
st.push(Data(top.H, top.T - 1));
sum += 1;
}
}
return sum;
}
The main reason why this function is slow is because it has exponential complexity, and it keeps recalculating the same members again and again. One possible cure is memoize pattern (handily explained with examples in C++ here). The idea is to store every result in a structure with a quick access (e.g. an array) and every time you need it again, retrieve already precomputed result. Of course, this approach is limited by the size of your memory, so it won't work for extremely big numbers...
In your case, we could do something like that (keeping the recursion but memoizing the results):
#include <cmath>
#include <map>
#include <utility>
std::map<std::pair<int,int>,int> MM;
int M(int H, int T){
std::pair<int,int> key = std::make_pair(H,T);
std::map<std::pair<int,int>,int>::iterator found = MM.find(key);
if (found!=MM.end()) return found->second; // skip the calculations if we can
int result = 0;
if (H == 0) result = T;
else if (H + 1 >= T) result = pow(2, T) - 1;
else result = M(H - 1, T - 1) + M(H, T - 1) + 1;
MM[key] = result;
return result;
}
Regarding time complexity, C++ maps are tree maps, so searching there is of the order of N*log(N) where N is the size of the map (number of results which have been already computed). There are also hash maps for C++ which are part of the STL but not part of the standard library, as was already mentioned on SO. Hash map promises constant search time (the value of the constant is not specified though :) ), so you might also give them a try.
You may calculate using one demintional array. Little theory,
Let F(a,b) == M(H,T)
1. F(0,b) = b
2. F(a,b) = 2^b - 1, when a+1 >= b
3. F(a,b) = F(a-1,b-1) + F(a,b-1) + 1
Let G(x,y) = F(y,x) ,then
1. G(x,0) = x // RULE (1)
2. G(x,y) = 2^x - 1, when y+1 >= x // RULE (2)
3. G(x,y) = G(x-1,y-1) + G(x-1,y) + 1 // RULE(3) --> this is useful,
// because for G(x,y) need only G(x-1,?), i.e if G - is two deminsions array, then
// for calculating G[x][?] need only previous row G[x-1][?],
// so we need only last two rows of array.
// Here some values of G(x,y)
4. G(0,y) = 2^0 - 1 = 0 from (2) rule.
5. G(1,0) = 1 from (1) rule.
6. G(1,y) = 2^1 - 1 = 1, when y > 0, from (2) rule.
G(0,0) = 0, G(0,1) = 0, G(0,2) = 0, G(0,3) = 0 ...
G(1,0) = 1, G(1,1) = 1, G(1,2) = 1, G(1,3) = 1 ...
7. G(2,0) = 2 from (1) rule
8. G(2,1) = 2^2 - 1 = 3 from (2) rule
9. G(2,y) = 2^2 - 1 = 3 when y > 0, from (2) rule.
G(2,0) = 2, G(2,1) = 3, G(2,2) = 3, G(2,3) = 3, ....
10. G(3,0) = 3 from (1) rule
11. G(3,1) = G(2,0) + G(2,1) + 1 = 2 + 3 + 1 = 6 from (3) rule
12. G(3,2) = 2^3 - 1 = 7, from (2) rule
Now, how to calculate this G(x,y)
int M(int H, int T ) { return G(T,H); }
int G(int x, int y)
{
const int MAX_Y = 100; // or something else
int arr[2][MAX_Y] = {0} ;
int icurr = 0, inext = 1;
for(int xi = 0; xi < x; ++xi)
{
for( int yi = 0; yi <= y ;++yi)
{
if ( yi == 0 )
arr[inext][yi] = xi; // rule (1);
else if ( yi + 1 >= xi )
arr[inext][yi] = (1 << xi) - 1; // rule ( 2 )
else arr[inext][yi] =
arr[icurr][yi-1] + arr[icurr][yi] + 1; // rule (3)
}
icurr ^= 1; inext ^= 1; //swap(i1,i2);
}
return arr[icurr][y];
}
// Or some optimizing
int G(int x, int y)
{
const int MAX_Y = 100;
int arr[2][MAX_Y] = {0};
int icurr = 0, inext = 1;
for(int ix = 0; ix < x; ++ix)
{
arr[inext][0] = ix; // rule (1)
for(int iy = 1; iy < ix - 1; ++ iy)
arr[inext][iy] = arr[icurr][iy-1] + arr[icurr][iy] + 1; // rule (3)
for(int iy = max(0,ix-1); iy <= y; ++iy)
arr[inext][iy] = (1 << ix ) - 1; // rule(2)
icurr ^= 1 ; inext ^= 1;
}
return arr[icurr][y];
}

How to update a range in segment tree while maintaining max and min?

I'm implementing segment tree from an array of data, and I also want to maintaining the max/min of the tree while updating a range of data. Here is my initial approach following this tutorial http://p--np.blogspot.com/2011/07/segment-tree.html.
Unfortunately it doesn't work at all, the logic makes sense to me, but I'm a little confused about b and e, I wonder is this the range of the data array? or it's the actual range of the tree? From what I understand, the max_segment_tree[1] should hold the max of the range [1, MAX_RANGE] while min_segment_tree[1] should hold the min of the range [1, MAX_RANGE].
int data[MAX_RANGE];
int max_segment_tree[3 * MAX_RANGE + 1];
int min_segment_tree[3 * MAX_RANGE + 1];
void build_tree(int position, int left, int right) {
if (left > right) {
return;
}
else if (left == right) {
max_segment_tree[position] = data[left];
min_segment_tree[position] = data[left];
return;
}
int middle = (left + right) / 2;
build_tree(position * 2, left, middle);
build_tree(position * 2 + 1, middle + 1, right);
max_segment_tree[position] = max(max_segment_tree[position * 2], max_segment_tree[position * 2 + 1]);
min_segment_tree[position] = min(min_segment_tree[position * 2], min_segment_tree[position * 2 + 1]);
}
void update_tree(int position, int b, int e, int i, int j, int value) {
if (b > e || b > j || e < i) {
return;
}
if (i <= b && j >= e) {
max_segment_tree[position] += value;
min_segment_tree[position] += value;
return;
}
update_tree(position * 2 , b , (b + e) / 2 , i, j, value);
update_tree(position * 2 + 1 , (b + e) / 2 + 1 , e , i, j, value);
max_segment_tree[position] = max(max_segment_tree[position * 2], max_segment_tree[position * 2 + 1]);
min_segment_tree[position] = min(min_segment_tree[position * 2], min_segment_tree[position * 2 + 1]);
}
EDIT
Adding test cases:
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <algorithm>
#include <map>
#include <set>
#include <utility>
#include <stack>
#include <deque>
#include <queue>
#include <fstream>
#include <functional>
#include <numeric>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cassert>
using namespace std;
const int MAX_RANGE = 20;
int data[MAX_RANGE];
int max_segment_tree[2 * MAX_RANGE];
int min_segment_tree[2 * MAX_RANGE];
int added_to_interval[2 * MAX_RANGE] = {0};
void update_bruteforce(int x, int y, int z, int &smallest, int &largest) {
for (int i = x - 1; i < y; ++i) {
data[i] += z;
}
// update min/max
smallest = data[0];
largest = data[0];
for (int i = 0; i < MAX_RANGE; ++i) {
if (data[i] < smallest) {
smallest = data[i];
}
if (data[i] > largest) {
largest = data[i];
}
}
}
void build_tree(int position, int left, int right) {
if (left > right) {
return;
}
else if (left == right) {
max_segment_tree[position] = data[left];
min_segment_tree[position] = data[left];
return;
}
int middle = (left + right) / 2;
build_tree(position * 2, left, middle);
build_tree(position * 2 + 1, middle + 1, right);
max_segment_tree[position] = max(max_segment_tree[position * 2], max_segment_tree[position * 2 + 1]);
min_segment_tree[position] = min(min_segment_tree[position * 2], min_segment_tree[position * 2 + 1]);
}
void update_tree(int position, int b, int e, int i, int j, int value) {
if (b > e || b > j || e < i) {
return;
}
if (i <= b && e <= j) {
max_segment_tree[position] += value;
min_segment_tree[position] += value;
added_to_interval[position] += value;
return;
}
update_tree(position * 2 , b , (b + e) / 2 , i, j, value);
update_tree(position * 2 + 1 , (b + e) / 2 + 1 , e , i, j, value);
max_segment_tree[position] = max(max_segment_tree[position * 2], max_segment_tree[position * 2 + 1]) + added_to_interval[position];
min_segment_tree[position] = min(min_segment_tree[position * 2], min_segment_tree[position * 2 + 1]) + added_to_interval[position];
}
void update(int x, int y, int value) {
// memset(added_to_interval, 0, sizeof(added_to_interval));
update_tree(1, 0, MAX_RANGE - 1, x - 1, y - 1, value);
}
namespace unit_test {
void test_show_data() {
for (int i = 0; i < MAX_RANGE; ++i) {
cout << data[i] << ", ";
}
cout << endl << endl;
}
void test_brute_force_and_segment_tree() {
// arrange
int number_of_operations = 100;
for (int i = 0; i < MAX_RANGE; ++i) {
data[i] = i + 1;
}
build_tree(1, 0, MAX_RANGE - 1);
// act
int operation;
int x;
int y;
int z;
int smallest = 1;
int largest = MAX_RANGE;
// assert
while (number_of_operations--) {
operation = rand() % 1;
x = 1 + rand() % MAX_RANGE;
y = x + (rand() % (MAX_RANGE - x + 1));
z = 1 + rand() % MAX_RANGE;
if (operation == 0) {
z *= 1;
}
else {
z *= -1;
}
cout << "left, right, value: " << x - 1 << ", " << y - 1 << ", " << z << endl;
update_bruteforce(x, y, z, smallest, largest);
update(x, y, z);
test_show_data();
cout << "correct:\n";
cout << "\tsmallest = " << smallest << endl;
cout << "\tlargest = " << largest << endl;
cout << "possibly correct:\n";
cout << "\tsmallest = " << min_segment_tree[1] << endl;
cout << "\tlargest = " << max_segment_tree[1] << endl;
cout << "\n--------------------------------------------------------------\n";
cin.get();
}
}
}
int main() {
unit_test::test_brute_force_and_segment_tree();
}
You need to store separately the max/min for each interval, AND what values have been added to it (just their sum). Here's how it could go wrong:
Suppose we're building a tree (I'll only show the min tree here) for the array [5, 1, 3, 7]. The tree looks like this:
1
1 3
5 1 3 7
Then we add 1 to the whole interval. The tree looks like this:
2
1 3
5 1 3 7
because the propagation has stopped on the first node since the updated interval covers it completely.
Then add 1 to the range [0-1]. This range does not cover the whole interval of the first node, so we update the children, and then set the min for the whole interval (that is, the value of the first node) to be the min of nodes 2 and 3. Here is the resulting tree:
2
2 3
5 1 3 7
And here is where it got wrong - there is no element 2 in the array, yet the tree claims that the min of the whole array is 2. This is happening because the lower levels of the tree never actually get the information that their values have been increased - the second node isn't aware of the fact that its values are not [5, 1] but rather [6, 2].
In order to make it work correctly, you can add a third array that keeps the values that have been added to whole intervals - say, int added_to_interval[3 * MAX_RANGE + 1];. Then, when you're updating a whole interval (the case where i <= b && j >= e), you also have to increment added_to_interval[position] with value. Also, when going up the tree to update the nodes from the values of the children, you also have to add that has been added to the whole interval (e.g. max_segment_tree[position] = max(max_segment_tree[position * 2], max_segment_tree[position * 2 + 1]) + added_to_interval[position];).
EDIT:
Here are the changes to the code to make it working:
if (i <= b && j >= e) {
max_segment_tree[position] += value;
min_segment_tree[position] += value;
added_to_interval[position] += value;
return;
}
...
update_tree(position * 2 , b , (b + e) / 2 , i, j, value);
update_tree(position * 2 + 1 , (b + e) / 2 + 1 , e , i, j, value);
max_segment_tree[position] = max(max_segment_tree[position * 2], max_segment_tree[position * 2 + 1]) + added_to_interval[position];
min_segment_tree[position] = min(min_segment_tree[position * 2], min_segment_tree[position * 2 + 1]) + added_to_interval[position];
I haven't tested it extensively - I'm leaving that to you, but I tried a bunch of examples that seemed to work correctly.
Also, I don't think you need 3 * MAX_RANGE + 1 elements in the arrays - 2 * MAX_RANGE or something like that should be enough.
[b, e] is the range, covered by *_segment_tree[ position ], and [i, j] is the current queried range.
About range storage:
*_segment_tree[ 1 ] holds max/min of the whole data array - It's the root of the tree, because array-based binary tree has to be indexed from 1. It's because children of n-th node of the tree are numbered 2*n and 2*n + 1, and 0 cannot be used as n, because in that case 2*n = n. Hereby, if *_segment_tree[k] holds min/max of data[b, e], then *segment_tree[ 2*k ] holds min/max of data[ b, ( b + e ) / 2 ] and *segment_tree[ 2*k + 1 ] - of data[ ( b + e ) / 2 + 1, e ] - you can see these indicies in the code.
The segment tree can be generically implemented by two types one is through DMA and other is through standard vector method especially this is a template code for those people who do Competitive programming
class __SEGMENTTREES
{
private:
public:
void __SegTreeCreation(int Ind, int Left, int Right, vector<int> &v, vector<int> &Seg)
{
if (Left == Right)
{
Seg[Ind] = v[Left];
return;
}
else
{
int Mid = Left + (Right - Left) / 2;
__SegTreeCreation(2 * Ind + 1, Left, Mid, v, Seg);
__SegTreeCreation(2 * Ind + 2, Mid + 1, Right, v, Seg);
Seg[Ind] = min(Seg[2 * Ind + 1], Seg[2 * Ind + 2]);
}
}
void __UpdateSegTree(int Ind, int Left, int Right, int Loc, int Newval, vector<int> &Seg)
{
int Mid = Left + (Right - Left) / 2;
if ((Left == Right) && (Left == Loc))
Seg[Ind] = Newval;
if (Mid >= Loc)
__UpdateSegTree(Ind * 2 + 1, Left, Mid, Loc, Newval, Seg), Seg[Ind] = min(Seg[2 * Ind + 1], Seg[2 * Ind + 2]);
else
__UpdateSegTree(Ind * 2 + 2, Mid + 1, Right, Loc, Newval, Seg), Seg[Ind] = min(Seg[2 * Ind + 1], Seg[2 * Ind + 2]);
}
int __SegTreeQuery(int Ind, int Left, int Right, int TreeLeft, int TreeRight, vector<int> &Seg)
{
if (Left > TreeRight || Right < TreeLeft)
return INT_MAX;
else if (TreeLeft >= Left && TreeRight <= Right)
return Seg[Ind];
else
{
int Mid = TreeLeft + (TreeRight - TreeLeft) / 2;
return (min(__SegTreeQuery(2 * Ind + 1, Left, Right, TreeLeft, Mid, Seg), __SegTreeQuery(2 * Ind + 2, Left, Right, Mid + 1, TreeRight, Seg)));
}
}
};
This is the basic Code of segment tree creation , updating the value and Queries.