What is a bit operation?
All the numbers in the program are stored in computer memory in binary form. To put it bluntly, bit operation is to operate the binary bits of integers directly in memory. For example, and operation is a logical operator, but and operation can also be performed between integers. For example, the binary of 6 is 1 10 and the binary of 1 11,so 6 and 1 1
1 10
And 10 1 1
-
00 10->; 2
Because bit operation directly operates on memory data and does not need to be converted into decimal, the processing speed is very fast. Of course, some people will say, what's the use of this quick? Calculating 6 and 1 1 has no practical significance. This series of articles will tell you what bit operations can do, what classic applications they have, and how to optimize your program with bit operations.
Bit operation symbols in Pascal and C languages.
The following a and b are both integer types, so:
C language Pascal language
- + -
A & ampb | a and b
A | b | a or b
One
~a | not a
A<& lt should be
a & gt& gta shr b
Note that the signs of logical operations and bit operations in C are different. 520 | 13 14 =1834, but 520 |1314 =1because of True logical operations 520 and13/kloc. The same! A and ~a are also different.
Use of various bit operations
=== 1.and operation = = =
And operation is usually used for binary bit extraction operation. For example, the result of a number and 1 is to take the last bit of the binary. This can be used to judge the parity of an integer. The last bit of the binary is 0, which means that the number is even, and the last bit is 1, which means that the number is odd.
=== 2.or operation = = =
Or operation is usually used for unconditional assignment of specific bits of binary. For example, the result of a number or 1 is to forcibly change the last bit of the binary to 1. If you need to change the last bit of the binary to 0, just subtract one from this number or 1. Its practical significance is to forcibly change this number to the nearest even number.
=== 3. XOR operation = = =
The Xor operation is usually used to negate a specific bit of a binary, because XOR can be defined as follows: 0 and 1 XOR 0 are unchanged, and XOR 1 is negated.
The inverse operation of XOR operation is itself, that is to say, the final result of two XOR operations of the same number remains unchanged, that is, (a xor b) xor b = a Xor operation can be used for simple encryption. For example, I want to say 13 14520 to my MM, but I'm afraid others will know, so the two sides agreed to take my birthday 198805 16 as the key. 1314520xor19880516 = 20665500, so I will tell MM 20665500. MM calculated the value of 20665500 XOR19880516 and got 13 14520, so she understood my intention.
Now let's look at another thing. Define two symbols # and @ (I can't find a crossed character in that circle). These two symbols are reciprocal operations, that is, (x # y) @ y = x Now execute the following three commands in turn. What is the result?
x & lt- x # y
y & lt- x @ y
x & lt- x @ y
After the first sentence is executed, X becomes X # Y, so the essence of the second sentence is Y.
Addition and subtraction are reciprocal operations, and addition satisfies the commutative law. By replacing # with+and @ with-,we can write an exchange procedure without temporary variables.
Var a, b: longint;
begin
a:= a+b;
b:= a-b;
a:= a-b;
End;
Well, didn't you just say that the inverse operation of XOR is itself? So we have a seemingly strange exchange process:
Var a, b: longint;
begin
a:= a xor b;
b:= a xor b;
a:= a xor b;
End;
=== 4. Not operation = = =
Not operation is defined as inverting all 0s and 1 in memory. Be extra careful when using the not operation. You need to pay attention to whether integer types have symbols. If the object of not is an unsigned integer (which cannot represent a negative number), then the value obtained is the difference between it and the upper bound of the type, because the number of unsigned types is represented from 00 to $FFFF in turn. The following two programs (only in different languages) both return 65435.
defined variable
A: word
begin
a:= 100;
A:= not a;
writeln(a);
End.
# include & ltstdio.h & gt
int main()
{
Unsigned short integer a =100;
a = ~ a
printf( "%d\n ",a);
Returns 0;
}
If the object of not is a signed integer, the situation is different, which we will mention later in the section "storage of integer types"
=== 5.shl operation = = =
A shl b means to convert a into binary and shift b bits to the left (followed by b zeros). For example, if 100 is1100 in binary and110000 in decimal, then10000 SHL. It can be seen that the value of a shl b is actually a times 2 to the b power, because adding a 0 after a binary number is equivalent to multiplying this number by 2.
It is generally believed that a shl 1 is faster than a * 2, because the former is a low-level operation. Therefore, the multiplication of 2 in the program should be shifted to the left as far as possible instead.
Defining some constants may use shl operation. You can conveniently use 1 shl 16- 1 to represent 65535. Many algorithms and data structures require that the data size must be a power of 2. At this time, shl can be used to define constants such as max _ n. ..
=== 6.shr operation = = =
Similar to shl, a shr b stands for binary right shift b bit (removing the last b bit), which is equivalent to a divided by 2 to the b power (rounding). We often use shr 1 instead of div 2, such as method of bisection, heap insertion and so on. Finding a method to replace division with shr can greatly improve the efficiency of the program. The binary algorithm of the greatest common divisor replaces the surprisingly slow mod operation with division by 2, and the efficiency can be improved by 60%.
Simple application of bit operation
Sometimes our program needs a small hash table to record the status. For example, when doing Sudoku, we need 27 hash tables to count the numbers in each row, column and box. At this point, we can use 27 integers less than 2 9 to record. For example, a small nine-square grid with only 2 and 5 is represented by the number 18 (binary is 0000 100 10), and the state of a line is 5 1 1, indicating that the line is full. When we need to change the state, we don't need to convert this number into binary and then back, but directly perform bit operation. When searching, expressing the state as an integer can better judge the weight and other operations. This problem is a classic example of using bit operation acceleration in search. We will see more examples in the future.
Some common binary bit conversion operations are listed below.
Function | Example | Bit Operation
- + - + -
Remove the last digit | (1011->101| xshr1
Add a 0 | (101101->1010/kloc-0) | xshl/kloc-.
Add a1| (1011->; 10 1 10 1 1)| x SHL 1+ 1
Change the last digit to 1 | (101100->10101) | x or/kloc-
Change the last digit to 0 | (101->10100) | x or 1- 1
The last bit is inverted | (1011->101/kloc-0) | xxor1
Change the k bit of the right number to1| (101->1010, k=3) | x or (/kloc-0).
Change the k bit of the right number to 0 | (10101->101001,k=3) | x and not (/kloc)
The k-th bit of the right number is inverted | (101001->; 10 1 10 1,k=3) | x xor ( 1 shl (k- 1))
Take the last three digits | (11011->101) | x and 7.
Take the last k | (11011->11kloc-0/,k=5) | x and (/)
Take k | (1101->1,k=4) | x shr (k- 1) and/kloc-0.
Change the last k bit to1| (101->1011,k=4) | x or
Finally, the k bit is inverted | (101001->; 100 1 10,k=4) | x xor ( 1 shl k- 1)
Change the continuous 1 on the right to 0 | (1001kloc-0/11->; 100 100000) | x and (x+ 1)
Change the first 0 on the right to1| (1001kloc-0/1->1001/.
Change the continuous 0 on the right to1| (11kloc-0/000->111/kloc.
Take right continuous1| (1001kloc-0/11->; 1 1 1 1)|(x xor(x+ 1))SHR 1
Remove the left | of the first 1 from the right (100101000->; 1000) | x and (x exclusive or (x- 1))
The last one will be used in the tree array.
Hexadecimal representation in 16 Pascal and C.
Pascal needs a $ sign before the binary number 16, and c needs a 0x before it. We'll use this a lot in the future.
Integer storage
None of the bit operations we mentioned earlier involve negative numbers, and we all assume that these operations are operated on unsigned/word types (integers that can only represent positive numbers). But how does the computer handle integer types with positive and negative symbols? The following two programs are all about the storage mode of 16-bit integers (only in different languages).
defined variable
A, b: integer;
begin
a:= 00;
b:= 0 1;
Write (a,'', b,'');
Answer: = $ FFFE;;
b:= $ FFFF;
Write (a,'', b,'');
A: = FFF;;
b:= 00;
writeln(a,' ',b);
End.
# include & ltstdio.h & gt
int main()
{
short int a,b;
a = 0x0000
b = 0x 000 1;
printf( "%d %d ",a,b);
a = 0xFFFE
b = 0xFFFF
printf( "%d %d ",a,b);
a = 0x 7 fff;
b = 0x8000
printf( "%d %d\n ",a,b);
Returns 0;
}
The output of both programs is 01-2-132767-32768. The first two numbers are when the memory value is the smallest, the middle two numbers are when the memory value is the largest, and the last two numbers are the boundary between positive and negative numbers. From this, we can clearly see how the computer stores an integer: the computer uses 00 to FFF to represent the numbers from 0 to 32767 in turn, and the remaining 00 to $FFFF to represent the numbers from -32768 to-1 in turn. 32-bit signed integers are stored in a similar way. If you pay a little attention, you will find that the first bit of binary is used to represent symbols, 0 means positive and 1 means negative. There is a problem here: 0 is neither positive nor negative, but it occupies the position of 00, so the number of positive numbers in the signed integer type range is one less than the number of negative numbers. After a signed number is not operated, the change of the highest bit will lead to the sign inversion, and the absolute value of the number will be different by 1. In other words, not a is actually equal to -a- 1. This way of storing integers is called "complement".
Introduction to bit operation and practical skills (II): Advanced chapter (1)
= = = = It's really strong! =====
Is 1 odd or even in binary?
We can use the following code to calculate the parity of the binary number 1 of a 32-bit integer. When there is even number 1 in the binary representation of input data, the program outputs 0 and odd number outputs 1. For example, the binary of 6543810100000101110.
defined variable
I,x,c:longint;
begin
readln(x);
c:= 0;
For i:= 1 to 32 do
begin
C:=c+x and1;
x:= x SHR 1;
End;
Writeln( c and1);
End.
However, this efficiency is not high, and the magic of bit operation has not yet been reflected.
The same is to judge the parity of binary 1, and the following code is strong. Can you see the principle of this code?
defined variable
x:longint;
begin
readln(x);
x:= x xor(x SHR 1);
x:= x xor(x SHR 2);
x:= x xor(x SHR 4);
x:= x xor(x SHR 8);
x:= x xor(x SHR 16);
Writeln(x and1);
End.
To illustrate the principle of the above code, we take 13 14520 as an example. The binary of 13 14520 is1000001110/kloc.
00000000000 10 1000000 1 1 10 1 10 1 1000
XOR 0000000000000101000001011101.
-
00000000000 1 1 1 100000 100 1 10 1 10 100
The result is a new binary number, in which the number on the I-th bit from the right indicates whether I and I+ 1 are odd numbers of 1 or even numbers of 1. For example, the rightmost 0 indicates an even number with 1 in the last two digits of the original number, and the 1 in the third digit of the right number indicates an odd number with 1 in the first digit of the original number. The result of the second XOR of this number is as follows:
00000000000 1 1 1 100000 100 1 10 1 10 100
XOR 000000000000 1 1 10000 100 1 10 1 1 10 1
-
00000000000 1 100 1 1000 10 1 1 1 10 1 100 1
Each 1 in the result indicates that this position of the original number and its first three positions have odd numbers of 1, and each 0 indicates that the four positions corresponding to the original number have even numbers of 1. Until the end of the fifth XOR, the last digit of the binary number indicates how many 1 are in the whole 32 digits, which is the final answer we want.
Calculate the binary number of 1.
Also assume that x is a 32-bit integer. After the following five assignments, the value of x is the number of bits in the binary representation of the original number 1. For example, at the beginning, x was 13 14520 (user is crazy: can you change the number? ), then x finally becomes 9, which means that there are 9 13 14520 in the binary.
X := (x and 555555)+((x shr 1) and 555555);
X := (x and 333333)+((xsshr2) and 333333);
X := (x and F0F0F0F)+((x shr 4) and f0f0f0f);
X := (x and FF00FF)+((x shr 8) and FF00FF);
X := (x and 00FFFF)+((x shr 16) and 00ffff);
For the sake of explanation, we will only explain how this program handles an 8-bit integer. Let's take the number 2 1 1 (the birthday of a certain MM in our class) as an example. The binary of 2 1 1 is110011
+ - + - + - + - + - + - + - + - +
| 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | & lt; -Original number
+ - + - + - + - + - + - + - + - +
| 1 0 | 0 1 | 0 0 | 1 0 | & lt; -After the first operation.
+ - + - + - + - +
| 0 0 1 1 | 0 0 1 0 | & lt; -After the second operation.
+ - + - +
| 0 0 0 0 1 0 1 | & lt; -After the third operation, the number is 5.
+ - +
The whole process is the idea of divide and rule. For the first time, we add every two adjacent numbers to get the number 1 in each number. For example, the first two digits of 10 means that there are two 1 in the first two digits of the original number. Continue to add two phases for the second time,10+01=10+10 =10, and the result is 0010010. Last time we added 00 1 1 and 00 10 to get the number of 1 in the whole binary. The program skillfully uses position taking and right shift. For example, in the second line, the binary number of 3333333 is 00111100 ... Using it and x for AND operation is equivalent to extracting data at intervals of 2. The function of shr is to align the same number of digits in addition operation.
Method of bisection of leading zeros in 32-bit integers.
The C language used here, I copied it directly on Hacker's Delight. This code looks better in C. If it is written in Pascal, there will be many begin and end, so the code will be ugly. The program idea is method of bisection, which should be very simple, so I won't elaborate.
Int nlz (unsigned x)
{
int n;
If (x == 0) returns (32);
n = 1;
If ((x>>16) = = 0) {n = n+16; x = x & lt& lt 16; }
If ((x>& gt24)= = 0){ n = n+8; = n+8; x = x & lt& lt8; }
If ((x>& gt28)= = 0){ n = n+4; = n+4; x = x & lt& lt4; }
If ((x>& gt30)= = 0){ n = n+2; = n+2; x = x & lt& lt2; }
n = n-(x & gt; & gt3 1);
Returns n;
}
Take the absolute value only by bit operation.
This is a very interesting question. Think about yourself first. Ctrl+A displays the answer.
Answer: Assuming that X is a 32-bit integer, the result of XXOR (not (xshr31)+xshr31) is the absolute value of X.
X shr 3 1 is the highest bit of binary, which is used to represent the symbol of X. If it is 0(x is positive), then not (x shr 3 1)+ 1 equals 000000, and the result of arbitrary XOR remains unchanged. If the highest bit is 1(x is negative), then not (xsshr3 1)+1is equal to $FFFFFFFF, and x XOR is equivalent to the inversion of all numbers, and XOR is followed by1.
High-low bit exchange
This question was actually written by me as the first question in the school's internal NOIp simulation competition. The topic is this:
Give a positive integer less than 2 32. This number can be represented by a 32-bit binary number (less than 32 bits are supplemented by 0). We call the first 16 bits of this binary number "high bits" and the last 16 bits "low bits". We can get a new number by exchanging its high and low positions. What is this new number (expressed in decimal)?
For example, the number 13 14520 is expressed in binary as 00000000100000111. The last 16 bit is the low bit, that is, 00001110101000. By exchanging its high and low bits, we get a new binary number 00001111100000000000100. The decimal number is 249036820.
At that time, few people thought of replacing lengthy programs with one-bit operations. If you use bit operation, two sentences are over.
defined variable
n:dword;
begin
readln(n);
Writeln( (n shr 16) or (nshl16));
End.
Actually Pascal has a system function swap, which can be used directly.
Binary inverse order
The following program reads a 32-bit integer and outputs a number represented by its binary reverse order.
Input: 13 14520 (binary is 0000000010000011kloc-0//kloc-0.
Output: 460335 104 (binary is 000101111000000/.
defined variable
x:dword;
begin
readln(x);
X := (x and 555555) shl 1 or (x and $ AAAAAAAA) SHR1;
X := (x and 333333) shl 2 or (X and $ CCCCCCCCC) SHR 2;
X := (x and F0F0F0F) shl 4 or (x and $ f0f0f0) SHR 4;
X := (x and FF00FF) shl 8 or (x and $ ff00ff00) SHR 8;
X := (x and 00FFFF) shl 16 or (x and $ ffff0000) SHR16;
writeln(x);
End.
Its principle is basically the same as the example of finding the number of 1 in binary just now. The program first exchanges the numbers on every two adjacent bits, and then regards the exchanged numbers as a whole, and continues the left-right exchange operation in units of 2 bits and 4 bits. We use the 8-bit integer 2 1 1 to demonstrate the program execution process again:
+ - + - + - + - + - + - + - + - +
| 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | & lt; -Original number
+ - + - + - + - + - + - + - + - +
| 1 1 | 1 0 | 0 0 | 1 1 | & lt; -After the first operation.
+ - + - + - + - +
| 1 0 1 1 | 1 1 0 0 | & lt; -After the second operation.
+ - + - +
| 1 1 0 1 0 1 1 | & lt; -After the third operation.
+ - +
Introduction to bit operation and practical skills (3): advanced chapter (2)
Today we will look at two slightly more complicated examples.
N queen question bit operation version
I won't tell you what's wrong with Queen N. Anyone who studies programming must have seen it. The following ten lines of code is an efficient bit operation program for the N-Queen problem, and everyone who has seen it is full of praise. Initially, upperlim: = (1shln)-1. After the main program calls test (0,0,0), the value of sum is the total solution number of Queen N. Take this to USACO, 0.3s, cool.
Program testing (row, ld, rd: longint);
defined variable
pos,p:longint;
begin
If it's ok<& gt, then it's upperlim.
begin
Pos: = upper and not (row or ld or rd);
When pos<& gt0 do
begin
P:=pos and-pos;
pos:= pos-p;
test(row+p,(ld+p)shl 1,(rd+p)SHR 1);
End;
end
else Inc(sum);
End;
At first glance, this seems completely confusing. In fact, the whole process is very easy to understand. Here, I still suggest you run and get to know it yourself. If you haven't learned it, look at the following explanation.
Like the ordinary algorithm, this is a recursive process, and the program finds the place to put the queen line by line. The program uses three parameters, row, ld and rd, to indicate where the line cannot be placed under the restriction of vertical column and two diagonal directions. Let's take a 6x6 chessboard as an example to see how the program works. Suppose we have recursively reached the fourth level, and the children in the first three levels have been marked on the left. The red, blue, and green lines respectively indicate the positions with conflicts in the three directions, and the positions with conflicts on this line are indicated by 1 in row, ld, and rd. Combine the three of them and get all the forbidden positions in the line. After inversion, all the positions that can be put are obtained (represented by pos). As mentioned earlier, -a is equivalent to not a+ 1, and the code line 6 here is equivalent to pos and (not pos+ 1), and the result is to take out the rightmost 1. In this way, p represents a location where a row can be placed, it is deleted from the pos, and the test procedure is called recursively. Pay attention to the changes of three parameters when calling recursively. Each parameter adds a forbidden bit, but the influence of two diagonal forbidden bits on the next line needs to be shifted by one bit. Finally, if row =1111is found recursively at a certain moment, it means that all six queens have been put in. At this time, the program jumps from line 1 to line 1 1 for solution.
~ ~ ~ ~ = = ~ = = = = Gorgeous dividing line = = = = ~ = = = = = = = ~ ~
Gray coding
If I have four potential girlfriends, I need to decide who to be with. A simple way is to associate with each MM for a period of time in turn, and finally choose the MM that brings me the greatest "satisfaction". But after reading dd Niu's theory, things start to get complicated: I can choose to be with multiple MMs, so the assessed state becomes 2 4 =16 (including the state of 0000, of course, because I may be a glass). The question now is, in what order should I traverse these 16 states?
Traditionally, all possible combinations are traversed in the order of binary numbers. In other words, I need to use 0000-& gt;; 000 1->; 00 10->; 00 1 1->; 0 100->; ...-& gt; 1111Test each state in this order. This order is unscientific, and the transfer of state is very time-consuming in many cases. For example, from 0 1 1 1 to 1000, I need to temporarily remove all the existing 3 mm, and then put the fourth MM. What a huge project it is to change the relationship between all MM and me at the same time. So I want to know if there is any way to change (chase or throw) my relationship with a MM from the state without MM, just traverse all possible combinations after 15 operations (the final state is not necessarily11). Try it yourself first.
The solution to this problem is ingenious. Let's explain how to get the traversal order of n=3 if we already know the legal traversal order of n=2. Obviously, the traversal order of n=2 is as follows:
00
0 1
1 1
10
You may have thought of how to extend the above traversal order to n=3. When n=3, a * * * has eight states. The first four states copy the ergodic order of n=2, then fold symmetrically, and add 1 in front as the last four states:
000
00 1
0 1 1
0 10 ↑
-
1 10 ↓
1 1 1
10 1
100