There is a very common branch of dynamic programming —— State compression dynamic planning , Many people are afraid of state compression , But it's not that hard , I hope this article can take you to learn this classic application .
Binary represents state
When explaining the problem of multiple backpacks , We've talked about binary representation to solve multiple knapsacks . Using the properties of binary , Split multiple items into a few items , Transformed into a simple zero one backpack to solve . Today's state compression is also inseparable from binary , But I personally feel that today's binary applications are easier to understand .
Many binary applications are inseparable from aggregate The concept , We all know that in computers , All data is stored in binary form . Generally one \(int\) Plastic surgery is 4 Bytes , That is to say 32 position bit, We go through this 32 position bit On 0 and 1 How big can a combination of 21 Billion different numbers . If we put this 32 position bit Think of it as a collection , that Each number should correspond to a state of the set , And the states of each number are different .
For example, in the picture above , We listed 5 Binary bits , We set two of them to 1, The rest of the settings are 0. We calculate , You can get 6 This number , that 6 It means (00110) This state . Numbers and states are one-to-one , Because every integer converted to binary is unique .
That is to say, an integer can be converted into a binary number , It can represent a state of a collection , The two correspond one by one . This is very important , It's the basis of all the derivation .
The binary representation of an integer can represent the state of a binary set , Since it is a state, it can be transferred . On this basis , We can come to another very important conclusion —— We can use the addition and subtraction of integers to represent the transition between states .
Let's use the example just now , In the picture above, we list 5 Binary bits , Suppose we use this 5 Binary bits indicate 5 A little ball , The numbers of these balls are 0 To 4. thus , Just now 6 It can be considered as taking 1 Number and 2 The state of two little balls .
If we take it again at this time 3 Little ball No , So the state of the set changes , We use a picture to show ：
The pen of the fans in the picture above represents the decision-making , For example, we took 3 Number one is a decision , Under the influence of this decision , The state of the collection has shifted . The number represented by the transferred set is 14, It's made up of previous sets 6 Plus the changes brought about by the transfer , That's what you get . It just means taking 3 The decision of the number one ball , So we're going to chain up the whole process .
To sum up , We use binary 0 and 1 Represents the state of a binary set . The state in which an article exists or does not exist . Because of binary 0 and 1 Can be transformed into a \(int\) Integers , In other words, we use integers to represent the state of a set . thus , We can The addition and subtraction of integers is used to represent the change of set state .
This is the essence of state compression , So called compression , In fact, it means that a set is compressed into an integer , Because integers can be used as subscripts for arrays , This operation will facilitate our coding .
There are a lot of tricks about bit operations , Link to the original text ：Here
Travel agent problem
After understanding the meaning of state compression , Let's look at a classic example , That is to say, the problem of famous travel agents .
The background of the traveling salesman problem is interesting , It is said that there is a businessman who wants to Travel all over And Trade . There are a number of One way passage Connected to a , The merchant set out from one place , I want to take the shortest journey around all the areas , What's the shortest distance for a tour ？ In this question , Let's assume that the businessman from 0 Set out , Finally, I still return to my position 0.
Let's take a look at the picture below to get a sense of ：
Let's say that our businessmen from 0 Set out , to want to Return to... After a week's tour 0, So what's the shortest distance it needs to go through ？
This picture is still relatively simple , If in In extreme cases, there is a connection between all points When , For every point , The next position it can choose is \(n-1\) Kind of . Then there are a total of alternative routes \(n!\) Kind of , This is a very big value , Obviously we can't accept . That's why we say that the problem of traveling agents is a problem of \(NP-Hard\) problem .
Now that we're here NP problem , Talk to you briefly NP The definition of the problem .
Many beginners of algorithms are very confused about these concepts , It's true , These concepts all sound similar , It's easy to get dizzy . Let's start with the simplest , First of all P problem .
P The problem can be regarded as a solved problem , The definition of this solution is that it can be done In the time complexity of polynomials solve . So called polynomials , That is to say , there k It's a constant . There are many functions opposite to polynomials , For example, exponential function 、 Factorials and so on .
\(NP\) The problem is not P The opposite of the problem , there N It can't be understood as No, It's like \(noSQL\) It's not \(SQL\) It means the same thing .\(NP\) The problem is that you can The problem of verifying solutions in polynomials .
For example, given a sequence of sorts, we can judge whether it is ordered or not , It's very simple , We just need to traverse it . Another example is factorization of large integers , It's hard for us to do factorization , But let's judge whether the solution of factorization is correct or not is much simpler , We can just multiply them and compare them with the original formula .
obviously all P The problem is NP problem , Now that we can find solutions in polynomials , So we can also verify whether the solution is correct in polynomials . But is the reverse true , The problem of whether the solution can be verified in polynomial time , It can also be solved in polynomial time by some algorithm ？ We haven't thought of algorithm yet , Or the solution doesn't exist at the beginning ？
The above question is famous NP=P The question of whether it is true , The problem is still a mystery , Some people believe in the establishment of , Some people don't believe , It's also considered one of the biggest problems of the 21st century .
In order to prove the problem , Scientists have come up with another way , It's about making rules for problems . for instance , Like solving equations , It's very simple for us to solve the equation of one variable and one degree , But it's a little more difficult to solve the bivariate equation . If we come up with a way to understand the equation of 2 yuan degree , So it can also be used to solve the equation of one variable and one degree , Because we just need to make another unknown equal to 0 It's a one-dimensional equation .
Empathy , We can also put NP Problems are transformed , Make it more difficult , Increasing to the limit becomes an ultimate problem . Because the ultimate question is all NP The problem translates into , As long as we come up with algorithms to solve the ultimate problem , that , be-all NP All the problems are solved . For example, if we come up with an idea to understand N The algorithm of the meta equation , Then all the problems of solving the equations are solved . The problem after this transformation is called NP Problem completely , It's also called NPC problem .
Let's take a look at a classic NPC problem , That is, the logic circuit problem .
Here is a logic circuit , Suppose we know that its output is \(True\) , We also know the structure of the circuit , So can we be sure that we can find a combination of inputs , So the final output is \(True\) Do you ？
It's obviously a \(NP\) problem , Because we can directly put the solution into the circuit to calculate , Then we can verify whether the solution is correct , But it's hard to get an answer . After rigorous proof , all NP All problems can be transformed into , That is to say, if we find a solution, we can solve this problem in polynomial , Then we've solved all the problems \(NP\) problem .
Last , One more \(NP-Hard\) problem ,\(NP-Hard\) The problem is that all \(NP\) The problem can be transformed into , however It's not... In itself NP problem , That is to say, we can't judge whether its solution is correct in polynomial time .
For example, the problem of traveling agents mentioned just now is one of \(NP-Hard\) problem , Because even if we give a solution , We also There is no way to quickly determine whether a given solution is correct , You have to go through all the situations before . The complexity of our verification is beyond the scope of polynomials , So it doesn't belong to \(NP\) problem , Than \(NP\) The problem is more difficult , So it's a \(NP-Hard\) problem .
State compression solution
That's it \(NP\) problem , Let's go back to the algorithm itself .
Since we are going to use the idea of dynamic planning to solve this problem , Just Can't get out of state and decision making . As mentioned above, we can use binary to represent the state of a set with an integer , It's easy to think of this state as a state of dynamic planning , But it's not true .
There are no restrictions on the transfer between simple sets , For example, in the previous examples, we have taken 1 Ball and 2 Ball No , You can take the rest of the ball in the back , But the problem with the traveling agent is different , Suppose we've been to 0 and 1 Two places , We are in the position 1, We can't use 2 and 5 The connection between the two places updates the status of , Because we can only from 1 We're on the way out . That is to say, we There are limits to the decisions that can be taken .
So we can't just take the state of the set as the State , To ensure the correct order of movement between locations , We also need to add one dimension , That is, the current position . therefore The real state is the state of the location we've traversed before , Add the current location , The combination of the two .
The state is established , The decision is simple , Where the current location can go and has not been before , All can make up a decision .
We talked about that before , In dynamic planning problems , Complexity is equal to the number of states multiplied by the number of decisions , The number of States is , The number of decisions is n, So the overall complexity is . Although the figure still seems to be exaggerated , But still more than n! Many small .
Let's take an example of , If \(n=10,n!=3628800\),, The difference between the two is more than 30 times . With n The increase of , The gap between the two will be even greater .
Last , Let's implement the following algorithm ：
import math if __name__ == "__main__": inf = 1 << 31 # Adjacency matrix stores edge weight d = [[inf for _ in range(10)] for _ in range(10)] # Test data edges = [[0, 1, 3], [1, 2, 5], [2, 3, 5], [3, 4, 3], [4, 0, 7], [4, 1, 6], [0, 3, 4], [2, 0, 4]] for u, v, l in edges: d[u][v] = l # Initialize to a value approximately infinite dp = [[inf for _ in range(5)] for _ in range((1 << 5))] dp = 0 # Traversal state for s in range(1, (1 << 5)): for u in range(5): # Traversal decision for v in range(5): # It must be demanded that this point has not been if (s >> v) & 1 == 0: continue dp[s][v] = min(dp[s][v], dp[s - (1 << v)][u] + d[u][v]) print(dp[(1 << 5) - 1])
stay ACM In the code style of the competition , We usually use u Indicates the starting point of the edge ,v Indicates the end of the edge . So the first kind of triple loop above is to traverse all States , The next two loops enumerate the start and end points , That's all the sides . We traverse the last moving edge before the current state , That is to say, the current point is v, The previous point is u, So the previous state was \(s-2^v\) , The cost of making a decision is \(d[u][v]\), That is from u To v Distance of .
If you have read the previous article , It will be found that this is an inverse dynamic planning . We enumerate the current state and all sources of the current state , So as to find the optimal solution of the current state . If you are not familiar with this concept , You can check other articles under dynamic planning .
There are two details in this code , The first detail is We didn't do u The legal judgment of , It's possible that we u It's illegal , For example, we only have 2 and 3 Two points , But we enumerate from 4 To 5 The strategy of . This is no problem , Because we started by setting all States to infinity , Only the legal state is not infinite , Because we hope that the smaller the final result, the better , Illegal status will not be used to update .
The second detail is a little more subtle , That's what we set during initialization \(dp = 0\) . This means that we started with empty sets , Not from 0 Point start . because 0 The number of states that the point has traversed is 1, Of course, we can also set it to 0 Already visited , from 0 PM , In this case, each point cannot be accessed repeatedly , So in the end we can't go back to 0 Dot , To get the right results we need to add back 0 Point of consumption needed .
The first point is the basis of the second point , If we all judge when enumerating strategies u Is the point legal , Then there is no way to implement this algorithm , because For an empty set , All points are unanswered , It's all illegal , We can't find a visiting u As a starting point for decision making .
It doesn't matter if you don't understand the above , I'll attach a slightly simpler way ：
# We from 0 The point has been traversed dp = 0 for s in range(2, (1 << 5)): for u in range(5): # Strict restrictions u Must have traversed if (s >> u) & 1 == 0: continue for v in range(5): if (s >> v) & 1 == 0: continue dp[s][v] = min(dp[s][v], dp[s - (1 << v)][u] + d[u][v]) ans = inf # Finally add back to 0 Distance of points for i in range(5): ans = min(ans, dp[(1 << 5) - 1][i] + d[i]) print(ans)
In this way , We From the State 1 Start , That is to say, we put 0 The position of the number is taken as the current point , And it's been traversed , So the sign is 1. The problem is that we can't go back to 0 了 , Because a point can only go once , So at the end of the day, we need to find back 0 The optimal path of a point .
\((1 << n) - 1\) The value of is from \(0\) To \(n-1\) All binary bits are \(1\) Value , It means \(n\) All positions have been traversed . Then we traverse all back to \(0\) Point of departure , Find the nearest one . Compared with the above method , This is easier to understand , But write a few more lines of code , But it's easier to understand . I suggest that if it is difficult to understand the first code directly , You can understand the second paragraph first , Then I want to understand why the first code also works .
I don't know how many people have successfully seen this , Dynamic planning is not simple , It's normal to find it difficult to understand the first time you learn . But it's the kind of entry that feels especially difficult before , however Once you think about it, it's a very simple question . And you can see from the amount of code , I used thousands of words to describe the algorithm , It's only a dozen lines .
Dynamic programming algorithms are always like this , The code is not long. , But every line is the essence . At this point , It's really cost-effective .
Okay , Today's article is all about , If you think you've got something , Please feel free to order recommend Well , Your help is very important to me .