Skip to content

Commit b1e3837

Browse files
authored
Add portable formulas for fenwick calculations
Alongside the equations provided to calculate g(i) and h(i), I provided some alternative ones which are more portable. (Prior to C++20, it wasn't guaranteed that signed integers would be represented as two's complement, and the formulas I added do not rely on this, unlike the formulas previously mentioned in this Fenwick tree article. One exception would be when i = 0, but 0 & x, where x is any integer, is always 0.)
1 parent d0f1a33 commit b1e3837

File tree

1 file changed

+27
-0
lines changed

1 file changed

+27
-0
lines changed

src/data_structures/fenwick.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,18 @@ $$h(i) = i + (i ~\&~ (-i)).$$
300300

301301
As you can see, the main benefit of this approach is that the binary operations complement each other very nicely.
302302

303+
**Note**: for improved portability, the extraction of the last set bit can alternatively be computed as $i - (i ~\&~ (i-1))$. Accordingly:
304+
305+
$$g(i) = i ~\&~ (i-1)$$
306+
307+
and
308+
309+
$$h(i) = 2i - (i ~\&~ (i-1)).$$
310+
311+
This also allows the code for $g(i)$ to be succinctly written as `i &= i - 1`.
312+
313+
An example of this lack of portability is any version of C++ prior to C++20. Before [C++20](https://en.cppreference.com/w/cpp/20), it was not guaranteed for signed integers to be implemented as [two's complement](https://en.wikipedia.org/wiki/Two%27s_complement), so the use of negative numbers ($-i$) alongside bitwise-and ($~\&~$) could ([theoretically](https://en.cppreference.com/w/cpp/language/types#Range_of_values)) lead to unexpected results.
314+
303315
The following implementation can be used like the other implementations, however it uses one-based indexing internally.
304316

305317
```{.cpp file=fenwick_sum_onebased}
@@ -335,6 +347,21 @@ struct FenwickTreeOneBasedIndexing {
335347
}
336348
};
337349
```
350+
An equivalent implementation which utilizes the more portable formulas would have the following changes:
351+
352+
```cpp
353+
int sum(int idx) {
354+
int ret = 0;
355+
for (++idx; idx > 0; idx &= idx - 1)
356+
ret += bit[idx];
357+
return ret;
358+
}
359+
360+
void add(int idx, int delta) {
361+
for (++idx; idx < n; idx += idx - (idx & (idx - 1))
362+
bit[idx] += delta;
363+
}
364+
```
338365

339366
## Range operations
340367

0 commit comments

Comments
 (0)