-
Notifications
You must be signed in to change notification settings - Fork 210
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #93 from priyanka350/daaupdates
Addition of Design and Analysis of Algorithms
- Loading branch information
Showing
41 changed files
with
2,447 additions
and
0 deletions.
There are no files selected for viewing
95 changes: 95 additions & 0 deletions
95
...res/Design_and_Analysis_of_Algorithms/All_Pair_Shortest_path_problems/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# All-Pairs Shortest Path (APSP) | ||
|
||
## What is All-Pairs Shortest Path? | ||
|
||
The **All-Pairs Shortest Path (APSP)** problem is a classic problem in graph theory. It involves finding the shortest paths between all pairs of nodes in a weighted graph. For every pair of nodes \(u\) and \(v\), the algorithm determines the shortest distance (or path) from \(u\) to \(v\). | ||
|
||
### Problem Statement: | ||
|
||
Given a graph \(G\) with \(n\) vertices and weighted edges, find the shortest paths between every pair of vertices. The weights on the edges may be positive or negative, but the graph should not contain negative-weight cycles. | ||
|
||
### Key Algorithms: | ||
|
||
There are several efficient algorithms designed to solve the APSP problem, two of the most well-known being: | ||
|
||
1. **Floyd-Warshall Algorithm** | ||
2. **Johnson’s Algorithm** | ||
|
||
--- | ||
|
||
## 1. Floyd-Warshall Algorithm | ||
|
||
### Overview: | ||
|
||
The **Floyd-Warshall Algorithm** is a dynamic programming-based algorithm used to solve the APSP problem in \(O(n^3)\) time, where \(n\) is the number of vertices in the graph. It works by iteratively improving the shortest path estimates between all pairs of nodes, considering each node as an intermediate point along potential paths. | ||
|
||
### Steps: | ||
|
||
1. **Initialization**: Start with a distance matrix where the direct edge weight between vertices is given. If no edge exists, the distance is set to infinity. | ||
2. **Update**: For each pair of nodes, check whether including an intermediate node results in a shorter path. Update the distance matrix accordingly. | ||
3. **Result**: The final matrix contains the shortest paths between all pairs of nodes. | ||
|
||
### Time Complexity: | ||
|
||
- **Time Complexity**: \(O(n^3)\) | ||
- **Space Complexity**: \(O(n^2)\) | ||
|
||
### Applications: | ||
|
||
- **Routing Algorithms**: Used in network routing to determine the most efficient paths for data packets between nodes. | ||
- **Game Development**: Helps in finding the shortest paths for characters or elements to travel in a virtual world. | ||
- **Geographic Mapping Systems**: Identifying the quickest travel routes between cities or locations on a map. | ||
|
||
--- | ||
|
||
## 2. Johnson's Algorithm | ||
|
||
### Overview: | ||
|
||
**Johnson’s Algorithm** is an advanced approach for solving the APSP problem, especially when the graph contains sparse edges (i.e., fewer edges compared to a complete graph). The algorithm modifies the weights of the graph to ensure no negative-weight edges, then applies **Dijkstra’s Algorithm** from each node. | ||
|
||
### Steps: | ||
|
||
1. **Graph Reweighting**: Use **Bellman-Ford Algorithm** to adjust the edge weights to ensure all weights are non-negative. | ||
2. **Dijkstra’s Algorithm**: Apply Dijkstra’s Algorithm from each vertex to determine the shortest path to all other vertices. | ||
3. **Result**: After reweighting, the shortest paths are calculated efficiently in \(O(n^2 \log n + nm)\) time. | ||
|
||
### Time Complexity: | ||
|
||
- **Time Complexity**: \(O(n^2 \log n + nm)\) (where \(m\) is the number of edges) | ||
- **Space Complexity**: \(O(n^2)\) | ||
|
||
### Applications: | ||
|
||
- **Large Sparse Graphs**: Johnson’s algorithm is preferred when the graph is sparse (i.e., has fewer edges compared to vertices), such as in road networks or network topology. | ||
- **Telecommunication Networks**: Used to optimize routing paths in large-scale communication systems. | ||
- **Social Networks**: Helps in identifying the shortest relationships or interactions between individuals in a social network. | ||
|
||
--- | ||
|
||
## Key Differences Between Floyd-Warshall and Johnson's Algorithm: | ||
|
||
| Algorithm | Time Complexity | Space Complexity | Suitable For | | ||
|-------------------|--------------------------|------------------|---------------------------------------| | ||
| **Floyd-Warshall**| \(O(n^3)\) | \(O(n^2)\) | Dense graphs, simpler implementation | | ||
| **Johnson’s** | \(O(n^2 \log n + nm)\) | \(O(n^2)\) | Sparse graphs, more complex, scalable | | ||
|
||
--- | ||
|
||
## Applications of APSP Algorithms | ||
|
||
1. **Network Design**: Efficient pathfinding in telecommunication and transportation networks to ensure minimum-cost routing. | ||
2. **Web Mapping Services**: Shortest path algorithms are used in GPS and web services like Google Maps to find optimal routes between locations. | ||
3. **Social Network Analysis**: Determine centrality, influence, and shortest interactions in social graphs. | ||
4. **Robotics and Pathfinding**: Autonomous systems use APSP algorithms to navigate efficiently in environments by planning the shortest routes between points. | ||
5. **Traffic Management Systems**: In urban settings, APSP helps model and manage traffic flow by finding the least congested routes between intersections. | ||
|
||
--- | ||
|
||
## Conclusion | ||
|
||
The **All-Pairs Shortest Path (APSP)** problem is fundamental in graph theory and computer science. Understanding the Floyd-Warshall and Johnson’s algorithms, along with their applications, is critical for solving pathfinding problems in diverse areas like networks, robotics, and optimization. | ||
|
||
By mastering these algorithms, you can optimize real-world systems and solve complex graph-related challenges efficiently. | ||
|
||
--- |
45 changes: 45 additions & 0 deletions
45
...tures/Design_and_Analysis_of_Algorithms/All_Pair_Shortest_path_problems/floyd_warshall.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
def floydWarshall(graph): | ||
# Create a distance matrix initialized with the values from the input graph | ||
dist = list(map(lambda i: list(map(lambda j: j, i)), graph)) | ||
|
||
# Iterate through each vertex as an intermediate point | ||
for k in range(len(graph)): | ||
# Iterate through each source vertex | ||
for i in range(len(graph)): | ||
# Iterate through each destination vertex | ||
for j in range(len(graph)): | ||
# Update the shortest distance between vertices i and j | ||
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]) | ||
|
||
# Print the final solution showing shortest distances | ||
printSolution(dist) | ||
|
||
def printSolution(dist): | ||
# Print the header for the distance matrix | ||
print("Following matrix shows the shortest distances between every pair of vertices:") | ||
# Iterate through each row in the distance matrix | ||
for i in range(len(dist)): | ||
# Iterate through each column in the distance matrix | ||
for j in range(len(dist)): | ||
# Check if the distance is infinite (no connection) | ||
if dist[i][j] == INF: | ||
print("%7s" % ("INF"), end=" ") # Print 'INF' if there's no path | ||
else: | ||
print("%7d\t" % (dist[i][j]), end=' ') # Print the distance if it exists | ||
# Print a newline at the end of each row | ||
if j == len(dist) - 1: | ||
print() | ||
|
||
if __name__ == "__main__": | ||
V = int(input("Enter the number of vertices: ")) # Get the number of vertices from the user | ||
INF = float('inf') # Define infinity for graph initialization | ||
|
||
graph = [] # Initialize an empty list to represent the graph | ||
print("Enter the graph as an adjacency matrix (use 'INF' for no connection):") | ||
# Read the adjacency matrix from user input | ||
for i in range(V): | ||
row = list(map(lambda x: float('inf') if x == 'INF' else int(x), input().split())) | ||
graph.append(row) # Append each row to the graph | ||
|
||
# Call the Floyd-Warshall function with the constructed graph | ||
floydWarshall(graph) |
92 changes: 92 additions & 0 deletions
92
..._Structures/Design_and_Analysis_of_Algorithms/All_Pair_Shortest_path_problems/johnsons.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
from collections import defaultdict | ||
|
||
# Define a constant for infinity | ||
INT_MAX = float('Inf') | ||
|
||
def Min_Distance(dist, visit): | ||
# Initialize minimum distance and the corresponding vertex | ||
minimum, minVertex = INT_MAX, -1 | ||
# Iterate through all vertices to find the vertex with the minimum distance | ||
for vertex in range(len(dist)): | ||
if minimum > dist[vertex] and not visit[vertex]: # Check if the vertex is not visited | ||
minimum, minVertex = dist[vertex], vertex # Update minimum distance and vertex | ||
return minVertex # Return the vertex with the minimum distance | ||
|
||
def Dijkstra_Algorithm(graph, Altered_Graph, source): | ||
tot_vertices = len(graph) # Total number of vertices in the graph | ||
sptSet = defaultdict(lambda: False) # Set to track the shortest path tree | ||
dist = [INT_MAX] * tot_vertices # Initialize distances to infinity | ||
dist[source] = 0 # Distance from source to itself is 0 | ||
|
||
# Loop through all vertices | ||
for _ in range(tot_vertices): | ||
curVertex = Min_Distance(dist, sptSet) # Find the vertex with the minimum distance | ||
sptSet[curVertex] = True # Mark the vertex as visited | ||
|
||
# Update distances to adjacent vertices | ||
for vertex in range(tot_vertices): | ||
# Check for an edge and if the current distance can be improved | ||
if (not sptSet[vertex] and | ||
dist[vertex] > dist[curVertex] + Altered_Graph[curVertex][vertex] and | ||
graph[curVertex][vertex] != 0): | ||
dist[vertex] = dist[curVertex] + Altered_Graph[curVertex][vertex] # Update the distance | ||
|
||
# Print the final distances from the source vertex | ||
for vertex in range(tot_vertices): | ||
print(f'Vertex {vertex}: {dist[vertex]}') # Output the distance for each vertex | ||
|
||
def BellmanFord_Algorithm(edges, graph, tot_vertices): | ||
# Initialize distances from source to all vertices as infinity | ||
dist = [INT_MAX] * (tot_vertices + 1) | ||
dist[tot_vertices] = 0 # Set the distance to the new vertex (source) as 0 | ||
|
||
# Add edges from the new source vertex to all other vertices | ||
for i in range(tot_vertices): | ||
edges.append([tot_vertices, i, 0]) | ||
|
||
# Relax edges repeatedly for the total number of vertices | ||
for _ in range(tot_vertices): | ||
for (source, destn, weight) in edges: | ||
# Update distance if a shorter path is found | ||
if dist[source] != INT_MAX and dist[source] + weight < dist[destn]: | ||
dist[destn] = dist[source] + weight | ||
|
||
return dist[0:tot_vertices] # Return distances to original vertices | ||
|
||
def JohnsonAlgorithm(graph): | ||
edges = [] # Initialize an empty list to store edges | ||
# Create edges list from the graph | ||
for i in range(len(graph)): | ||
for j in range(len(graph[i])): | ||
if graph[i][j] != 0: # Check for existing edges | ||
edges.append([i, j, graph[i][j]]) # Append edge to edges list | ||
|
||
# Get modified weights using the Bellman-Ford algorithm | ||
Alter_weights = BellmanFord_Algorithm(edges, graph, len(graph)) | ||
# Initialize altered graph with zero weights | ||
Altered_Graph = [[0 for _ in range(len(graph))] for _ in range(len(graph))] | ||
|
||
# Update the altered graph with modified weights | ||
for i in range(len(graph)): | ||
for j in range(len(graph[i])): | ||
if graph[i][j] != 0: # Check for existing edges | ||
Altered_Graph[i][j] = graph[i][j] + Alter_weights[i] - Alter_weights[j] | ||
|
||
print('Modified Graph:', Altered_Graph) # Output the modified graph | ||
|
||
# Run Dijkstra's algorithm for each vertex as the source | ||
for source in range(len(graph)): | ||
print(f'\nShortest Distance with vertex {source} as the source:\n') | ||
Dijkstra_Algorithm(graph, Altered_Graph, source) # Call Dijkstra's algorithm | ||
|
||
if __name__ == "__main__": | ||
V = int(input("Enter the number of vertices: ")) # Get number of vertices from user | ||
graph = [] # Initialize an empty list for the graph | ||
print("Enter the graph as an adjacency matrix (use 0 for no connection):") | ||
# Read the adjacency matrix from user input | ||
for _ in range(V): | ||
row = list(map(int, input().split())) # Read a row of the adjacency matrix | ||
graph.append(row) # Append the row to the graph | ||
|
||
# Call the Johnson's algorithm with the input graph | ||
JohnsonAlgorithm(graph) |
95 changes: 95 additions & 0 deletions
95
...ms_and_Data_Structures/Design_and_Analysis_of_Algorithms/Backtracking/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# Backtracking | ||
|
||
## What is Backtracking? | ||
|
||
**Backtracking** is an algorithmic technique used to solve problems incrementally by building possible solutions and discarding those that fail to satisfy the constraints of the problem. It’s a depth-first search approach where we explore all possible paths to find a solution and backtrack whenever we hit a dead-end, i.e., when the current solution cannot be extended further without violating the problem’s constraints. | ||
|
||
### Steps of Backtracking: | ||
|
||
1. **Choose**: Start with an initial state and make a choice that seems feasible. | ||
2. **Explore**: Recursively explore each choice to extend the current solution. | ||
3. **Backtrack**: If the current choice leads to a dead-end, discard it and backtrack to try another option. | ||
|
||
Backtracking efficiently prunes the search space by eliminating paths that do not lead to feasible solutions, making it an ideal approach for solving combinatorial problems. | ||
|
||
### Key Characteristics: | ||
|
||
- **Recursive Approach**: Backtracking often involves recursion to explore all possible solutions. | ||
- **Exhaustive Search**: It tries out all possible solutions until it finds the correct one or determines none exists. | ||
- **Constraint Satisfaction**: Backtracking is well-suited for problems with constraints, where solutions must satisfy certain rules. | ||
|
||
--- | ||
|
||
## Applications of Backtracking | ||
|
||
### 1. **Graph Coloring** | ||
|
||
**Graph Coloring** is the problem of assigning colors to the vertices of a graph such that no two adjacent vertices share the same color. The challenge is to do this using the minimum number of colors. | ||
|
||
- **Backtracking Approach**: Starting with the first vertex, assign a color and move to the next vertex. If no valid color is available for the next vertex, backtrack and try a different color for the previous vertex. | ||
|
||
- **Time Complexity**: \(O(m^n)\), where \(m\) is the number of colors and \(n\) is the number of vertices. | ||
|
||
- **Use Case**: Scheduling problems, where tasks need to be scheduled without conflicts (e.g., class timetabling). | ||
|
||
### 2. **Hamiltonian Cycle** | ||
|
||
The **Hamiltonian Cycle** problem seeks a cycle in a graph that visits each vertex exactly once and returns to the starting point. | ||
|
||
- **Backtracking Approach**: Start from a vertex and add other vertices to the path one by one, ensuring that each added vertex is not already in the path and has an edge connecting it to the previous vertex. If a vertex leads to a dead-end, backtrack and try another path. | ||
|
||
- **Time Complexity**: Exponential, typically \(O(n!)\), where \(n\) is the number of vertices. | ||
|
||
- **Use Case**: Circuit design and optimization, where paths or tours need to be found efficiently. | ||
|
||
### 3. **Knight's Tour** | ||
|
||
The **Knight's Tour** problem involves moving a knight on a chessboard such that it visits every square exactly once. | ||
|
||
- **Backtracking Approach**: Starting from a given position, the knight makes a move to an unvisited square. If a move leads to a dead-end (i.e., no further valid moves), backtrack and try a different move. | ||
|
||
- **Time Complexity**: \(O(8^n)\), where \(n\) is the number of squares on the board (typically \(n = 64\) for a standard chessboard). | ||
|
||
- **Use Case**: Chess puzzle solvers and pathfinding problems on a grid. | ||
|
||
### 4. **Maze Solving** | ||
|
||
The **Maze Solving** problem involves finding a path from the entrance to the exit of a maze, moving only through valid paths. | ||
|
||
- **Backtracking Approach**: Starting from the entrance, attempt to move in one direction. If the path leads to a dead-end, backtrack and try another direction until the exit is reached. | ||
|
||
- **Time Complexity**: Depends on the size of the maze, typically \(O(4^n)\) for an \(n \times n\) maze. | ||
|
||
- **Use Case**: Robotics and AI navigation systems, where the goal is to find the optimal route through a complex environment. | ||
|
||
### 5. **N-Queens Problem** | ||
|
||
The **N-Queens Problem** is a classic puzzle where the goal is to place \(N\) queens on an \(N \times N\) chessboard so that no two queens threaten each other. This means no two queens can share the same row, column, or diagonal. | ||
|
||
- **Backtracking Approach**: Start by placing the first queen in the first row and recursively place queens in subsequent rows. If placing a queen in a row leads to a conflict, backtrack and try placing it in another column. | ||
|
||
- **Time Complexity**: \(O(N!)\), where \(N\) is the number of queens (or the size of the chessboard). | ||
|
||
- **Use Case**: Resource allocation and optimization problems, where multiple entities must be placed in non-conflicting positions (e.g., server load balancing). | ||
|
||
--- | ||
|
||
## Key Differences Between Backtracking Applications: | ||
|
||
| Problem | Time Complexity | Use Case | | ||
|---------------------|-----------------|-------------------------------------------| | ||
| **Graph Coloring** | \(O(m^n)\) | Scheduling, Timetabling | | ||
| **Hamiltonian Cycle**| \(O(n!)\) | Circuit design, Optimization | | ||
| **Knight's Tour** | \(O(8^n)\) | Chess puzzle solvers, Pathfinding | | ||
| **Maze Solving** | \(O(4^n)\) | Robotics, Navigation Systems | | ||
| **N-Queens** | \(O(N!)\) | Resource allocation, Server optimization | | ||
|
||
--- | ||
|
||
## Conclusion | ||
|
||
**Backtracking** is a versatile and powerful technique for solving constraint-based problems. By exploring all possibilities and eliminating invalid paths through backtracking, this approach enables the efficient solving of complex combinatorial problems. Applications like **Graph Coloring**, **Hamiltonian Cycle**, **Knight's Tour**, **Maze Solving**, and the **N-Queens Problem** showcase the wide applicability of backtracking, from puzzle-solving to real-world optimization tasks. | ||
|
||
Mastering backtracking is essential for understanding and solving a range of computational problems, making it a critical tool in algorithmic design. | ||
|
||
--- |
64 changes: 64 additions & 0 deletions
64
...thms_and_Data_Structures/Design_and_Analysis_of_Algorithms/Backtracking/graph_coloring.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Number of vertices in the graph | ||
V = 4 | ||
|
||
def print_solution(color): | ||
# Print the solution if it exists | ||
print("Solution Exists: Following are the assigned colors") | ||
print(" ".join(map(str, color))) # Print colors assigned to each vertex | ||
|
||
def is_safe(v, graph, color, c): | ||
# Check if it is safe to assign color c to vertex v | ||
for i in range(V): | ||
# If there is an edge between v and i, and i has the same color, return False | ||
if graph[v][i] and c == color[i]: | ||
return False | ||
return True # Color assignment is safe | ||
|
||
def graph_coloring_util(graph, m, color, v): | ||
# Base case: If all vertices are assigned a color | ||
if v == V: | ||
return True | ||
|
||
# Try different colors for vertex v | ||
for c in range(1, m + 1): | ||
# Check if assigning color c to vertex v is safe | ||
if is_safe(v, graph, color, c): | ||
color[v] = c # Assign color c to vertex v | ||
|
||
# Recur to assign colors to the next vertex | ||
if graph_coloring_util(graph, m, color, v + 1): | ||
return True # If successful, return True | ||
|
||
color[v] = 0 # Backtrack: remove color c from vertex v | ||
|
||
return False # If no color can be assigned, return False | ||
|
||
def graph_coloring(graph, m): | ||
# Initialize color assignment for vertices | ||
color = [0] * V | ||
|
||
# Start graph coloring utility function | ||
if not graph_coloring_util(graph, m, color, 0): | ||
print("Solution does not exist") # If no solution exists | ||
return False | ||
|
||
print_solution(color) # Print the colors assigned to vertices | ||
return True # Solution found | ||
|
||
def main(): | ||
print("Enter the number of vertices:") | ||
global V # Declare V as global to modify it | ||
V = int(input()) # Read the number of vertices from user | ||
|
||
graph = [] # Initialize an empty list for the adjacency matrix | ||
print("Enter the adjacency matrix (0 for no edge, 1 for edge):") | ||
for _ in range(V): | ||
row = list(map(int, input().split())) # Read each row of the adjacency matrix | ||
graph.append(row) # Append the row to the graph | ||
|
||
m = int(input("Enter the number of colors: ")) # Read the number of colors from user | ||
|
||
graph_coloring(graph, m) # Call the graph coloring function | ||
|
||
if __name__ == "__main__": | ||
main() # Run the main function |
Oops, something went wrong.