CSenshi

temp_hamilton.py

May 2nd, 2020
351
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.96 KB | None | 0 0
  1. from typing import List
  2.  
  3. """
  4.    A Hamiltonian cycle (Hamiltonian circuit) is a graph cycle
  5.    through a graph that visits each node exactly once.
  6.  
  7.    Determining whether such paths and cycles exist in graphs
  8.    is the 'Hamiltonian path problem', which is NP-complete.
  9.    
  10.    Wikipedia: https://en.wikipedia.org/wiki/Hamiltonian_path
  11. """
  12.  
  13.  
  14. def valid_connection(
  15.     graph: List[List[int]], next_ver: int, curr_ind: int, path: List[int]
  16. ) -> bool:
  17.     """
  18.    Checks whether it is possible to add next into path by validating 2 statements
  19.    1. There should be path between current and next vertex
  20.    2. Next vertex should not be in path
  21.    If both validations succeeds we return true saying that it is possible to connect this vertices
  22.    either we return false
  23.    
  24.    Case 1:Use exact graph as in main function, with initialized values
  25.  
  26.    >>> graph = [[0, 1, 0, 1, 0],
  27.    ...          [1, 0, 1, 1, 1],
  28.    ...          [0, 1, 0, 0, 1],
  29.    ...          [1, 1, 0, 0, 1],
  30.    ...          [0, 1, 1, 1, 0]]
  31.    >>> path = [0, -1, -1, -1, -1, 0]
  32.    >>> curr_ind = 1
  33.    >>> next_ver = 1
  34.    >>> valid_connection(graph, next_ver, curr_ind, path)
  35.    True
  36.  
  37.    Case 2: Same graph, but trying to connect to node that is already in path
  38.    >>> path = [0, 1, 2, 4, -1, 0]
  39.    >>> curr_ind = 4
  40.    >>> next_ver = 1
  41.    >>> valid_connection(graph, next_ver, curr_ind, path)
  42.    False
  43.    """
  44.  
  45.     # 1. Validate that path exists between current and next vertices
  46.     if graph[path[curr_ind - 1]][next_ver] == 0:
  47.         return False
  48.  
  49.     # 2. Validate that next vertex is not already in path
  50.     return not any(vertex == next_ver for vertex in path)
  51.  
  52.  
  53. def util_hamilton_cycle(graph: List[List[int]], path: List[int], curr_ind: int) -> bool:
  54.     """
  55.    Pseudo-Code
  56.  
  57.    Base Case:
  58.    1. Chceck if we visited all of vertices
  59.        1.1 If last visited vertex has path to starting vertex return True either return False
  60.  
  61.    Recursive Step:
  62.    2. Iterate over each vertex
  63.        Check if next vertex is valid for transiting from current vertex
  64.            2.1 Remember next vertex as next transition
  65.            2.2 Do recursive call and check if going to this vertex solves problem
  66.            2.3 if next vertex leads to solution return True
  67.            2.4 else backtrack, delete remembered vertex
  68.            
  69.    Case 1: Use exact graph as in main function, with initialized values
  70.    >>> graph = [[0, 1, 0, 1, 0],
  71.    ...          [1, 0, 1, 1, 1],
  72.    ...          [0, 1, 0, 0, 1],
  73.    ...          [1, 1, 0, 0, 1],
  74.    ...          [0, 1, 1, 1, 0]]
  75.    >>> path = [0, -1, -1, -1, -1, 0]
  76.    >>> curr_ind = 1
  77.    >>> util_hamilton_cycle(graph, path, curr_ind)
  78.    True
  79.    >>> print(path)
  80.    [0, 1, 2, 4, 3, 0]
  81.  
  82.    Case 2: Use exact graph as in previous case, but in the properties taken from middle of calculation
  83.    >>> graph = [[0, 1, 0, 1, 0],
  84.    ...          [1, 0, 1, 1, 1],
  85.    ...          [0, 1, 0, 0, 1],
  86.    ...          [1, 1, 0, 0, 1],
  87.    ...          [0, 1, 1, 1, 0]]
  88.    >>> path = [0, 1, 2, -1, -1, 0]
  89.    >>> curr_ind = 3
  90.    >>> util_hamilton_cycle(graph, path, curr_ind)
  91.    True
  92.    >>> print(path)
  93.    [0, 1, 2, 4, 3, 0]
  94.    """
  95.  
  96.     # Base Case
  97.     if curr_ind == len(graph):
  98.         # return whether path exists between current and starting vertices
  99.         return graph[path[curr_ind - 1]][path[0]] == 1
  100.  
  101.     # Recursive Step
  102.     for next in range(0, len(graph)):
  103.         if valid_connection(graph, next, curr_ind, path):
  104.             # Insert current vertex  into path as next transition
  105.             path[curr_ind] = next
  106.             # Validate created path
  107.             if util_hamilton_cycle(graph, path, curr_ind + 1):
  108.                 return True
  109.             # Backtrack
  110.             path[curr_ind] = -1
  111.     return False
  112.  
  113.  
  114. def hamilton_cycle(graph: List[List[int]], start_index: int = 0) -> List[int]:
  115.     """
  116.    Wrapper function to call subroutine called util_hamilton_cycle,
  117.    which will either return array of vertices indicating hamiltonian cycle
  118.    or an empty list indicating that hamiltonian cycle was not found.
  119.  
  120.    Case 1:
  121.    Following graph consists of 5 edges.
  122.    If we look closely, we can see that there are multiple Hamiltonian cycles.
  123.    For example one result is when we iterate like:
  124.    (0)->(1)->(2)->(4)->(3)->(0)
  125.    
  126.    (0)---(1)---(2)
  127.     |   /   \  |
  128.     |  /     \ |
  129.     | /       \ |
  130.     |/         \|
  131.    (3)---------(4)
  132.    >>> graph = [[0, 1, 0, 1, 0],
  133.    ...          [1, 0, 1, 1, 1],
  134.    ...          [0, 1, 0, 0, 1],
  135.    ...          [1, 1, 0, 0, 1],
  136.    ...          [0, 1, 1, 1, 0]]
  137.    >>> hamilton_cycle(graph)
  138.    [0, 1, 2, 4, 3, 0]
  139.    
  140.    Case 2:
  141.    Same Graph as it was in Case 1, changed starting index from default to 3
  142.    
  143.    (0)---(1)---(2)
  144.     |   /   \  |
  145.     |  /     \ |
  146.     | /       \ |
  147.     |/         \|
  148.    (3)---------(4)
  149.    >>> graph = [[0, 1, 0, 1, 0],
  150.    ...          [1, 0, 1, 1, 1],
  151.    ...          [0, 1, 0, 0, 1],
  152.    ...          [1, 1, 0, 0, 1],
  153.    ...          [0, 1, 1, 1, 0]]
  154.    >>> hamilton_cycle(graph, 3)
  155.    [3, 0, 1, 2, 4, 3]
  156.  
  157.    Case 3:
  158.    Following Graph is exactly what it was before, but edge 3-4 is removed.
  159.    Result is that there is no Hamiltonian Cycle anymore.
  160.    
  161.    (0)---(1)---(2)
  162.     |   /   \  |
  163.     |  /     \ |
  164.     | /       \ |
  165.     |/         \|
  166.    (3)         (4)
  167.    >>> graph = [[0, 1, 0, 1, 0],
  168.    ...          [1, 0, 1, 1, 1],
  169.    ...          [0, 1, 0, 0, 1],
  170.    ...          [1, 1, 0, 0, 0],
  171.    ...          [0, 1, 1, 0, 0]]
  172.    >>> hamilton_cycle(graph,4)
  173.    []
  174.    """
  175.  
  176.     # Initialize path with -1, indicating that we have not visited them yet
  177.     path = [-1] * (len(graph) + 1)
  178.     # initialize start and end of path with starting index
  179.     path[0] = path[-1] = start_index
  180.     # evaluate and if we find answer return path either return empty array
  181.     if util_hamilton_cycle(graph, path, 1):
  182.         return path
  183.     return []
Add Comment
Please, Sign In to add comment