Guest User

2d House Robber : Max Flow

a guest
Dec 17th, 2015
861
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.81 KB | None | 0 0
  1. import random
  2. """
  3. 2 Dimensional House Robber Problem
  4.  
  5. The 1D House Robber is : Given a street containing N houses labeled 1 to n
  6. each containing valuables totaling v_i to be stolen. Your task is to rob houses
  7. in order to maximize the amount of loot. However there is a security system
  8. in place and if you rob two adjacent houses an alarm will go off.
  9. Find the optimum set of non-adjacent houses to rob.
  10.    e.g. Given a list of houses represented as their total value:
  11.        houses    := [ 10, 5, 5, 20, 5]
  12.        alignment := [  1, 0, 0,  1, 0]
  13.        That is, selecting the first and fourth house maximizes profit.
  14.  
  15. This problem can be expanded into 2 dimensions, and is considerably more
  16. difficult.
  17.  
  18. The 2D House Robber is : Given a City laid out on a grid, there are R rows
  19. of houses and C columns of houses. Each house is known to contain some amount
  20. of valuables totaling v_ij. Your task is to rob as many houses as possible
  21. to maximize the amount of loot. However there is a security system in place
  22. and if you rob two adjacent houses an alarm will go off
  23. Find the optimum set of non-adjacent houses to rob.
  24.    e.g.:  Houses:  alignment:
  25.          10 20 10     0  1  0
  26.          20 30 20 =>  1  0  1
  27.          10 20 10     0  1  0
  28.        This alignment results in the maximum of 80.
  29.  
  30. Solution using Dynamic Programming:
  31.    The naive DP solution is O(R*2^C) but can be optimized to O(R*φ^C)
  32.    The solution is exponential in N
  33.  
  34. Solution using LP:
  35.    maximize c^Tx (read: c transpose x (dot product))
  36.    define constraints:
  37.         x + y <= 1 for each pair (x,y) in the grid graph
  38.         xij >= 0 for all variables
  39.    e.g. for a 2x2 grid:
  40.            5  2
  41.            8  4
  42.        maximize :
  43.            5*x11 + 2*x12 + 8*x21 + 4*x22
  44.        subject to these constraints:
  45.            x11 + x12 <= 1
  46.            x11 + x21 <= 1
  47.            x22 + x12 <= 1
  48.            x22 + x21 <= 1
  49.            x11,x12,x21,x22 >= 0
  50.    The fact that the A^T matrix is Totally Unimodular means that the solution
  51.    to this linear program has a linear solution, by duality. This proof
  52.    is rather involved.
  53.  
  54.    I havent done out the big Oh notation for this but it looks like O(N^6).
  55.  
  56. Solution using Flow:
  57.  
  58. This is essentially a Maximum Bipartite matching problem.
  59.  
  60. The grid graph is bipartite, so separate the nodes into two sets, U and V.
  61. Create the following flow network:
  62. *  create a vertex for the source S and sink T.
  63. *  for every node u in U with weight w, add an edge from S to u with capacity w
  64. *  for every node v in V with weight w, add an edge from v to T with capacity w
  65. *  for every edge (u,v) in the graph, add an edge from u to v with arbitrarily
  66.        large weight, such that any min cut would be better off cutting all the
  67.        other edges instead of any one of these.
  68.  
  69. The min cut will cut edges adjacent to S and T.
  70. Suppose we have cut this graph.
  71. Then for every edge (u,v) with large capacity, either (S,u) was cut,
  72. or (u,T) was cut.
  73. This cut is isomorphic to a selection of vertices.
  74. Thus for every edge (u,v) either u was selected or v was selected.
  75. Thus our min cut actually corresponds to a min vertex cover.
  76. Once we have the min vertex cover, we can take its complement to get
  77. the max weighted independent set.
  78. """
  79.  
  80. def printm(m):
  81.     print("----"*len(m[0]))
  82.     for row in m:
  83.         print(' '.join("%3d"%x for x in row))
  84.     print("----"*len(m[0]))
  85.  
  86. def flow_solver_update_main(C,tbl,ali,i,j,x,y):
  87.     """ compute flow from s->u->v->t
  88.     node u = (i,j) and node v=(x,y)
  89.    """
  90.     u = tbl[i][j]
  91.     v = tbl[x][y]
  92.     f = min(u,v)
  93.     if f!=0:
  94.         tbl[i][j] -= f
  95.         tbl[x][y] -= f
  96.         if tbl[x][y]==0:
  97.             print("cut %d->t"%((C+1)*x+y+1))
  98.             ali[x][y]=0
  99.         if tbl[i][j]==0:
  100.             print("cut s->%d"%((C+1)*i+j+1))
  101.             ali[i][j]=0
  102.  
  103.     return f
  104.  
  105. def flow_solver_update(R,C,tbl,ali,i,j):
  106.     """compute flow from path s to u (i,j) to each adjacent node v"""
  107.     flow = 0
  108.     if i>0:
  109.         flow+=flow_solver_update_main(C,tbl,ali,i,j,i-1,j)
  110.     if j>0:
  111.         flow+=flow_solver_update_main(C,tbl,ali,i,j,i,j-1)
  112.     if j<C:
  113.         flow+=flow_solver_update_main(C,tbl,ali,i,j,i,j+1)
  114.     if i<R:
  115.         flow+=flow_solver_update_main(C,tbl,ali,i,j,i+1,j)
  116.     return flow
  117.  
  118. def flow_solver(R,C,tbl):
  119.     """return optimal alignment for a given 2d house-robber matrix"""
  120.  
  121.     printm(tbl)
  122.  
  123.     # start with an alignment which selects all indices
  124.     # then eliminate any index corresponding to the min cut
  125.     ali=[[1,]*C for i in range(R)]
  126.  
  127.     #Given:  Partition:  valid paths:
  128.     # 1 2 3      u v u    s->1->2->t
  129.     # 4 5 6  =>  v u v    s->1->4->t
  130.     # 7 8 9      u v u    s->5->8->t (etc)
  131.     # calculate flow passing through all nodes u in U.
  132.     # this is effectivly Dinic's Algorithm for flow.
  133.     # perform a depth first search s->u->v->t
  134.     # all u->v links are implied connections based on locality in the grid.
  135.     f = 0
  136.     for i in range(R):
  137.         for j in range(i%2,C,2):
  138.             f+=flow_solver_update(R-1,C-1,tbl,ali,i,j)
  139.  
  140.     print("tbl:")
  141.     printm(tbl)
  142.     print("ali:")
  143.     printm(ali)
  144.  
  145.     print("max flow / min cut :",f) # equal to the min cut
  146.  
  147.     return ali
  148.  
  149.  
  150. def main():
  151.  
  152.     #
  153.     # 1 2 3
  154.     # 4 5 6
  155.     # 7 8 9
  156.  
  157.     tbla = [
  158.           [10,20,10], # min cut / max flow is 70
  159.           [20,30,20],
  160.           [10,20,10],
  161.     ]
  162.  
  163.     tblb = [
  164.           [25, 1,25], # min cut / max flow is 21
  165.           [ 2, 3, 4],
  166.           [ 5,25, 6],
  167.     ]
  168.  
  169.     tblc = [
  170.           [10,20,10,20,],
  171.           [20,30,20,90,],
  172.           [10,20,10,20,],
  173.     ]
  174.  
  175.     #C = 5
  176.     #R = 5
  177.     #tblr = [ [random.randint(0,20) for c in range(C)] for r in range(R) ]
  178.  
  179.     tbl=tbla
  180.     R = len(tbl)
  181.     C = len(tbl[0])
  182.     flow_solver(R,C,tbl)
  183.  
  184. if __name__ == '__main__':
  185.     main()
Advertisement
Add Comment
Please, Sign In to add comment