# II. Implementation Hiểu đơn giản là bắt tay vào code. ## II.1 - Bitmask Là một kĩ thuật tận dụng những toán tử(hay là cái gì đấy) liên quan tới `bit` để giải quyết một cách hiệu quả những bài toán yêu cầu sử dụng `bit` ### II.1-a: IsZero(X) Là 1 hàm kiểm tra xem số nguyên đó có bằng 0 không, nó trả về giá trị `bool`. ```cpp bool IsZero(int x) { if(x==0){ return true; } return false; } int x = 0; int y = 1; bool IsZero(int x) return x == 0; if(IsZero (x)) { cout << "x là số 0" } cout << "\n"; if(!IsZero (y)) { cout << "y không là số 0"; } ``` **Output** ``` x là số 0 y không là số 0 ``` ### II.1-b: IsOdd(X) IsEven(X) Là 2 hàm kiểm tra số chẵn và lẻ, Odd là lẻ và even là chẵn và trả về giá trị bool. ```cpp bool IsOdd(int x) { return (x & 1); } bool IsEven(int x) { return !(x & 1); } int x; cin >> x; if(IsOdd(x)) { cout << "x là số lẻ"; } cout << "\n"; if(IsEven(x)) { cout << "x là số chẵn"; } ``` **Input** ``` 1 ``` **Output** ``` x là số lẻ ``` **Giải thích** Để ý các số chẵn lẻ ta thấy: |Số thập phân|Nhị phân|Bit cuối|Loại| |------------|--------|--------|----| |4 |100 |0 |chẵn| |5 |101 |1 |lẻ | |6 |110 |0 |chẵn| |7 |111 |1 |lẻ | Từ đó ta suy ra chỉ cẩn kiểm tra bit cuối ta có thể biết số đó là chẵn hay lẻ ### II.1-c: IsPowerOfTwo(X) Là hàm kiểm tra 1 số có phải một số có thể viết dưới dạng 2^n^ không. ```cpp bool IsPowerOfTwo(long long x) { return (x > 0 && (x & (x - 1)) == 0); } ``` Để ý các số cần tìm ta thấy: |Số thập phân|Nhị phân| |------------|--------| |4 |100 | |4 - 1 = 3 |011 | |8 |1000 | |8 - 1 = 7 |0111 | Để ý, ta thấy số ngay trước số có thể viết dưới dạng 2^n^ không có bit nào trùng, vì vậy chỉ cần kiểm tra xem số ngay trước nó có trùng bit nào hay không và đảm bảo rằng `x > 0` ### II.1-d: IsPowerOfFour(X) IsPowerOfPowerOfTwo(X, K) Là hàm kiểm tra xem nó có phải lũy thữa của 4 hay không, không có sẵn trong c++. ```cpp //đã include cmath bool IsPowerOfTwo(long long x) { return (x > 0 && (x & (x - 1)) == 0); } bool IsPowerOfFour(long long x) { if (!IsPowerOfTwo(x)) return false; long long r = sqrt(x); return r * r == x && IsPowerOfTwo(r); } ``` Để kiểm tra xem liệu một số có thể viết dưới dạng 4^n^ hay không. ```cpp bool IsPowerOfTwo(long long x) { return (x > 0 && (x & (x - 1)) == 0); } bool IsPowerOfPowerofTwo(long long x, long long k) { if(x <= 0) return false; if(!IsPowerOfTwo(x)) return false; int p = 0; while(x > 1) { x >>= 1; p++; } return (p % k == 0); } ``` Để kiểm tra xem liệu một số có thể viết dưới dạng 2^p×k^ hay không. ### II.1-e: CheckBit(X, B) SetBit(X, B) UnsetBit(X, B) ToggleBit(X, B) `CheckBit(x,b)`: check biến thứ B xem nó có đang bật hay không, nghĩa là tại vị trí thứ b của x thì có phải số 1 không ```cpp bool CheckBit(int x, int b) { if((x & (1 << b)) == 0) {// & là kiểm tra xem 2 dãy bit đó có bit //cùng bằng 1 thấp nhất, 1 << b là tạo 1 dãy bit toàn 0 chỉ có ở //vị trí b là 1 return false; } return true; } ``` `setbit(x,b)`: bật bit thứ b lên nếu nó = 0. ```cpp int setbit(int x, int b) { return x | (1 << b); // toán tử '|' hay còn gọi là toán tử OR // nó giữ lại tất cả bit khác và chỉ bật bit thứ b lên. // OR hoạt động theo kiểu nếu 1 trong 2 bằng 1 thì nó sẽ = 1. } ``` `unsetbit(x,b)`: ngược lại với setbit, nó tắt thằng đang mở. ```cpp int UnsetBit(int x, int b) { return x & ~(1 << b); } // code này hoạt động bằng cách cho 1 dãy bit và chỉ có ở vị // trí b bằng 1, đảo lại hết tức là nêu tại b = 1 thì giờ b = 0 // tại b,nếu x = 1 hay 0 thì nó vẫn trả về 0 ``` `togglebit(x,b)`: nghịch đảo bit tại vị trí b của x. ```cpp int togglebit(int x,int b) { return x ^ (1 << b); //code hoạt động như sau: //tạo ra 1 dãy bit chỉ có b bật //sử dụng toán tử XOR(^) để nếu b đang bật thì tắt và nếu b tắt thì không làm gì //lí do hoạt động: nếu tại vị trí b, b = 1 thì toán tử sẽ biến nó thành ngược lại } ``` **Giải thích toán tử `XOR`** Ta có `x = 1010`, `b = 2` - Đầu tiên, ta có `1 << b` tạo thành một chuỗi bit `0100` - Tiếp theo, toán tử `XOR` chỉ đảo bit khi nó được kêu phải làm, và chuỗi bit `0100` chính là câu lệnh - Tại vị trí số 3, tức là `1` trong chuỗi `0100`, toán tử XOR sẽ đảo bit tại đúng vị trí đó trong x. - Kết quả: `x = 1010` → `x = 1110` ### II.1-f: AssignBit(X, B, V) Là bật hoặc tắt bit tại vị trí b của x theo v. ```cpp int AssignBit(int X, int B, int V) { if (V == 0) return X & ~(1 << B); //tắt bit, có giải thích ở unsetbit else return X | (1 << B); //bật bit,giải thích ở setbit } ``` ### II.1-g: ToBinary(X) Là chuyển đổi từ hệ thập phân sang nhị phân. ```cpp string ToBinary(int n) { string bin = ""; while(n > 0) { bin = char('0' + (n % 2)) + bin; n /= 2; } if(bin == "") bin = "0"; return bin; } ``` Dùng quy tắc chia dư cho hai. ### II.1-h: ToOctet(X) ToHex(X) ToOctet(X):Là chuyển đổi từ hệ thập phân sang bát phân. ```cpp string ToOctet(int n) { string oct = ""; while(n > 0) { oct = char('0' + (n % 8)) + oct; n /= 8; } if(oct == "") oct = "0"; return oct; } ``` Áp dụng tương tự như `ToBinary` Là chuyển đổi từ hệ thập phân sang thập lục phân(16). ```cpp string ToHex(int n) { if(n == 0) return "0"; string hex = ""; char hdigits[] = "0123456789ABCDEF"; while(n > 0) { hex = hdigits[n % 16] + hex; n /= 16; } return hex; } ``` Áp dụng tương tự như `ToBinary`, nhưng xử lí phần dư từ 10 → 15 bằng chữ cái A → F. ### II.1-i: XorSwap(X, Y) Hoán đổi 2 biến mà không cần biến thứ 3, nhưng chậm hơn swap bình thường, áp dụng bit. ```cpp int x = 3,y = 5;//đổi ra bit thì 3-->0011,5-->0101 x = x ^ y; //toán tử xor nếu 2 bit bằng nhau thì nó tắt và ngược lại. //code trên sử dụng toán tử xor cho 3 và 5 hay 0011 với 0101 //suy ra x bây giờ bằng 0110 và y vẫn bằng 0011. y = x ^ y; //lúc này đổi 0110 với 0011 suy ra y=0101=5. x = x ^ y; //đổi 0110 với 0101 suy ra x=0011=3. //vậy là đã đổi giá trị của x và y. ``` ### II.1-j: BitLength(X) Log2(X) Tính số bit cần dùng để biểu diễn X mà không có số 0 ở đầu ```cpp int bitLength(int X) { if (X == 0) return 1; int len = 0; while (X > 0) { len++; X >>= 1; //dịch sang phải 1 bit, đồng thời bỏ luôn bit cuối } return len; } ``` Tìm `n` sao cho 2^n^ = x. ```cpp //Đã include cmath cout << log2(x); ``` ### II.1-k: CountSetBit(X),CountUnsetBit(X) CountSetBit(X): đếm số bit bật - đếm số lượng số bật có trong dãy bit của 1 số. ```cpp int CountSetBit(int x) { int count = 0; while (x != 0) { if (x & 1) count++; x >>= 1; } return count; } ``` CountUnsetBit(X): đếm số bit tắt. ```cpp int CountUnsetBit(int x) { int count = 0; while (x != 0) { if (!(x & 1)) count++; x >>= 1; } return count; } ``` ### II.1-l: CountTrailingZero(X) CountTrailingOne(X) Là 2 thuật toán kiểm tra xem có bao nhiêu số 0 hoặc 1 liên tiếp trong dãy bit của số x tính từ phải sang trái, CountTrailingZero(X) là kiểm tra số 0, CountTrailingOne(X) là kiểm tra số 1. Nói cách khác là kiểm tra chia cho 2 hết hay dư tối đa bao nhiêu lần. ```cpp int CountTrailingZero(x) { int d = 0; while(x & 0 and x > 0) {//nếu bit cuối của x bằng 0 và x>0 sẽ lặp. d++;//biến đếm cộng lên. x = x >> 1;//cho x dịch trái 1 lần rồi gán lại vào x. } if(x == 0) { return "vô hạn" }// nếu x=0 thì bit cuối sẽ luôn là 0 dù có dịch trái return d; } int CountTrailingZero(x) {//tượng tự như trên chỉ khác đk while. int d = 0; while(x & 1) { d++; x = x >> 1; } return d; } ``` ### II.1-m: CountLeadingZero(X) CountLeadingOne(X) Giống CountTrailingZero(X) CountTrailingOne(X) nhưng đếm từ trái sang. ### II.1-n: LeastSignificantBit(X) MostSignificantBit(X) Tìm vị trí bit được bật ở ngoài cùng. ```cpp int LeastSignificantBit(int x) { int index = 0; while (x % 2 == 0) { x >>= 1; index++; } return index; } ``` Tìm vị trí bit được bật ở trong cùng. ```cpp int MostSignificantBit(int x) { int index = 0; while (x > 1) { x >>= 1; index++; } return index; } ``` ### II.1-o: BitLeftRotation(X, N) BitRightRotation(X, N) Là đẩy các số 1 của dãy bit qua hoặc qua phải n lần. (Chỉ dùng cho 32 bit được thôi với code này) ```cpp unsigned int BitLeftRotation(unsigned int X, int N) { return (X << N) | (X >> (32 - N)); } unsigned int BitRightRotation(unsigned int X, int N) { return (X >> N) | (X << (32 - N)); } ``` ### II.1-p: SubmaskEnumeration(X) ### II.1-q: SignOf(X) IsPositive(X) IsNegative(X) Cả 3 hàm trên đều để kiểm tra xem có phải là số âm, dương không, signof(x) trả về int 1, 0, -1 để biểu thị còn 2 cái con lại trả về bool. ```cpp= int SignOf(X) { if(x > 0) return 1; if(x < 0) return -1; return 0; } bool IsPositive(X) { if(X>0) return true; return false; } bool IsNegative(X) { if(x<0) return true; return false; } ``` ### II.1-r: HasOppositeSign(X, Y) ```cpp bool HasOppositeSign(X, Y) { if ((X < 0 && Y > 0) || (X > 0 && Y < 0)) return true; return false; } ``` ### II.1-t: BitAbs(X) Giá trị tuyệt đối ```cpp int bitabs(int x) { int y = x >> 31; (x + y) ^ y; } ``` ### II.1-u: BitMin(X, Y) BitMax(X, Y) ```cpp int BitMin(X, Y) { return x < y ? x : y; } int BitMax(X, Y) { return x < y ? y : x; } ``` ### II.1-v: LexicoNextBitPermutation(X) LexicoPrevBitPermutation(X) ### II.1-w: NextPowerOfTwo(X) PrevPowerOfTwo(X) ### II.1-x: GrayCode(X) InverseGrayCode(X) ### II.1-x: ReverseBit(X) ```cpp int ReverseBits(int n) { int ans = 0; while (n > 0) { ans <<= 1; if (n & 1 == 1) ans |= 1; n >>= 1; } return ans; } ``` ### II.1-z: GenAllBinary(N) GenAllTernary(N) GenAllQuaternary(N) GenAllGrayCode(N) ## II.2 - Bitmask Bultin ### II.2-a: Count Set Bit __builtin_popcount**(X) (**=NULL,L,LL) ### II.2-b: Count Trailing Zeros __builtin_ctz**(X) (**=NULL,L,LL) ### II.2-c: Count Leading Zeros __builtin_clz**(X) (**=NULL,L,LL) ### II.2-d: Parity Check __builtin_parity**(X) (**=NULL,L,LL) ### II.2-e: Least Significant Setbit __builtin_ffs**(X) (**=NULL,L,LL) ### II.2-f: Bit Left Rotation __builtin_rotateleft**(X, N) (**=8,16,32,64) ### II.2-g: Bit Right Rotation __builtin_rotateright**(X, N) (**=8,16,32,64) ### II.2-h: Bit Swap Endians __builtin_bswap**(X) (**=8,16,32,64)