# Blog #1: Nguyên lí hoạt động của x & (-x)
**Tác giả: Phan Thành Hưng (hungg_261)**
___
### Giới thiệu
Chắc hẳn nhiều bạn cũng đã học tới cấu trúc dữ liệu ==Fenwick tree== rồi. Trong đó, nếu tinh ý bạn sẽ nhận ra dòng code sau:
```
x & (-x)
```
Vậy ý nghĩa của dòng code này là gì? Cùng mình tìm hiểu về nó trong bài blog này của mình nhé :face_with_cowboy_hat:.
___
### Định nghĩa
Biểu thức `x & (-x)` trong lập trình (phép $\&$ là phép [bitwise AND](https://wiki.vnoi.info/algo/basic/bitwise-operators.md#to%C3%A1n-t%E1%BB%AD-bitwise-and-or-v%C3%A0-xor)) giúp lấy ra bit 1 thấp nhất (least significant bit, aka. bit 1 nằm ngoài cùng phía bên phải) được bật (set) trong số nguyên`x`.
Ví dụ như ta có số sau (dưới dạng nhị phân): $\text{x} = 101100_2$ thì $\text{x & (-x)}$ sẽ trả ra $000100_2$ là bit 1 thấp nhất của $\text{x}$.
### Giải thích
Vậy nguyên lí hoạt động của biểu thức này là gì, tại sao `x & (-x)` lại có thể trả ra kết quả được như vậy??? Tại sao lại có số âm trong đây??
>[!Note] Cách C++ biểu diễn số âm bằng bit
> Trước hết, ta cần hiểu cách C++ (và nhiều ngôn ngữ khác) xử lí số âm với bit. Để chuyển từ $\text x$ thành $- \text x$ thì C++ sẽ lật hết các bit lại (aka. flip - từ 1 thành 0, từ 0 thành 1), sau đó cộng 1 vào số mới sau khi lật đó. Cụ thể hơn, nếu `~` là toán tử bitwise dùng để lật (flip) hết toàn bộ bit trong một số, thì: $-\text x = (\sim \text {x}) + 1$
> Ta có thể chạy đoạn code sau để kiểm chứng:
> ```cpp
> #include<bits/stdc++.h>
> using namespace std;
>
> signed main(){
> ios_base::sync_with_stdio(0);cin.tie(0);
>
> int x = 5;
> int new_x = (~x) + 1;
>
> cout << "x = " << x << '\n';
> cout << "-x = " << -x << '\n';
> cout << "(~x) + 1 = " << new_x << '\n';
>
> return 0;
> }
> ```
> Có thể thấy output của $-\text x$ và $($~$\text {x}) + 1$ là giống nhau.
> **Lưu ý:** đây là quy ước rồi nên mình không cần chứng minh nhé :+1:
**Vậy tại sao công thức lại đúng?**
Đặt biểu diễn nhị phân $k$-bit của $\text x$ là $\overline{a_1a_2...a_k}\ \ (a_i \in \{0,1\})$.
Ví dụ với kiểu dữ liệu `int` thì $k=32$, còn `long long` thì $k=64$.
Giả sử bit 1 nhỏ nhất nằm ở vị trí $i$, lúc này $\text x$ sẽ trở thành:
$$
\overline{a_1a_2...a_i\ \underbrace{00...0}_{k-i}}
= \overline{a_1a_2...1\ \underbrace{00...0}_{k-i}}
$$
Tất cả $k-i$ bit nằm sau $a_i$ bắt buộc phải là $0$ do $a_i$ là bit 1 thấp nhất.
Để thuận tiện, ta đặt $A$ là một biểu diễn nhị phân và $A=\overline{a_1a_2...a_{i-1}}$. Lúc này $\text x$ được viết lại thành:
$$
\text x = \overline{A\ 1\ \underbrace{00...0}_{k-i}}
$$
Như mình đã nói ở trên, nếu đảo dấu thì ta flip hết các bit và cộng 1. Trước hết, ta thực hiện thao tác flip hết các bit của $\text x$, $\text x$ trở thành:
$$
\sim \text x = \overline{(\sim A)\ 0\ \underbrace{11...1}_{k-i}}
$$ Đừng quên $\sim$ là thao tác flip hết các bit trong một biểu diễn nhị phân.
Sau đó, ta thực hiện bước tiếp theo (cộng 1 vào giá trị sau khi đã được flip bit), lúc này $\text x$ là:
$$
(\sim \text x) + 1 = -\text x = \overline{(\sim A)\ 1\ \underbrace{00...0}_{k-i}}
$$
Cuối cùng, ta thực hiện phép bitwise AND ($\&$) giữa $\text x$ và $-\text x$:
$$
\text x\ \&\ (-\text x) = \overline{A\ 1\ \underbrace{00...0}_{k-i}}\quad \&\quad \overline{(\sim A)\ 1\ \underbrace{00...0}_{k-i}}
$$
Thực hiện theo từng phần một, ta có:
- $A\ \&\ (\sim A) = \underbrace{00...0}_{i-1}$
- $1\ \&\ 1 = 1$
- $\underbrace{00...0}_{k-i}\quad\&\quad \underbrace{00...0}_{k-i}\ \ =\ \ \underbrace{00...0}_{k-i}$
Ghép các kết quả có được, ta được:
$$
\text x\ \&\ (-\text x) = \overline{A\ 1\ \underbrace{00...0}_{k-i}}\quad \&\quad \overline{(\sim A)\ 1\ \underbrace{00...0}_{k-i}}
$$
$$
= \overline{\underbrace{00...0}_{i-1}\ 1\ \underbrace{00...0}_{k-i}}
$$
Có thể thấy, bit 1 duy nhất còn lại chính là bit nằm ở vị trí $i$, là $a_i$, chính là bit 1 thấp nhất. Vậy biểu thức `x & (-x)` trả ra bit 1 thấp nhất của `x`.