Skip to content

Commit

Permalink
Merge branch 'main' into feat(docs)/Update-Python-Hash
Browse files Browse the repository at this point in the history
  • Loading branch information
SaviDahegaonkar authored Oct 13, 2024
2 parents 3721384 + c3e9970 commit edac8e5
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,112 @@ Exploring **C**:

![a-star-6](https://raw.githubusercontent.com/Codecademy/docs/main/media/a-star-tree-6.png)

The next node in the open list is again **B**. However, because **B** has already been explored, meaning a shortest path to **B** has been found, it is not explored again and the algorithm continues to the next candidate.
The next node in the open list is again **B**. However, because **B** has already been explored, meaning the shortest path to **B** has been found, it is not explored again, and the algorithm continues to the next candidate.

![a-star-7](https://raw.githubusercontent.com/Codecademy/docs/main/media/a-star-tree-7.png)

The next node to be explored is the goal node **G**, meaning the shortest path to **G** has been found! The path is constructed by tracing the graph backward from **G** to **S**:
The next node to be explored is the goal node **G**, meaning the shortest path to **G** has been found! The path is constructed by tracing the graph backwards from **G** to **S**:

![a-star-8](https://raw.githubusercontent.com/Codecademy/docs/main/media/a-star-tree-8.png)

## Using the A\* Algorithm

This algorithm is guaranteed to find a shortest path if one exists. One of the main uses of this algorithm is route planning. However, there are many other uses.
This algorithm is guaranteed to find the shortest path if one exists. One of the main uses of this algorithm is route planning, but there are many other uses.

## Example Code

Here is an example of the A\* algorithm implemented in Python that solves the above example graph:

```py
from heapq import heappop, heappush

def a_star_search(graph: dict, start: str, goal: str, heuristic_values: dict) -> int:
'''
A* search algorithm implementation.
@param graph: The graph to search.
@param start: The starting node.
@param goal: The goal node.
@param heuristic_values: The heuristic values for each node. The goal node must be admissible, and the heuristic value must be 0.
@return: The path cost from the start node to the goal node.
'''

# A min heap is used to implement the priority queue for the open list.
# The heapq module from Python's standard library is utilized.
# Entries in the heap are tuples of the form (cost, node), ensuring that the entry with the lowest cost is always smaller during comparisons.
# The heapify operation is not required, as the heapq module maintains the heap invariant after every push and pop operation.

# The closed list is implemented as a set for efficient membership checking.

open_list, closed_list = [(heuristic_values[start], start)], set()

while open_list:
cost, node = heappop(open_list)

# The algorithm ends when the goal node has been explored, NOT when it is added to the open list.
if node == goal:
return cost

if node in closed_list:
continue

closed_list.add(node)

# Subtract the heuristic value as it was overcounted.
cost -= heuristic_values[node]

for neighbor, edge_cost in graph[node]:
if neighbor in closed_list:
continue

# f(x) = g(x) + h(x), where g(x) is the path cost and h(x) is the heuristic.
neighbor_cost = cost + edge_cost + heuristic_values[neighbor]
heappush(open_list, (neighbor_cost, neighbor))

return -1 # No path found

EXAMPLE_GRAPH = {
'S': [('A', 4), ('B', 10), ('C', 11)],
'A': [('B', 8), ('D', 5)],
'B': [('D', 15)],
'C': [('D', 8), ('E', 20), ('F', 2)],
'D': [('F', 1), ('I', 20), ('H', 16)],
'E': [('G', 19)],
'F': [('G', 13)],
'H': [('J', 2), ('I', 1)],
'I': [('K', 13), ('G', 5), ('J', 5)],
'J': [('K', 7)],
'K': [('G', 16)]
}

# Node heuristic values (admissible heuristic values for the nodes)
EXAMPLE_HEURISTIC_VALUES = {
'S': 7,
'A': 8,
'B': 6,
'C': 5,
'D': 5,
'E': 3,
'F': 3,
'G': 0,
'H': 7,
'I': 4,
'J': 5,
'K': 3
}

EXAMPLE_RESULT = a_star_search(EXAMPLE_GRAPH, 'S', 'G', EXAMPLE_HEURISTIC_VALUES)
print(EXAMPLE_RESULT)
```

The code above produces the following output:

```shell
23
```

## Complexity Analysis

For time complexity, one might notice that each heappush corresponds to an edge, which would be the dominating complexity for most cases. Indeed, A\* is equivalent to Dijkstra's algorithm when the heuristic function is 0, and A\* is equivalent to Dijkstra's algorithm with reduced cost when the heuristic function is admissible i.e. `O(V+Elog(V))` time complexity.

However, a good heuristic function can drastically decrease A*'s complexity. The idea here is we need to look at exponentially fewer nodes with a better heuristic. So the time and space complexity are actually `O(b1^d)`, where b1 is the effective branching factor, i.e., an empirical average of neighboring nodes not in the closed list, and d is the search depth, i.e., optimal path length. The large space complexity is the biggest disadvantage of the A* search, giving rise to other algorithm variations.
2 changes: 1 addition & 1 deletion content/javascript/concepts/this/this.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
Title: 'this'
Description: 'In JavaScript, the this keyword can have several meanings depending on the execution context. Most often it is used within a method of an object to return the instance of the object whose function is being executed, but what this returns can vary depending on the context.'
Description: 'It is often used within an object method, but what it refers to will vary depending on the execution context.'
Subjects:
- 'Web Development'
- 'Computer Science'
Expand Down
48 changes: 48 additions & 0 deletions content/pytorch/concepts/tensors/terms/size/size.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
Title: '.size()'
Description: 'Returns the size of the self tensor as a tuple of integers.'
Subjects:
- 'Data Science'
- 'Machine Learning'
Tags:
- 'AI'
- 'Data Structures'
- 'Deep Learning'
- 'Methods'
CatalogContent:
- 'learn-python-3'
- 'paths/computer-science'
---

The **`size()`** method in PyTorch returns a `torch.Size` object containing the size (shape) information of a tensor. It serves as a fundamental function for dynamically obtaining the tensor's shape during operations. Specific dimensions can be accessed by indexing into the `torch.Size` object, which functions like a tuple.

## Syntax

```pseudo
tensor.size(dim=None)
```

- `tensor`: The PyTorch tensor on which the `.size()` method is called.
- `dim` (Optional): Specifies the dimension for which to retrieve the size. The default value is `None`.
- If `dim` is not provided, the returned value is a `torch.Size` object representing the size of all dimensions.
- If `dim` is specified, the returned value is an `int` representing the size of the given dimension.

## Example

The following example shows how to use the `.size()` method:

```py
import torch

t = torch.empty(3, 4, 5)

print(t.size())
print(t.size(dim=1))
```

The code above generates the following output:

```shell
torch.Size([3, 4, 5])
4
```

0 comments on commit edac8e5

Please sign in to comment.