I'm coding a recursive 3-way partitioning in C++, but I don't see what's wrong here. (It is 97% of the time correct, sometimes incorrect. That's making me hard to debug.)
Expected invariant: After the partitioning, A[p, ..., p + less1 + less2 - 1] is less than pivot, A[p + less1 + less2, ..., p + less1 + less2 + same1 + same2 - 1] is the same with pivot, the rest is bigger than pivot.
Wrong result: "The middle" part (the part with values the same with pivot) doesn't locate correctly.
Reproducable test case:
A = {0, 1, 4, 7, 6, 9, 3, 10, 11, 12, 2, 8, 5}
p = 0, r = 12, pivot = 11
Expected output of less : 11
Current output : 10
Code:
template <typename T>
void PPartition(std::vector<T>& A, size_t p, size_t r, const T& pivot, std::size_t& less, std::size_t& same) {
if (p == r) {
less = A[p] < pivot;
same = A[p] == pivot;
return;
}
std::size_t m = (p + r) / 2;
std::size_t less1 = 0, less2 = 0, same1 = 0, same2 = 0;
PPartition(A, p, m, pivot, less1, same1);
PPartition(A, m + 1, r, pivot, less2, same2);
if (less2 > m - p + 1 - less1) {
// sends "less" part of the right to the left
for (std::size_t k = 0; k < m - p + 1 - less1; k++) {
std::swap(A[p + less1 + k], A[m + less2 - k]);
}
// maintains "equal" part
for (std::size_t k = 0; k < same1 + same2; k++) {
std::swap(A[p + less1 + less2 + k], A[m + less2 + same2 - k]);
}
} else {
// sends "less" part of the right to the left
for (std::size_t k = 0; k < less2; k++) {
std::swap(A[p + less1 + k], A[m + less2 - k]);
}
// maintains "equal" part
for (std::size_t k = 0; k < same1 + same2; k++) {
std::swap(A[p + less1 + less2 + k], A[m + less2 + same2 - k]);
}
}
less = less1 + less2;
same = same1 + same2;
}
Self-answer:
If less2 == 0, then We shouldn't swap "middle" partition of the left part with the right part.
template <typename T>
void PPartition(std::vector<T>& A, size_t p, size_t r, const T& pivot, std::size_t& less, std::size_t& same) {
if (p == r) {
less = A[p] < pivot;
same = A[p] == pivot;
return;
}
std::size_t m = (p + r) / 2;
std::size_t less1 = 0, less2 = 0, same1 = 0, same2 = 0;
PPartition(A, p, m, pivot, less1, same1);
PPartition(A, m + 1, r, pivot, less2, same2);
if (less2 > m - p + 1 - less1) {
for (std::size_t k = 0; k < m - p + 1 - less1; k++) {
std::swap(A[p + less1 + k], A[m + less2 - k]);
}
for (std::size_t k = 0; k < same1 + same2 && p + less1 + k < m + same2 - k; k++) {
std::swap(A[p + less1 + less2 + k], A[m + less2 + same2 - k]);
}
} else {
for (std::size_t k = 0; k < less2; k++) {
std::swap(A[p + less1 + k], A[m + less2 - k]);
}
if (less2 > 0) {
for (std::size_t k = 0; k < same1 + same2; k++) {
std::swap(A[p + less1 + less2 + k], A[m + less2 + same2 - k]);
}
} else {
for (std::size_t k = 0; k < same2; k++) {
std::swap(A[p + less1 + same1 + k], A[m + same2 - k]);
}
}
}
less = less1 + less2;
same = same1 + same2;
}
Related
I would like to use this "Levenshtein" function to assess similarities between two strings (to check if user has committed a spelling mistake).
Since I work on null safe mode, it points out an error with the LIST constructor :
List<List<int>> d = List.generate(sa + 1, (int i) => List(sb + 1));
What can I write to replace List(sb+1)); ?
int levenshtein(String a, String b) {
a = a.toUpperCase();
b = b.toUpperCase();
int sa = a.length;
int sb = b.length;
int i, j, cost, min1, min2, min3;
int levenshtein;
// ignore: deprecated_member_use
List<List<int>> d = List.generate(sa + 1, (int i) => List(sb + 1));
if (a.length == 0) {
levenshtein = b.length;
return (levenshtein);
}
if (b.length == 0) {
levenshtein = a.length;
return (levenshtein);
}
for (i = 0; i <= sa; i++) d[i][0] = i;
for (j = 0; j <= sb; j++) d[0][j] = j;
for (i = 1; i <= a.length; i++)
for (j = 1; j <= b.length; j++) {
if (a[i - 1] == b[j - 1])
cost = 0;
else
cost = 1;
min1 = (d[i - 1][j] + 1);
min2 = (d[i][j - 1] + 1);
min3 = (d[i - 1][j - 1] + cost);
d[i][j] = min(min1, min(min2, min3));
}
levenshtein = d[a.length][b.length];
return (levenshtein);
}
You can use List.generate for the inner list as well.
List<List<int>> d = List.generate(sa + 1, (int i) => List.generate(sb + 1, (int j) => 0));
Also, if they're all going to be initialized to 0 you can just do this too:
List<List<int>> d = List.filled(sa + 1, List.filled(sb + 1, 0));
I'm new to MPI; so forgive me if my code is clumsy. I want to convert a n*n matrix to upper triangular form and then calculate its determinant. My code works with one processor, but with more than one processor, it doesn't work. I am hoping someone can give me some advice about it.
My code:
#include <stdio.h>
#include <string.h> /* For strlen */
#include <mpi.h> /* For MPI functions, etc */
#include<stdlib.h> /* for rand */
#include<math.h>
#define n 10
#define N 100
int main(void)
{
srand(0);
double A[N];
double temp[N];
int i;
int j;
int k;
int m=100;
int s=1;
int y;
double z;
double det=1;
int pid;
int np;
int share=0;
for (i = 0; i < N; i++)
{
A[i] = rand();
}
MPI_Init(NULL, NULL);
MPI_Comm_rank(MPI_COMM_WORLD, &pid);
MPI_Comm_size(MPI_COMM_WORLD, &np);
if (np == 1)
{
for (i = 0; i < n; i++)
{
if (A[i * n + i] == 0)
{
s = 0;
for (j = i + 1; j < n; j++)
{
if (A[j * n + i] != 0)
{
for (k = i; k < n; k++)
{
A[i * n + k] = A[i * n + k] + A[j * n + k];
}
s = 1;
}
if (s == 1)
break;
}
}
if (s != 1)
{
det = 0;
break;
}
z = A[i * n + i];
det = det * z;
for (k = i; k < n; k++)
{
A[i * n + k] = A[i * n + k] / z;
}
for (j = i + 1; j < n; j++)
{
z = A[j * n + i];
for (k = i; k < n; k++)
{
A[j * n + k] = A[j * n + k] - z * A[i * n + k];
}
}
}
for (i = 0; i < N; i++)
{
printf("element %d of matrix is %G \n", i, A[i]);
}
printf("det is %G", det);
}
else
{
for (i = 0; i < n ; i++)
{
if (A[i * n + i] == 0)
{
s = 0;
for (j = i + 1; j < n; j++)
{
if (A[j * n + i] != 0)
{
for (k = i; k < n; k++)
{
A[i * n + k] = A[i * n + k] + A[j * n + k];
}
s = 1;
}
if (s == 1)
break;
}
}
if (s != 1)
{
det = 0;
break;
}
z = A[i * n + i];
det = det * z;
for (k = i; k < n; k++)
{
A[i * n + k] = A[i * n + k] / z;
}
m = n - i;
if (m > 1)
{
if (np >= m)
{
if (pid == 0)
{
for (k = 1; k < m; k++)
{
MPI_Send(&m, 1, MPI_INT, k, 0, MPI_COMM_WORLD);
MPI_Send(&A[(k + i) * n + i], m, MPI_DOUBLE, k, 0, MPI_COMM_WORLD);
}
for (k = 1; k < m; k++)
{
MPI_Recv(&temp[0], m + 1, MPI_DOUBLE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
for (j = 0; j < m; j++)
{
A[(int(temp[m]) + i) * n + i + j] = temp[j];
}
}
printf("det is %G", det);
}
else
{
double local_A[N];
double local_z;
int local_m;
MPI_Recv(&local_m, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
MPI_Recv(&local_A[0], local_m, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
local_z = local_A[0];
for (k = 0; k < m; k++)
{
local_A[k] = local_A[k] - (local_z * A[(i * n) + i + k]);
}
local_A[k + 1] = pid;
MPI_Send(&local_A[0], local_m + 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
}
}
if (np < m)
{
if (m % np == 0)
{
if (pid == 0)
{
share = m / np;
for (k = 1; k < np; k++)
{
MPI_Send(&m, 1, MPI_INT, k, 0, MPI_COMM_WORLD);
MPI_Send(&share, 1, MPI_INT, k, 0, MPI_COMM_WORLD);
MPI_Send(&A[share * k * n], share * n, MPI_DOUBLE, k, 0, MPI_COMM_WORLD);
}
for (k = 1; k < share; k++)
{
z = A[(k + i) * n + i];
for (j = 0; j < m; j++)
{
A[(k + i) * n + i + j] = A[(k + i) * n + i + j] - z;
}
}
for (k = 1; k < np; k++)
{
MPI_Recv(&temp[0], share * m + 1, MPI_DOUBLE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
for (j = 0; j < share * m; j++)
{
A[(int(temp[share * m]) + i) * n * share + i + j] = temp[j];
}
}
printf("det is %G", det);
}
else
{
double local_A[N];
double local_z;
int local_share;
int local_m;
MPI_Recv(&local_m, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
MPI_Recv(&local_share, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
MPI_Recv(&local_A[0], local_share * n, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
for (k = 0; k < local_share; k++)
{
local_z = local_A[(k + i) * n + i];
for (j = 0; j < n; j++)
{
local_A[k * n + j] = local_A[(k + i) * n + i + j] - local_z;
}
}
local_A[local_share * local_m] = pid;
MPI_Send(&local_A[0], share * local_m + 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
}
}
if (m % np != 0)
{
if (pid == 0)
{
share = m / np;
for (k = 1; k < np; k++)
{
MPI_Send(&m, 1, MPI_INT, k, 0, MPI_COMM_WORLD);
MPI_Send(&share, 1, MPI_INT, k, 0, MPI_COMM_WORLD);
MPI_Send(&A[share * k * n], share * n, MPI_DOUBLE, k, 0, MPI_COMM_WORLD);
}
for (k = 1; k < share; k++)
{
z = A[(k + i) * n + i];
for (j = 0; j < m; j++)
{
A[(k + i) * n + i + j] = A[(k + i) * n + i + j] - z;
}
}
for (k = 1; k < np; k++)
{
MPI_Recv(&temp[0], share * m + 1, MPI_DOUBLE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
for (j = 0; j < share * m; j++)
{
A[(int(temp[share * m]) + i) * n * share + i + j] = temp[j];
}
}
for (k = 1; k < (m % np) + 1; k++)
{
y = (share * np) + k;
MPI_Send(&y, 1, MPI_INT, k, 0, MPI_COMM_WORLD);
MPI_Send(&A[(share * np + i + k) * n + i], m, MPI_DOUBLE, k, 0, MPI_COMM_WORLD);
}
for (k = 1; k < m; k++)
{
MPI_Recv(&temp[0], m + 1, MPI_DOUBLE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
for (j = 0; j < n; j++)
{
A[int(temp[n]) * n + i + j] = temp[j];
}
}
printf("det is %G", det);
}
else
{
double local_A[N];
double local_z;
double local_y;
int local_share;
int local_m;
MPI_Recv(&local_m, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
MPI_Recv(&local_share, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
MPI_Recv(&local_A[0], local_share * n, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
for (k = 0; k < local_share; k++)
{
local_z = local_A[(k + i) * n + i];
for (j = 0; j < n; j++)
{
local_A[k * n + j] = local_A[(k + i) * n + i + j] - local_z;
}
}
local_A[local_share * local_m] = pid;
MPI_Send(&local_A[0], local_share * local_m + 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
MPI_Recv(&local_y, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
MPI_Recv(&local_A[0], m, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
local_z = local_A[0];
for (j = 0; j < local_m; j++)
{
local_A[j] = local_A[j] - local_z;
}
local_A[j + 1] = local_y;
MPI_Send(&local_A[0], m + 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
}
}
}
}
}
}
MPI_Finalize();
}
Even with two or treee processor my code doesn't work, and I don't know why it doesn't work.
I am trying to find average of 2x2 block pixels within a window of 6x6 of overall image size mxn. I can able to find the average of block till the end of first row and when the code has to move to next row, it throws runtime error "exception at memory location"
vector<int>m; vector<int>m1; vector<int>m2; vector<int>m3;vector<int>m4; vector<int>m5; vector<int>m6; vector<int>m7; vector<int>m8;
for (int i = 2; i < road.rows - 2 ; i++){
for (int j = 2; j < road.cols - 2 ; j++){
//center block
int avg=(round((road.at<uchar>(i, j) + road.at<uchar>(i, j + 1) + road.at<uchar>(i + 1, j) + road.at<uchar>(i + 1, j + 1)) / 4));
//top left block
int avg1= (round((road.at<uchar>(i - 2, j - 2) + road.at<uchar>(i - 2, j - 1) + road.at<uchar>(i - 1, j - 2) + road.at<uchar>(i - 1, j - 1)) / 4));
//top
int avg2 = (round((road.at<uchar>(i - 2, j) + road.at<uchar>(i - 2, j + 1) + road.at<uchar>(i - 1, j) + road.at<uchar>(i - 1, j + 1)) / 4));
//top right block
int avg3 = (round((road.at<uchar>(i - 2, j + 2) + road.at<uchar>(i - 2, j + 3) + road.at<uchar>(i - 1, j + 2) + road.at<uchar>(i - 1, j + 3)) / 4));
//left block
int avg4 = (round((road.at<uchar>(i, j - 2) + road.at<uchar>(i, j - 1) + road.at<uchar>(i + 1, j - 2) + road.at<uchar>(i + 1, j - 1)) / 4));
//right block
int avg5 = (round((road.at<uchar>(i, j + 2) + road.at<uchar>(i, j + 3) + road.at<uchar>(i + 1, j + 2) + road.at<uchar>(i + 1, j + 3)) / 4));
//bottom left block
int avg6 = (round((road.at<uchar>(i + 2, j - 2) + road.at<uchar>(i + 2, j - 1) + road.at<uchar>(i + 3, j - 2) + road.at<uchar>(i + 3, j - 1)) / 4));
//bottom
int avg7 = (round((road.at<uchar>(i + 2, j) + road.at<uchar>(i + 2, j + 1) + road.at<uchar>(i + 3, j) + road.at<uchar>(i + 3, j + 1)) / 4));
//bottom right block
int avg8 = (round((road.at<uchar>(i + 2, j + 2) + road.at<uchar>(i + 2, j + 3) + road.at<uchar>(i + 3, j + 2) + road.at<uchar>(i + 3, j + 3)) / 4));
m.push_back(avg);
m1.push_back(avg1);
m2.push_back(avg2);
m3.push_back(avg3);
m4.push_back(avg4);
m5.push_back(avg5);
m6.push_back(avg6);
m7.push_back(avg7);
m8.push_back(avg8);
}
}
Help me out of this error;
I wrote an implementation of Hwang-Lin merge algorithm (A simple algorithm for merging two disjoint linearly ordered sets). Though it works, it turns out to be very slow. Here is my implementation:
template<typename Iter>
void hwang_lin_buf(Iter first1, Iter first2, Iter last, Iter buf_start) {
ptrdiff_t m = first2 - first1, n = last - first2, buff_size = m;
swap_blocks(first1, buf_start, m);
block_rotate(first1, first2, last);// B first1 - first1 + n, A buf_start - buf_start + m
Iter a = buf_start, b = first1, buffer = first1 + n, buffer_end = buffer + buff_size;
while (m > 0 && n > 0) {
if (n > m) {
size_t alpha = (size_t) floor(log2((double) n / m)), x = n - pow(2, alpha) + 1;
if (*(a + m - 1) < *(b + x - 1)) {
block_rotate(b + x - 1, buffer, buffer_end); //pull out the set of all elements in B >= bx
buffer = b + x - 1; //new buffer position
buffer_end = buffer + buff_size;
n = x - 1;
} else {
Iter place = binary_search(b + x - 1, b + n - 1, a + m - 1);
if (place != b + n) {
block_rotate(place, buffer, buffer_end); //pull out the set of all elements in B >= am
buffer = place;
buffer_end = buffer + buff_size;
}
std::swap(*(a + m - 1), *(buffer_end - 1)); //insert am
--buff_size;
buffer_end = buffer + buff_size;
--m;
n = place - b;
}
} else {
size_t alpha = (size_t) floor(log2((double) m / n)), x = m - std::pow(2, alpha) + 1;
if (*(b + n - 1) < *(a + x - 1)) {
swap_blocks(a + x - 1, buffer_end - m + x - 1,
m - (x - 1)); //pull out the set of all elements in A >= ax
buff_size -= m - (x - 1);
buffer_end = buffer + buff_size;
m = x - 1;
} else {
Iter place = binary_search(a + x - 1, a + m - 1, b + n - 1);
ptrdiff_t c_size = a + m - place;
swap_blocks(place, buffer_end - c_size, c_size); //pull out the set of all elements in A >= bn
buff_size -= c_size;
buffer_end = buffer + buff_size;
block_rotate(b + n - 1, buffer, buffer_end); //insert bn
--buffer;
buffer_end = buffer + buff_size;
--n;
m -= c_size;
}
}
}
if (m > 0)
swap_blocks(a, buffer, m);
}
Here are functions used in it:
template<typename Iter>
inline void swap_blocks(Iter pos1, Iter pos2, size_t len) {
for (size_t i = 0; i < len; ++i)
std::swap(*(pos1 + i), *(pos2 + i));
}
template<typename Iter>
inline Iter binary_search(Iter be, Iter en, Iter key) {//returns first iter bigger than key if nothing found
while (be < en) {
Iter mid = be + (en - be) / 2;
if (*mid < *key)
be = mid + 1;
else
en = mid;
}
if (((en == be) && (*be == *key)) || (*be > *key))
return be;
else
return be + 1;
}
template<typename Iter>
inline void floating_hole(Iter pos1, Iter pos2, size_t len) {
auto t = *(pos1 - 1);
for (size_t i = 0; i < len; ++i) {
*(pos1 + i - 1) = *(pos2 + i);
*(pos2 + i) = *(pos1 + i);
}
*(pos1 + len - 1) = t;
}
template<typename Iter>
inline void block_rotate(Iter first1, Iter first2, Iter last) {
ptrdiff_t i = first2 - first1, j = last - first2;
if (i == 0 || j == 0)
return;
Iter p = first1 + i;
while (i != j) {
if (i > j) {
swap_blocks(p - i, p, j);
i -= j;
} else {
swap_blocks(p - i, p + j - i, i);
j -= i;
}
}
swap_blocks(p - i, p, i);
}
I run tests to compare my implementation with std::inplace_merge. I expected my code to be somewhat slower, but I got following results for merging vector of 100000(two parts of 50000) int64_t with -O3 optimization:
Hwang-Lin: 5.72054 s
std::inplace_merge: 0.0010003 s
std::inplace_merge was 5000 times faster! Am I missing something?
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.