Skip to content

Commit

Permalink
Merge branch 'main' into C#-classes-static
Browse files Browse the repository at this point in the history
  • Loading branch information
Russellzj authored Oct 13, 2024
2 parents eaae2ec + 56aa4f1 commit 962b4f8
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 11 deletions.
2 changes: 1 addition & 1 deletion bin/concept-of-the-week.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
content/uiux/concepts/logo/logo.md
content/java/concepts/priorityqueue/priorityqueue.md
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
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ If possible, the `ndarray` returned will be a view of the original `ndarray`'s d

## Example

The below example creates an `ndarray` and then uses `.transpose()` on it.
The below example creates an `ndarray` and then uses `.transpose()` on it:

```py
import numpy as np
Expand All @@ -50,3 +50,16 @@ This produces the following output:
[2 5]
[3 6]]
```

## Codebyte Example

The `axes` argument allows control over the specific reordering of dimensions in a tensor. Run the below codebyte example to see how `.transpose()` can be used to rearrange the three dimensions of a 3D array:

```codebyte/python
import numpy as np
array_3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print(array_3d)
print(np.transpose(array_3d))
```
10 changes: 5 additions & 5 deletions content/python/concepts/built-in-functions/terms/hash/hash.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ CatalogContent:
- 'paths/computer-science'
---

The built-in **`hash()`** function returns the hash value of an object as a fixed sized integer. The object can be of various types, including numbers, strings, tuples, or custom objects that have implemented the `__hash__()` method. The hash value is computed using a specific hashing algorithm based on the object's type. It's worth noting that the `hash()` function is a built-in function in Python and doesn't require any import statements. Hash values are useful for efficient comparison of dictionary keys during lookups.
The built-in **`hash()`** function returns a fixed-size integer hash value for an object, such as numbers, strings, tuples, or custom objects implementing the `__hash__()` method. Using type-specific algorithms, it facilitates efficient dictionary key comparisons during lookups. It can be used directly without requiring any imports.

## Syntax

Expand All @@ -28,11 +28,11 @@ The `object` can be of any hashable type, such as numbers, strings, tuples, or c

## Example

The example below begins by defining a class called `MyClass` with an attribute called value. The `__hash__()` method is implemented to customize the hashing behavior based on the value attribute using the `hash()` function.
The example below defines a class called `MyClass`, with an attribute called `value`. The `__hash__()` method is implemented to customize the hashing behavior based on the `value` attribute.

Two instances of `MyClass` `obj1` and `obj2`, are created with different values. The `hash()` function is used to calculate the hash values of these objects. And these values are then printed to the console.
Two instances of `MyClass`, `obj1` and `obj2`, are created with different values. The `hash()` function is used to calculate the hash values of these objects. These values are then printed to the console.

This example demonstrates how to customize the hash function for a class using the `__hash__()` method. The `hash()` function allows us to obtain the hash value of an object, which is an integer used for quick comparison and dictionary key lookups.
This example demonstrates how to customize the hash function for a custom class using the `__hash__()` method.

```py
# Define a class
Expand All @@ -54,7 +54,7 @@ print(hash(obj2))

## Codebyte Example

In the example below, we define `my_tuple` as "1, 2, 3". Subsequently, we use the `hash()` function to obtain the hash value of the input `my_tuple`. Followed by a call to print the output of the `hash()` function.
In the example below, we define `my_tuple` as `(1, 2, 3)`. Subsequently, we use the `hash()` function to obtain the hash value of the input `my_tuple`. Then, we print the output of the `hash()` function.

```codebyte/python
my_tuple = (1, 2, 3)
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 962b4f8

Please sign in to comment.