resolve merge

This commit is contained in:
imengus 2023-08-15 13:20:56 +01:00
commit 3e3955530d
89 changed files with 2331 additions and 928 deletions

8
.devcontainer/Dockerfile Normal file
View File

@ -0,0 +1,8 @@
# https://github.com/microsoft/vscode-dev-containers/blob/main/containers/python-3/README.md
ARG VARIANT=3.11-bookworm
FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT}
COPY requirements.txt /tmp/pip-tmp/
RUN python3 -m pip install --upgrade pip \
&& python3 -m pip install --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
&& pipx install pre-commit ruff \
&& pre-commit install

View File

@ -0,0 +1,42 @@
{
"name": "Python 3",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
"args": {
// Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6
// Append -bullseye or -buster to pin to an OS version.
// Use -bullseye variants on local on arm64/Apple Silicon.
"VARIANT": "3.11-bookworm",
}
},
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
// Set *default* container specific settings.json values on container create.
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python",
"python.linting.enabled": true,
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance"
]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "pip3 install --user -r requirements.txt",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
}

View File

@ -16,12 +16,12 @@ repos:
- id: auto-walrus - id: auto-walrus
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.275 rev: v0.0.284
hooks: hooks:
- id: ruff - id: ruff
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 23.3.0 rev: 23.7.0
hooks: hooks:
- id: black - id: black
@ -33,7 +33,7 @@ repos:
- tomli - tomli
- repo: https://github.com/tox-dev/pyproject-fmt - repo: https://github.com/tox-dev/pyproject-fmt
rev: "0.12.1" rev: "0.13.1"
hooks: hooks:
- id: pyproject-fmt - id: pyproject-fmt
@ -51,7 +51,7 @@ repos:
- id: validate-pyproject - id: validate-pyproject
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.4.1 rev: v1.5.0
hooks: hooks:
- id: mypy - id: mypy
args: args:

View File

@ -25,6 +25,8 @@ We appreciate any contribution, from fixing a grammar mistake in a comment to im
Your contribution will be tested by our [automated testing on GitHub Actions](https://github.com/TheAlgorithms/Python/actions) to save time and mental energy. After you have submitted your pull request, you should see the GitHub Actions tests start to run at the bottom of your submission page. If those tests fail, then click on the ___details___ button try to read through the GitHub Actions output to understand the failure. If you do not understand, please leave a comment on your submission page and a community member will try to help. Your contribution will be tested by our [automated testing on GitHub Actions](https://github.com/TheAlgorithms/Python/actions) to save time and mental energy. After you have submitted your pull request, you should see the GitHub Actions tests start to run at the bottom of your submission page. If those tests fail, then click on the ___details___ button try to read through the GitHub Actions output to understand the failure. If you do not understand, please leave a comment on your submission page and a community member will try to help.
If you are interested in resolving an [open issue](https://github.com/TheAlgorithms/Python/issues), simply make a pull request with your proposed fix. __We do not assign issues in this repo__ so please do not ask for permission to work on an issue.
Please help us keep our issue list small by adding `Fixes #{$ISSUE_NUMBER}` to the description of pull requests that resolve open issues. Please help us keep our issue list small by adding `Fixes #{$ISSUE_NUMBER}` to the description of pull requests that resolve open issues.
For example, if your pull request fixes issue #10, then please add the following to its description: For example, if your pull request fixes issue #10, then please add the following to its description:
``` ```

View File

@ -74,6 +74,7 @@
* [Game Of Life](cellular_automata/game_of_life.py) * [Game Of Life](cellular_automata/game_of_life.py)
* [Nagel Schrekenberg](cellular_automata/nagel_schrekenberg.py) * [Nagel Schrekenberg](cellular_automata/nagel_schrekenberg.py)
* [One Dimensional](cellular_automata/one_dimensional.py) * [One Dimensional](cellular_automata/one_dimensional.py)
* [Wa Tor](cellular_automata/wa_tor.py)
## Ciphers ## Ciphers
* [A1Z26](ciphers/a1z26.py) * [A1Z26](ciphers/a1z26.py)
@ -236,8 +237,8 @@
* [Double Ended Queue](data_structures/queue/double_ended_queue.py) * [Double Ended Queue](data_structures/queue/double_ended_queue.py)
* [Linked Queue](data_structures/queue/linked_queue.py) * [Linked Queue](data_structures/queue/linked_queue.py)
* [Priority Queue Using List](data_structures/queue/priority_queue_using_list.py) * [Priority Queue Using List](data_structures/queue/priority_queue_using_list.py)
* [Queue By List](data_structures/queue/queue_by_list.py)
* [Queue By Two Stacks](data_structures/queue/queue_by_two_stacks.py) * [Queue By Two Stacks](data_structures/queue/queue_by_two_stacks.py)
* [Queue On List](data_structures/queue/queue_on_list.py)
* [Queue On Pseudo Stack](data_structures/queue/queue_on_pseudo_stack.py) * [Queue On Pseudo Stack](data_structures/queue/queue_on_pseudo_stack.py)
* Stacks * Stacks
* [Balanced Parentheses](data_structures/stacks/balanced_parentheses.py) * [Balanced Parentheses](data_structures/stacks/balanced_parentheses.py)
@ -293,7 +294,7 @@
* [Inversions](divide_and_conquer/inversions.py) * [Inversions](divide_and_conquer/inversions.py)
* [Kth Order Statistic](divide_and_conquer/kth_order_statistic.py) * [Kth Order Statistic](divide_and_conquer/kth_order_statistic.py)
* [Max Difference Pair](divide_and_conquer/max_difference_pair.py) * [Max Difference Pair](divide_and_conquer/max_difference_pair.py)
* [Max Subarray Sum](divide_and_conquer/max_subarray_sum.py) * [Max Subarray](divide_and_conquer/max_subarray.py)
* [Mergesort](divide_and_conquer/mergesort.py) * [Mergesort](divide_and_conquer/mergesort.py)
* [Peak](divide_and_conquer/peak.py) * [Peak](divide_and_conquer/peak.py)
* [Power](divide_and_conquer/power.py) * [Power](divide_and_conquer/power.py)
@ -324,8 +325,7 @@
* [Matrix Chain Order](dynamic_programming/matrix_chain_order.py) * [Matrix Chain Order](dynamic_programming/matrix_chain_order.py)
* [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py) * [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py)
* [Max Product Subarray](dynamic_programming/max_product_subarray.py) * [Max Product Subarray](dynamic_programming/max_product_subarray.py)
* [Max Sub Array](dynamic_programming/max_sub_array.py) * [Max Subarray Sum](dynamic_programming/max_subarray_sum.py)
* [Max Sum Contiguous Subsequence](dynamic_programming/max_sum_contiguous_subsequence.py)
* [Min Distance Up Bottom](dynamic_programming/min_distance_up_bottom.py) * [Min Distance Up Bottom](dynamic_programming/min_distance_up_bottom.py)
* [Minimum Coin Change](dynamic_programming/minimum_coin_change.py) * [Minimum Coin Change](dynamic_programming/minimum_coin_change.py)
* [Minimum Cost Path](dynamic_programming/minimum_cost_path.py) * [Minimum Cost Path](dynamic_programming/minimum_cost_path.py)
@ -336,9 +336,11 @@
* [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py) * [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py)
* [Optimal Binary Search Tree](dynamic_programming/optimal_binary_search_tree.py) * [Optimal Binary Search Tree](dynamic_programming/optimal_binary_search_tree.py)
* [Palindrome Partitioning](dynamic_programming/palindrome_partitioning.py) * [Palindrome Partitioning](dynamic_programming/palindrome_partitioning.py)
* [Regex Match](dynamic_programming/regex_match.py)
* [Rod Cutting](dynamic_programming/rod_cutting.py) * [Rod Cutting](dynamic_programming/rod_cutting.py)
* [Subset Generation](dynamic_programming/subset_generation.py) * [Subset Generation](dynamic_programming/subset_generation.py)
* [Sum Of Subset](dynamic_programming/sum_of_subset.py) * [Sum Of Subset](dynamic_programming/sum_of_subset.py)
* [Tribonacci](dynamic_programming/tribonacci.py)
* [Viterbi](dynamic_programming/viterbi.py) * [Viterbi](dynamic_programming/viterbi.py)
* [Word Break](dynamic_programming/word_break.py) * [Word Break](dynamic_programming/word_break.py)
@ -512,7 +514,7 @@
* Lstm * Lstm
* [Lstm Prediction](machine_learning/lstm/lstm_prediction.py) * [Lstm Prediction](machine_learning/lstm/lstm_prediction.py)
* [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py)
* [Polymonial Regression](machine_learning/polymonial_regression.py) * [Polynomial Regression](machine_learning/polynomial_regression.py)
* [Scoring Functions](machine_learning/scoring_functions.py) * [Scoring Functions](machine_learning/scoring_functions.py)
* [Self Organizing Map](machine_learning/self_organizing_map.py) * [Self Organizing Map](machine_learning/self_organizing_map.py)
* [Sequential Minimum Optimization](machine_learning/sequential_minimum_optimization.py) * [Sequential Minimum Optimization](machine_learning/sequential_minimum_optimization.py)
@ -571,9 +573,7 @@
* [Fermat Little Theorem](maths/fermat_little_theorem.py) * [Fermat Little Theorem](maths/fermat_little_theorem.py)
* [Fibonacci](maths/fibonacci.py) * [Fibonacci](maths/fibonacci.py)
* [Find Max](maths/find_max.py) * [Find Max](maths/find_max.py)
* [Find Max Recursion](maths/find_max_recursion.py)
* [Find Min](maths/find_min.py) * [Find Min](maths/find_min.py)
* [Find Min Recursion](maths/find_min_recursion.py)
* [Floor](maths/floor.py) * [Floor](maths/floor.py)
* [Gamma](maths/gamma.py) * [Gamma](maths/gamma.py)
* [Gamma Recursive](maths/gamma_recursive.py) * [Gamma Recursive](maths/gamma_recursive.py)
@ -586,17 +586,16 @@
* [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py)
* [Hexagonal Number](maths/hexagonal_number.py) * [Hexagonal Number](maths/hexagonal_number.py)
* [Integration By Simpson Approx](maths/integration_by_simpson_approx.py) * [Integration By Simpson Approx](maths/integration_by_simpson_approx.py)
* [Interquartile Range](maths/interquartile_range.py)
* [Is Int Palindrome](maths/is_int_palindrome.py) * [Is Int Palindrome](maths/is_int_palindrome.py)
* [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py) * [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py)
* [Is Square Free](maths/is_square_free.py) * [Is Square Free](maths/is_square_free.py)
* [Jaccard Similarity](maths/jaccard_similarity.py) * [Jaccard Similarity](maths/jaccard_similarity.py)
* [Juggler Sequence](maths/juggler_sequence.py) * [Juggler Sequence](maths/juggler_sequence.py)
* [Kadanes](maths/kadanes.py)
* [Karatsuba](maths/karatsuba.py) * [Karatsuba](maths/karatsuba.py)
* [Krishnamurthy Number](maths/krishnamurthy_number.py) * [Krishnamurthy Number](maths/krishnamurthy_number.py)
* [Kth Lexicographic Permutation](maths/kth_lexicographic_permutation.py) * [Kth Lexicographic Permutation](maths/kth_lexicographic_permutation.py)
* [Largest Of Very Large Numbers](maths/largest_of_very_large_numbers.py) * [Largest Of Very Large Numbers](maths/largest_of_very_large_numbers.py)
* [Largest Subarray Sum](maths/largest_subarray_sum.py)
* [Least Common Multiple](maths/least_common_multiple.py) * [Least Common Multiple](maths/least_common_multiple.py)
* [Line Length](maths/line_length.py) * [Line Length](maths/line_length.py)
* [Liouville Lambda](maths/liouville_lambda.py) * [Liouville Lambda](maths/liouville_lambda.py)
@ -712,7 +711,6 @@
* [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py) * [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py)
* [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py) * [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py)
* [Convolution Neural Network](neural_network/convolution_neural_network.py) * [Convolution Neural Network](neural_network/convolution_neural_network.py)
* [Input Data](neural_network/input_data.py)
* [Perceptron](neural_network/perceptron.py) * [Perceptron](neural_network/perceptron.py)
* [Simple Neural Network](neural_network/simple_neural_network.py) * [Simple Neural Network](neural_network/simple_neural_network.py)
@ -733,7 +731,6 @@
* [Linear Congruential Generator](other/linear_congruential_generator.py) * [Linear Congruential Generator](other/linear_congruential_generator.py)
* [Lru Cache](other/lru_cache.py) * [Lru Cache](other/lru_cache.py)
* [Magicdiamondpattern](other/magicdiamondpattern.py) * [Magicdiamondpattern](other/magicdiamondpattern.py)
* [Maximum Subarray](other/maximum_subarray.py)
* [Maximum Subsequence](other/maximum_subsequence.py) * [Maximum Subsequence](other/maximum_subsequence.py)
* [Nested Brackets](other/nested_brackets.py) * [Nested Brackets](other/nested_brackets.py)
* [Number Container System](other/number_container_system.py) * [Number Container System](other/number_container_system.py)
@ -744,7 +741,9 @@
* [Tower Of Hanoi](other/tower_of_hanoi.py) * [Tower Of Hanoi](other/tower_of_hanoi.py)
## Physics ## Physics
* [Altitude Pressure](physics/altitude_pressure.py)
* [Archimedes Principle](physics/archimedes_principle.py) * [Archimedes Principle](physics/archimedes_principle.py)
* [Basic Orbital Capture](physics/basic_orbital_capture.py)
* [Casimir Effect](physics/casimir_effect.py) * [Casimir Effect](physics/casimir_effect.py)
* [Centripetal Force](physics/centripetal_force.py) * [Centripetal Force](physics/centripetal_force.py)
* [Grahams Law](physics/grahams_law.py) * [Grahams Law](physics/grahams_law.py)
@ -1066,7 +1065,6 @@
* [Q Fourier Transform](quantum/q_fourier_transform.py) * [Q Fourier Transform](quantum/q_fourier_transform.py)
* [Q Full Adder](quantum/q_full_adder.py) * [Q Full Adder](quantum/q_full_adder.py)
* [Quantum Entanglement](quantum/quantum_entanglement.py) * [Quantum Entanglement](quantum/quantum_entanglement.py)
* [Quantum Random](quantum/quantum_random.py)
* [Quantum Teleportation](quantum/quantum_teleportation.py) * [Quantum Teleportation](quantum/quantum_teleportation.py)
* [Ripple Adder Classic](quantum/ripple_adder_classic.py) * [Ripple Adder Classic](quantum/ripple_adder_classic.py)
* [Single Qubit Measure](quantum/single_qubit_measure.py) * [Single Qubit Measure](quantum/single_qubit_measure.py)
@ -1172,6 +1170,7 @@
* [Is Pangram](strings/is_pangram.py) * [Is Pangram](strings/is_pangram.py)
* [Is Spain National Id](strings/is_spain_national_id.py) * [Is Spain National Id](strings/is_spain_national_id.py)
* [Is Srilankan Phone Number](strings/is_srilankan_phone_number.py) * [Is Srilankan Phone Number](strings/is_srilankan_phone_number.py)
* [Is Valid Email Address](strings/is_valid_email_address.py)
* [Jaro Winkler](strings/jaro_winkler.py) * [Jaro Winkler](strings/jaro_winkler.py)
* [Join](strings/join.py) * [Join](strings/join.py)
* [Knuth Morris Pratt](strings/knuth_morris_pratt.py) * [Knuth Morris Pratt](strings/knuth_morris_pratt.py)

View File

@ -13,7 +13,7 @@
<img src="https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3&style=flat-square" height="20" alt="Contributions Welcome"> <img src="https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3&style=flat-square" height="20" alt="Contributions Welcome">
</a> </a>
<img src="https://img.shields.io/github/repo-size/TheAlgorithms/Python.svg?label=Repo%20size&style=flat-square" height="20"> <img src="https://img.shields.io/github/repo-size/TheAlgorithms/Python.svg?label=Repo%20size&style=flat-square" height="20">
<a href="https://discord.gg/c7MnfGFGa6"> <a href="https://the-algorithms.com/discord">
<img src="https://img.shields.io/discord/808045925556682782.svg?logo=discord&colorB=7289DA&style=flat-square" height="20" alt="Discord chat"> <img src="https://img.shields.io/discord/808045925556682782.svg?logo=discord&colorB=7289DA&style=flat-square" height="20" alt="Discord chat">
</a> </a>
<a href="https://gitter.im/TheAlgorithms/community"> <a href="https://gitter.im/TheAlgorithms/community">
@ -42,7 +42,7 @@ Read through our [Contribution Guidelines](CONTRIBUTING.md) before you contribut
## Community Channels ## Community Channels
We are on [Discord](https://discord.gg/c7MnfGFGa6) and [Gitter](https://gitter.im/TheAlgorithms/community)! Community channels are a great way for you to ask questions and get help. Please join us! We are on [Discord](https://the-algorithms.com/discord) and [Gitter](https://gitter.im/TheAlgorithms/community)! Community channels are a great way for you to ask questions and get help. Please join us!
## List of Algorithms ## List of Algorithms

View File

@ -25,9 +25,11 @@ def newton_raphson(
""" """
x = a x = a
while True: while True:
x = Decimal(x) - (Decimal(eval(func)) / Decimal(eval(str(diff(func))))) x = Decimal(x) - (
Decimal(eval(func)) / Decimal(eval(str(diff(func)))) # noqa: S307
)
# This number dictates the accuracy of the answer # This number dictates the accuracy of the answer
if abs(eval(func)) < precision: if abs(eval(func)) < precision: # noqa: S307
return float(x) return float(x)

View File

@ -10,7 +10,7 @@ Python:
- 3.5 - 3.5
Usage: Usage:
- $python3 game_o_life <canvas_size:int> - $python3 game_of_life <canvas_size:int>
Game-Of-Life Rules: Game-Of-Life Rules:
@ -52,7 +52,8 @@ def seed(canvas: list[list[bool]]) -> None:
def run(canvas: list[list[bool]]) -> list[list[bool]]: def run(canvas: list[list[bool]]) -> list[list[bool]]:
"""This function runs the rules of game through all points, and changes their """
This function runs the rules of game through all points, and changes their
status accordingly.(in the same canvas) status accordingly.(in the same canvas)
@Args: @Args:
-- --
@ -60,7 +61,7 @@ def run(canvas: list[list[bool]]) -> list[list[bool]]:
@returns: @returns:
-- --
None canvas of population after one step
""" """
current_canvas = np.array(canvas) current_canvas = np.array(canvas)
next_gen_canvas = np.array(create_canvas(current_canvas.shape[0])) next_gen_canvas = np.array(create_canvas(current_canvas.shape[0]))
@ -70,10 +71,7 @@ def run(canvas: list[list[bool]]) -> list[list[bool]]:
pt, current_canvas[r - 1 : r + 2, c - 1 : c + 2] pt, current_canvas[r - 1 : r + 2, c - 1 : c + 2]
) )
current_canvas = next_gen_canvas return next_gen_canvas.tolist()
del next_gen_canvas # cleaning memory as we move on.
return_canvas: list[list[bool]] = current_canvas.tolist()
return return_canvas
def __judge_point(pt: bool, neighbours: list[list[bool]]) -> bool: def __judge_point(pt: bool, neighbours: list[list[bool]]) -> bool:
@ -98,7 +96,7 @@ def __judge_point(pt: bool, neighbours: list[list[bool]]) -> bool:
if pt: if pt:
if alive < 2: if alive < 2:
state = False state = False
elif alive == 2 or alive == 3: elif alive in {2, 3}:
state = True state = True
elif alive > 3: elif alive > 3:
state = False state = False

550
cellular_automata/wa_tor.py Normal file
View File

@ -0,0 +1,550 @@
"""
Wa-Tor algorithm (1984)
@ https://en.wikipedia.org/wiki/Wa-Tor
@ https://beltoforion.de/en/wator/
@ https://beltoforion.de/en/wator/images/wator_medium.webm
This solution aims to completely remove any systematic approach
to the Wa-Tor planet, and utilise fully random methods.
The constants are a working set that allows the Wa-Tor planet
to result in one of the three possible results.
"""
from collections.abc import Callable
from random import randint, shuffle
from time import sleep
from typing import Literal
WIDTH = 50 # Width of the Wa-Tor planet
HEIGHT = 50 # Height of the Wa-Tor planet
PREY_INITIAL_COUNT = 30 # The initial number of prey entities
PREY_REPRODUCTION_TIME = 5 # The chronons before reproducing
PREDATOR_INITIAL_COUNT = 50 # The initial number of predator entities
# The initial energy value of predator entities
PREDATOR_INITIAL_ENERGY_VALUE = 15
# The energy value provided when consuming prey
PREDATOR_FOOD_VALUE = 5
PREDATOR_REPRODUCTION_TIME = 20 # The chronons before reproducing
MAX_ENTITIES = 500 # The max number of organisms on the board
# The number of entities to delete from the unbalanced side
DELETE_UNBALANCED_ENTITIES = 50
class Entity:
"""
Represents an entity (either prey or predator).
>>> e = Entity(True, coords=(0, 0))
>>> e.prey
True
>>> e.coords
(0, 0)
>>> e.alive
True
"""
def __init__(self, prey: bool, coords: tuple[int, int]) -> None:
self.prey = prey
# The (row, col) pos of the entity
self.coords = coords
self.remaining_reproduction_time = (
PREY_REPRODUCTION_TIME if prey else PREDATOR_REPRODUCTION_TIME
)
self.energy_value = None if prey is True else PREDATOR_INITIAL_ENERGY_VALUE
self.alive = True
def reset_reproduction_time(self) -> None:
"""
>>> e = Entity(True, coords=(0, 0))
>>> e.reset_reproduction_time()
>>> e.remaining_reproduction_time == PREY_REPRODUCTION_TIME
True
>>> e = Entity(False, coords=(0, 0))
>>> e.reset_reproduction_time()
>>> e.remaining_reproduction_time == PREDATOR_REPRODUCTION_TIME
True
"""
self.remaining_reproduction_time = (
PREY_REPRODUCTION_TIME if self.prey is True else PREDATOR_REPRODUCTION_TIME
)
def __repr__(self) -> str:
"""
>>> Entity(prey=True, coords=(1, 1))
Entity(prey=True, coords=(1, 1), remaining_reproduction_time=5)
>>> Entity(prey=False, coords=(2, 1)) # doctest: +NORMALIZE_WHITESPACE
Entity(prey=False, coords=(2, 1),
remaining_reproduction_time=20, energy_value=15)
"""
repr_ = (
f"Entity(prey={self.prey}, coords={self.coords}, "
f"remaining_reproduction_time={self.remaining_reproduction_time}"
)
if self.energy_value is not None:
repr_ += f", energy_value={self.energy_value}"
return f"{repr_})"
class WaTor:
"""
Represents the main Wa-Tor algorithm.
:attr time_passed: A function that is called every time
time passes (a chronon) in order to visually display
the new Wa-Tor planet. The time_passed function can block
using time.sleep to slow the algorithm progression.
>>> wt = WaTor(10, 15)
>>> wt.width
10
>>> wt.height
15
>>> len(wt.planet)
15
>>> len(wt.planet[0])
10
>>> len(wt.get_entities()) == PREDATOR_INITIAL_COUNT + PREY_INITIAL_COUNT
True
"""
time_passed: Callable[["WaTor", int], None] | None
def __init__(self, width: int, height: int) -> None:
self.width = width
self.height = height
self.time_passed = None
self.planet: list[list[Entity | None]] = [[None] * width for _ in range(height)]
# Populate planet with predators and prey randomly
for _ in range(PREY_INITIAL_COUNT):
self.add_entity(prey=True)
for _ in range(PREDATOR_INITIAL_COUNT):
self.add_entity(prey=False)
self.set_planet(self.planet)
def set_planet(self, planet: list[list[Entity | None]]) -> None:
"""
Ease of access for testing
>>> wt = WaTor(WIDTH, HEIGHT)
>>> planet = [
... [None, None, None],
... [None, Entity(True, coords=(1, 1)), None]
... ]
>>> wt.set_planet(planet)
>>> wt.planet == planet
True
>>> wt.width
3
>>> wt.height
2
"""
self.planet = planet
self.width = len(planet[0])
self.height = len(planet)
def add_entity(self, prey: bool) -> None:
"""
Adds an entity, making sure the entity does
not override another entity
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.set_planet([[None, None], [None, None]])
>>> wt.add_entity(True)
>>> len(wt.get_entities())
1
>>> wt.add_entity(False)
>>> len(wt.get_entities())
2
"""
while True:
row, col = randint(0, self.height - 1), randint(0, self.width - 1)
if self.planet[row][col] is None:
self.planet[row][col] = Entity(prey=prey, coords=(row, col))
return
def get_entities(self) -> list[Entity]:
"""
Returns a list of all the entities within the planet.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> len(wt.get_entities()) == PREDATOR_INITIAL_COUNT + PREY_INITIAL_COUNT
True
"""
return [entity for column in self.planet for entity in column if entity]
def balance_predators_and_prey(self) -> None:
"""
Balances predators and preys so that prey
can not dominate the predators, blocking up
space for them to reproduce.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> for i in range(2000):
... row, col = i // HEIGHT, i % WIDTH
... wt.planet[row][col] = Entity(True, coords=(row, col))
>>> entities = len(wt.get_entities())
>>> wt.balance_predators_and_prey()
>>> len(wt.get_entities()) == entities
False
"""
entities = self.get_entities()
shuffle(entities)
if len(entities) >= MAX_ENTITIES - MAX_ENTITIES / 10:
prey = [entity for entity in entities if entity.prey]
predators = [entity for entity in entities if not entity.prey]
prey_count, predator_count = len(prey), len(predators)
entities_to_purge = (
prey[:DELETE_UNBALANCED_ENTITIES]
if prey_count > predator_count
else predators[:DELETE_UNBALANCED_ENTITIES]
)
for entity in entities_to_purge:
self.planet[entity.coords[0]][entity.coords[1]] = None
def get_surrounding_prey(self, entity: Entity) -> list[Entity]:
"""
Returns all the prey entities around (N, S, E, W) a predator entity.
Subtly different to the try_to_move_to_unoccupied square.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.set_planet([
... [None, Entity(True, (0, 1)), None],
... [None, Entity(False, (1, 1)), None],
... [None, Entity(True, (2, 1)), None]])
>>> wt.get_surrounding_prey(
... Entity(False, (1, 1))) # doctest: +NORMALIZE_WHITESPACE
[Entity(prey=True, coords=(0, 1), remaining_reproduction_time=5),
Entity(prey=True, coords=(2, 1), remaining_reproduction_time=5)]
>>> wt.set_planet([[Entity(False, (0, 0))]])
>>> wt.get_surrounding_prey(Entity(False, (0, 0)))
[]
>>> wt.set_planet([
... [Entity(True, (0, 0)), Entity(False, (1, 0)), Entity(False, (2, 0))],
... [None, Entity(False, (1, 1)), Entity(True, (2, 1))],
... [None, None, None]])
>>> wt.get_surrounding_prey(Entity(False, (1, 0)))
[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5)]
"""
row, col = entity.coords
adjacent: list[tuple[int, int]] = [
(row - 1, col), # North
(row + 1, col), # South
(row, col - 1), # West
(row, col + 1), # East
]
return [
ent
for r, c in adjacent
if 0 <= r < self.height
and 0 <= c < self.width
and (ent := self.planet[r][c]) is not None
and ent.prey
]
def move_and_reproduce(
self, entity: Entity, direction_orders: list[Literal["N", "E", "S", "W"]]
) -> None:
"""
Attempts to move to an unoccupied neighbouring square
in either of the four directions (North, South, East, West).
If the move was successful and the remaining_reproduction time is
equal to 0, then a new prey or predator can also be created
in the previous square.
:param direction_orders: Ordered list (like priority queue) depicting
order to attempt to move. Removes any systematic
approach of checking neighbouring squares.
>>> planet = [
... [None, None, None],
... [None, Entity(True, coords=(1, 1)), None],
... [None, None, None]
... ]
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.set_planet(planet)
>>> wt.move_and_reproduce(Entity(True, coords=(1, 1)), direction_orders=["N"])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[None, Entity(prey=True, coords=(0, 1), remaining_reproduction_time=4), None],
[None, None, None],
[None, None, None]]
>>> wt.planet[0][0] = Entity(True, coords=(0, 0))
>>> wt.move_and_reproduce(Entity(True, coords=(0, 1)),
... direction_orders=["N", "W", "E", "S"])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5), None,
Entity(prey=True, coords=(0, 2), remaining_reproduction_time=4)],
[None, None, None],
[None, None, None]]
>>> wt.planet[0][1] = wt.planet[0][2]
>>> wt.planet[0][2] = None
>>> wt.move_and_reproduce(Entity(True, coords=(0, 1)),
... direction_orders=["N", "W", "S", "E"])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5), None, None],
[None, Entity(prey=True, coords=(1, 1), remaining_reproduction_time=4), None],
[None, None, None]]
>>> wt = WaTor(WIDTH, HEIGHT)
>>> reproducable_entity = Entity(False, coords=(0, 1))
>>> reproducable_entity.remaining_reproduction_time = 0
>>> wt.planet = [[None, reproducable_entity]]
>>> wt.move_and_reproduce(reproducable_entity,
... direction_orders=["N", "W", "S", "E"])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[Entity(prey=False, coords=(0, 0),
remaining_reproduction_time=20, energy_value=15),
Entity(prey=False, coords=(0, 1), remaining_reproduction_time=20,
energy_value=15)]]
"""
row, col = coords = entity.coords
adjacent_squares: dict[Literal["N", "E", "S", "W"], tuple[int, int]] = {
"N": (row - 1, col), # North
"S": (row + 1, col), # South
"W": (row, col - 1), # West
"E": (row, col + 1), # East
}
# Weight adjacent locations
adjacent: list[tuple[int, int]] = []
for order in direction_orders:
adjacent.append(adjacent_squares[order])
for r, c in adjacent:
if (
0 <= r < self.height
and 0 <= c < self.width
and self.planet[r][c] is None
):
# Move entity to empty adjacent square
self.planet[r][c] = entity
self.planet[row][col] = None
entity.coords = (r, c)
break
# (2.) See if it possible to reproduce in previous square
if coords != entity.coords and entity.remaining_reproduction_time <= 0:
# Check if the entities on the planet is less than the max limit
if len(self.get_entities()) < MAX_ENTITIES:
# Reproduce in previous square
self.planet[row][col] = Entity(prey=entity.prey, coords=coords)
entity.reset_reproduction_time()
else:
entity.remaining_reproduction_time -= 1
def perform_prey_actions(
self, entity: Entity, direction_orders: list[Literal["N", "E", "S", "W"]]
) -> None:
"""
Performs the actions for a prey entity
For prey the rules are:
1. At each chronon, a prey moves randomly to one of the adjacent unoccupied
squares. If there are no free squares, no movement takes place.
2. Once a prey has survived a certain number of chronons it may reproduce.
This is done as it moves to a neighbouring square,
leaving behind a new prey in its old position.
Its reproduction time is also reset to zero.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> reproducable_entity = Entity(True, coords=(0, 1))
>>> reproducable_entity.remaining_reproduction_time = 0
>>> wt.planet = [[None, reproducable_entity]]
>>> wt.perform_prey_actions(reproducable_entity,
... direction_orders=["N", "W", "S", "E"])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[Entity(prey=True, coords=(0, 0), remaining_reproduction_time=5),
Entity(prey=True, coords=(0, 1), remaining_reproduction_time=5)]]
"""
self.move_and_reproduce(entity, direction_orders)
def perform_predator_actions(
self,
entity: Entity,
occupied_by_prey_coords: tuple[int, int] | None,
direction_orders: list[Literal["N", "E", "S", "W"]],
) -> None:
"""
Performs the actions for a predator entity
:param occupied_by_prey_coords: Move to this location if there is prey there
For predators the rules are:
1. At each chronon, a predator moves randomly to an adjacent square occupied
by a prey. If there is none, the predator moves to a random adjacent
unoccupied square. If there are no free squares, no movement takes place.
2. At each chronon, each predator is deprived of a unit of energy.
3. Upon reaching zero energy, a predator dies.
4. If a predator moves to a square occupied by a prey,
it eats the prey and earns a certain amount of energy.
5. Once a predator has survived a certain number of chronons
it may reproduce in exactly the same way as the prey.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.set_planet([[Entity(True, coords=(0, 0)), Entity(False, coords=(0, 1))]])
>>> wt.perform_predator_actions(Entity(False, coords=(0, 1)), (0, 0), [])
>>> wt.planet # doctest: +NORMALIZE_WHITESPACE
[[Entity(prey=False, coords=(0, 0),
remaining_reproduction_time=20, energy_value=19), None]]
"""
assert entity.energy_value is not None # [type checking]
# (3.) If the entity has 0 energy, it will die
if entity.energy_value == 0:
self.planet[entity.coords[0]][entity.coords[1]] = None
return
# (1.) Move to entity if possible
if occupied_by_prey_coords is not None:
# Kill the prey
prey = self.planet[occupied_by_prey_coords[0]][occupied_by_prey_coords[1]]
assert prey is not None
prey.alive = False
# Move onto prey
self.planet[occupied_by_prey_coords[0]][occupied_by_prey_coords[1]] = entity
self.planet[entity.coords[0]][entity.coords[1]] = None
entity.coords = occupied_by_prey_coords
# (4.) Eats the prey and earns energy
entity.energy_value += PREDATOR_FOOD_VALUE
else:
# (5.) If it has survived the certain number of chronons it will also
# reproduce in this function
self.move_and_reproduce(entity, direction_orders)
# (2.) Each chronon, the predator is deprived of a unit of energy
entity.energy_value -= 1
def run(self, *, iteration_count: int) -> None:
"""
Emulate time passing by looping iteration_count times
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.run(iteration_count=PREDATOR_INITIAL_ENERGY_VALUE - 1)
>>> len(list(filter(lambda entity: entity.prey is False,
... wt.get_entities()))) >= PREDATOR_INITIAL_COUNT
True
"""
for iter_num in range(iteration_count):
# Generate list of all entities in order to randomly
# pop an entity at a time to simulate true randomness
# This removes the systematic approach of iterating
# through each entity width by height
all_entities = self.get_entities()
for __ in range(len(all_entities)):
entity = all_entities.pop(randint(0, len(all_entities) - 1))
if entity.alive is False:
continue
directions: list[Literal["N", "E", "S", "W"]] = ["N", "E", "S", "W"]
shuffle(directions) # Randomly shuffle directions
if entity.prey:
self.perform_prey_actions(entity, directions)
else:
# Create list of surrounding prey
surrounding_prey = self.get_surrounding_prey(entity)
surrounding_prey_coords = None
if surrounding_prey:
# Again, randomly shuffle directions
shuffle(surrounding_prey)
surrounding_prey_coords = surrounding_prey[0].coords
self.perform_predator_actions(
entity, surrounding_prey_coords, directions
)
# Balance out the predators and prey
self.balance_predators_and_prey()
if self.time_passed is not None:
# Call time_passed function for Wa-Tor planet
# visualisation in a terminal or a graph.
self.time_passed(self, iter_num)
def visualise(wt: WaTor, iter_number: int, *, colour: bool = True) -> None:
"""
Visually displays the Wa-Tor planet using
an ascii code in terminal to clear and re-print
the Wa-Tor planet at intervals.
Uses ascii colour codes to colourfully display
the predators and prey.
(0x60f197) Prey = #
(0xfffff) Predator = x
>>> wt = WaTor(30, 30)
>>> wt.set_planet([
... [Entity(True, coords=(0, 0)), Entity(False, coords=(0, 1)), None],
... [Entity(False, coords=(1, 0)), None, Entity(False, coords=(1, 2))],
... [None, Entity(True, coords=(2, 1)), None]
... ])
>>> visualise(wt, 0, colour=False) # doctest: +NORMALIZE_WHITESPACE
# x .
x . x
. # .
<BLANKLINE>
Iteration: 0 | Prey count: 2 | Predator count: 3 |
"""
if colour:
__import__("os").system("")
print("\x1b[0;0H\x1b[2J\x1b[?25l")
reprint = "\x1b[0;0H" if colour else ""
ansi_colour_end = "\x1b[0m " if colour else " "
planet = wt.planet
output = ""
# Iterate over every entity in the planet
for row in planet:
for entity in row:
if entity is None:
output += " . "
else:
if colour is True:
output += (
"\x1b[38;2;96;241;151m"
if entity.prey
else "\x1b[38;2;255;255;15m"
)
output += f" {'#' if entity.prey else 'x'}{ansi_colour_end}"
output += "\n"
entities = wt.get_entities()
prey_count = sum(entity.prey for entity in entities)
print(
f"{output}\n Iteration: {iter_number} | Prey count: {prey_count} | "
f"Predator count: {len(entities) - prey_count} | {reprint}"
)
# Block the thread to be able to visualise seeing the algorithm
sleep(0.05)
if __name__ == "__main__":
import doctest
doctest.testmod()
wt = WaTor(WIDTH, HEIGHT)
wt.time_passed = visualise
wt.run(iteration_count=100_000)

View File

@ -10,13 +10,13 @@ primes = {
5: { 5: {
"prime": int( "prime": int(
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF",
base=16, base=16,
), ),
"generator": 2, "generator": 2,
@ -25,16 +25,16 @@ primes = {
14: { 14: {
"prime": int( "prime": int(
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ "15728E5A8AACAA68FFFFFFFFFFFFFFFF", "15728E5A8AACAA68FFFFFFFFFFFFFFFF",
base=16, base=16,
), ),
"generator": 2, "generator": 2,
@ -43,21 +43,21 @@ primes = {
15: { 15: {
"prime": int( "prime": int(
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
+ "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF", "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF",
base=16, base=16,
), ),
"generator": 2, "generator": 2,
@ -66,27 +66,27 @@ primes = {
16: { 16: {
"prime": int( "prime": int(
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
+ "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7"
+ "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6"
+ "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9"
+ "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
+ "FFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFF",
base=16, base=16,
), ),
"generator": 2, "generator": 2,
@ -95,33 +95,33 @@ primes = {
17: { 17: {
"prime": int( "prime": int(
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+ "6DCC4024FFFFFFFFFFFFFFFF", "6DCC4024FFFFFFFFFFFFFFFF",
base=16, base=16,
), ),
"generator": 2, "generator": 2,
@ -130,48 +130,48 @@ primes = {
18: { 18: {
"prime": int( "prime": int(
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
+ "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7"
+ "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6"
+ "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9"
+ "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD"
+ "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831"
+ "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B"
+ "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF"
+ "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6"
+ "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3"
+ "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328"
+ "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C"
+ "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE"
+ "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4"
+ "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300"
+ "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568"
+ "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+ "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B"
+ "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A"
+ "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36"
+ "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1"
+ "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92"
+ "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47"
+ "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71"
+ "60C980DD98EDD3DFFFFFFFFFFFFFFFFF", "60C980DD98EDD3DFFFFFFFFFFFFFFFFF",
base=16, base=16,
), ),
"generator": 2, "generator": 2,

View File

@ -150,7 +150,7 @@ def reverse_bwt(bwt_string: str, idx_original_string: int) -> str:
raise ValueError("The parameter idx_original_string must not be lower than 0.") raise ValueError("The parameter idx_original_string must not be lower than 0.")
if idx_original_string >= len(bwt_string): if idx_original_string >= len(bwt_string):
raise ValueError( raise ValueError(
"The parameter idx_original_string must be lower than" " len(bwt_string)." "The parameter idx_original_string must be lower than len(bwt_string)."
) )
ordered_rotations = [""] * len(bwt_string) ordered_rotations = [""] * len(bwt_string)

View File

@ -22,9 +22,13 @@ REFERENCES :
-> Wikipedia reference: https://en.wikipedia.org/wiki/Millimeter -> Wikipedia reference: https://en.wikipedia.org/wiki/Millimeter
""" """
from collections import namedtuple from typing import NamedTuple
class FromTo(NamedTuple):
from_factor: float
to_factor: float
from_to = namedtuple("from_to", "from_ to")
TYPE_CONVERSION = { TYPE_CONVERSION = {
"millimeter": "mm", "millimeter": "mm",
@ -40,14 +44,14 @@ TYPE_CONVERSION = {
} }
METRIC_CONVERSION = { METRIC_CONVERSION = {
"mm": from_to(0.001, 1000), "mm": FromTo(0.001, 1000),
"cm": from_to(0.01, 100), "cm": FromTo(0.01, 100),
"m": from_to(1, 1), "m": FromTo(1, 1),
"km": from_to(1000, 0.001), "km": FromTo(1000, 0.001),
"in": from_to(0.0254, 39.3701), "in": FromTo(0.0254, 39.3701),
"ft": from_to(0.3048, 3.28084), "ft": FromTo(0.3048, 3.28084),
"yd": from_to(0.9144, 1.09361), "yd": FromTo(0.9144, 1.09361),
"mi": from_to(1609.34, 0.000621371), "mi": FromTo(1609.34, 0.000621371),
} }
@ -115,7 +119,11 @@ def length_conversion(value: float, from_type: str, to_type: str) -> float:
f"Conversion abbreviations are: {', '.join(METRIC_CONVERSION)}" f"Conversion abbreviations are: {', '.join(METRIC_CONVERSION)}"
) )
raise ValueError(msg) raise ValueError(msg)
return value * METRIC_CONVERSION[new_from].from_ * METRIC_CONVERSION[new_to].to return (
value
* METRIC_CONVERSION[new_from].from_factor
* METRIC_CONVERSION[new_to].to_factor
)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -19,19 +19,23 @@ REFERENCES :
-> https://www.unitconverters.net/pressure-converter.html -> https://www.unitconverters.net/pressure-converter.html
""" """
from collections import namedtuple from typing import NamedTuple
class FromTo(NamedTuple):
from_factor: float
to_factor: float
from_to = namedtuple("from_to", "from_ to")
PRESSURE_CONVERSION = { PRESSURE_CONVERSION = {
"atm": from_to(1, 1), "atm": FromTo(1, 1),
"pascal": from_to(0.0000098, 101325), "pascal": FromTo(0.0000098, 101325),
"bar": from_to(0.986923, 1.01325), "bar": FromTo(0.986923, 1.01325),
"kilopascal": from_to(0.00986923, 101.325), "kilopascal": FromTo(0.00986923, 101.325),
"megapascal": from_to(9.86923, 0.101325), "megapascal": FromTo(9.86923, 0.101325),
"psi": from_to(0.068046, 14.6959), "psi": FromTo(0.068046, 14.6959),
"inHg": from_to(0.0334211, 29.9213), "inHg": FromTo(0.0334211, 29.9213),
"torr": from_to(0.00131579, 760), "torr": FromTo(0.00131579, 760),
} }
@ -71,7 +75,9 @@ def pressure_conversion(value: float, from_type: str, to_type: str) -> float:
+ ", ".join(PRESSURE_CONVERSION) + ", ".join(PRESSURE_CONVERSION)
) )
return ( return (
value * PRESSURE_CONVERSION[from_type].from_ * PRESSURE_CONVERSION[to_type].to value
* PRESSURE_CONVERSION[from_type].from_factor
* PRESSURE_CONVERSION[to_type].to_factor
) )

View File

@ -18,35 +18,39 @@ REFERENCES :
-> Wikipedia reference: https://en.wikipedia.org/wiki/Cup_(unit) -> Wikipedia reference: https://en.wikipedia.org/wiki/Cup_(unit)
""" """
from collections import namedtuple from typing import NamedTuple
class FromTo(NamedTuple):
from_factor: float
to_factor: float
from_to = namedtuple("from_to", "from_ to")
METRIC_CONVERSION = { METRIC_CONVERSION = {
"cubicmeter": from_to(1, 1), "cubic meter": FromTo(1, 1),
"litre": from_to(0.001, 1000), "litre": FromTo(0.001, 1000),
"kilolitre": from_to(1, 1), "kilolitre": FromTo(1, 1),
"gallon": from_to(0.00454, 264.172), "gallon": FromTo(0.00454, 264.172),
"cubicyard": from_to(0.76455, 1.30795), "cubic yard": FromTo(0.76455, 1.30795),
"cubicfoot": from_to(0.028, 35.3147), "cubic foot": FromTo(0.028, 35.3147),
"cup": from_to(0.000236588, 4226.75), "cup": FromTo(0.000236588, 4226.75),
} }
def volume_conversion(value: float, from_type: str, to_type: str) -> float: def volume_conversion(value: float, from_type: str, to_type: str) -> float:
""" """
Conversion between volume units. Conversion between volume units.
>>> volume_conversion(4, "cubicmeter", "litre") >>> volume_conversion(4, "cubic meter", "litre")
4000 4000
>>> volume_conversion(1, "litre", "gallon") >>> volume_conversion(1, "litre", "gallon")
0.264172 0.264172
>>> volume_conversion(1, "kilolitre", "cubicmeter") >>> volume_conversion(1, "kilolitre", "cubic meter")
1 1
>>> volume_conversion(3, "gallon", "cubicyard") >>> volume_conversion(3, "gallon", "cubic yard")
0.017814279 0.017814279
>>> volume_conversion(2, "cubicyard", "litre") >>> volume_conversion(2, "cubic yard", "litre")
1529.1 1529.1
>>> volume_conversion(4, "cubicfoot", "cup") >>> volume_conversion(4, "cubic foot", "cup")
473.396 473.396
>>> volume_conversion(1, "cup", "kilolitre") >>> volume_conversion(1, "cup", "kilolitre")
0.000236588 0.000236588
@ -54,7 +58,7 @@ def volume_conversion(value: float, from_type: str, to_type: str) -> float:
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Invalid 'from_type' value: 'wrongUnit' Supported values are: ValueError: Invalid 'from_type' value: 'wrongUnit' Supported values are:
cubicmeter, litre, kilolitre, gallon, cubicyard, cubicfoot, cup cubic meter, litre, kilolitre, gallon, cubic yard, cubic foot, cup
""" """
if from_type not in METRIC_CONVERSION: if from_type not in METRIC_CONVERSION:
raise ValueError( raise ValueError(
@ -66,7 +70,11 @@ def volume_conversion(value: float, from_type: str, to_type: str) -> float:
f"Invalid 'to_type' value: {to_type!r}. Supported values are:\n" f"Invalid 'to_type' value: {to_type!r}. Supported values are:\n"
+ ", ".join(METRIC_CONVERSION) + ", ".join(METRIC_CONVERSION)
) )
return value * METRIC_CONVERSION[from_type].from_ * METRIC_CONVERSION[to_type].to return (
value
* METRIC_CONVERSION[from_type].from_factor
* METRIC_CONVERSION[to_type].to_factor
)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -58,6 +58,19 @@ def inorder(root: Node | None) -> list[int]:
return [*inorder(root.left), root.data, *inorder(root.right)] if root else [] return [*inorder(root.left), root.data, *inorder(root.right)] if root else []
def reverse_inorder(root: Node | None) -> list[int]:
"""
Reverse in-order traversal visits right subtree, root node, left subtree.
>>> reverse_inorder(make_tree())
[3, 1, 5, 2, 4]
"""
return (
[*reverse_inorder(root.right), root.data, *reverse_inorder(root.left)]
if root
else []
)
def height(root: Node | None) -> int: def height(root: Node | None) -> int:
""" """
Recursive function for calculating the height of the binary tree. Recursive function for calculating the height of the binary tree.
@ -161,15 +174,12 @@ def zigzag(root: Node | None) -> Sequence[Node | None] | list[Any]:
def main() -> None: # Main function for testing. def main() -> None: # Main function for testing.
""" # Create binary tree.
Create binary tree.
"""
root = make_tree() root = make_tree()
"""
All Traversals of the binary are as follows:
"""
# All Traversals of the binary are as follows:
print(f"In-order Traversal: {inorder(root)}") print(f"In-order Traversal: {inorder(root)}")
print(f"Reverse In-order Traversal: {reverse_inorder(root)}")
print(f"Pre-order Traversal: {preorder(root)}") print(f"Pre-order Traversal: {preorder(root)}")
print(f"Post-order Traversal: {postorder(root)}", "\n") print(f"Post-order Traversal: {postorder(root)}", "\n")

View File

@ -39,8 +39,8 @@ Space: O(1)
from __future__ import annotations from __future__ import annotations
from collections import namedtuple
from dataclasses import dataclass from dataclasses import dataclass
from typing import NamedTuple
@dataclass @dataclass
@ -50,7 +50,9 @@ class TreeNode:
right: TreeNode | None = None right: TreeNode | None = None
CoinsDistribResult = namedtuple("CoinsDistribResult", "moves excess") class CoinsDistribResult(NamedTuple):
moves: int
excess: int
def distribute_coins(root: TreeNode | None) -> int: def distribute_coins(root: TreeNode | None) -> int:
@ -79,7 +81,7 @@ def distribute_coins(root: TreeNode | None) -> int:
# Validation # Validation
def count_nodes(node: TreeNode | None) -> int: def count_nodes(node: TreeNode | None) -> int:
""" """
>>> count_nodes(None): >>> count_nodes(None)
0 0
""" """
if node is None: if node is None:
@ -89,7 +91,7 @@ def distribute_coins(root: TreeNode | None) -> int:
def count_coins(node: TreeNode | None) -> int: def count_coins(node: TreeNode | None) -> int:
""" """
>>> count_coins(None): >>> count_coins(None)
0 0
""" """
if node is None: if node is None:

View File

@ -152,7 +152,7 @@ class RedBlackTree:
self.grandparent.color = 1 self.grandparent.color = 1
self.grandparent._insert_repair() self.grandparent._insert_repair()
def remove(self, label: int) -> RedBlackTree: def remove(self, label: int) -> RedBlackTree: # noqa: PLR0912
"""Remove label from this tree.""" """Remove label from this tree."""
if self.label == label: if self.label == label:
if self.left and self.right: if self.left and self.right:

View File

@ -7,7 +7,8 @@ class SegmentTree:
self.st = [0] * ( self.st = [0] * (
4 * self.N 4 * self.N
) # approximate the overall size of segment tree with array N ) # approximate the overall size of segment tree with array N
self.build(1, 0, self.N - 1) if self.N:
self.build(1, 0, self.N - 1)
def left(self, idx): def left(self, idx):
return idx * 2 return idx * 2

View File

@ -54,7 +54,7 @@ class Deque:
the current node of the iteration. the current node of the iteration.
""" """
__slots__ = "_cur" __slots__ = ("_cur",)
def __init__(self, cur: Deque._Node | None) -> None: def __init__(self, cur: Deque._Node | None) -> None:
self._cur = cur self._cur = cur

View File

@ -0,0 +1,141 @@
"""Queue represented by a Python list"""
from collections.abc import Iterable
from typing import Generic, TypeVar
_T = TypeVar("_T")
class QueueByList(Generic[_T]):
def __init__(self, iterable: Iterable[_T] | None = None) -> None:
"""
>>> QueueByList()
Queue(())
>>> QueueByList([10, 20, 30])
Queue((10, 20, 30))
>>> QueueByList((i**2 for i in range(1, 4)))
Queue((1, 4, 9))
"""
self.entries: list[_T] = list(iterable or [])
def __len__(self) -> int:
"""
>>> len(QueueByList())
0
>>> from string import ascii_lowercase
>>> len(QueueByList(ascii_lowercase))
26
>>> queue = QueueByList()
>>> for i in range(1, 11):
... queue.put(i)
>>> len(queue)
10
>>> for i in range(2):
... queue.get()
1
2
>>> len(queue)
8
"""
return len(self.entries)
def __repr__(self) -> str:
"""
>>> queue = QueueByList()
>>> queue
Queue(())
>>> str(queue)
'Queue(())'
>>> queue.put(10)
>>> queue
Queue((10,))
>>> queue.put(20)
>>> queue.put(30)
>>> queue
Queue((10, 20, 30))
"""
return f"Queue({tuple(self.entries)})"
def put(self, item: _T) -> None:
"""Put `item` to the Queue
>>> queue = QueueByList()
>>> queue.put(10)
>>> queue.put(20)
>>> len(queue)
2
>>> queue
Queue((10, 20))
"""
self.entries.append(item)
def get(self) -> _T:
"""
Get `item` from the Queue
>>> queue = QueueByList((10, 20, 30))
>>> queue.get()
10
>>> queue.put(40)
>>> queue.get()
20
>>> queue.get()
30
>>> len(queue)
1
>>> queue.get()
40
>>> queue.get()
Traceback (most recent call last):
...
IndexError: Queue is empty
"""
if not self.entries:
raise IndexError("Queue is empty")
return self.entries.pop(0)
def rotate(self, rotation: int) -> None:
"""Rotate the items of the Queue `rotation` times
>>> queue = QueueByList([10, 20, 30, 40])
>>> queue
Queue((10, 20, 30, 40))
>>> queue.rotate(1)
>>> queue
Queue((20, 30, 40, 10))
>>> queue.rotate(2)
>>> queue
Queue((40, 10, 20, 30))
"""
put = self.entries.append
get = self.entries.pop
for _ in range(rotation):
put(get(0))
def get_front(self) -> _T:
"""Get the front item from the Queue
>>> queue = QueueByList((10, 20, 30))
>>> queue.get_front()
10
>>> queue
Queue((10, 20, 30))
>>> queue.get()
10
>>> queue.get_front()
20
"""
return self.entries[0]
if __name__ == "__main__":
from doctest import testmod
testmod()

View File

@ -1,52 +0,0 @@
"""Queue represented by a Python list"""
class Queue:
def __init__(self):
self.entries = []
self.length = 0
self.front = 0
def __str__(self):
printed = "<" + str(self.entries)[1:-1] + ">"
return printed
"""Enqueues {@code item}
@param item
item to enqueue"""
def put(self, item):
self.entries.append(item)
self.length = self.length + 1
"""Dequeues {@code item}
@requirement: |self.length| > 0
@return dequeued
item that was dequeued"""
def get(self):
self.length = self.length - 1
dequeued = self.entries[self.front]
# self.front-=1
# self.entries = self.entries[self.front:]
self.entries = self.entries[1:]
return dequeued
"""Rotates the queue {@code rotation} times
@param rotation
number of times to rotate queue"""
def rotate(self, rotation):
for _ in range(rotation):
self.put(self.get())
"""Enqueues {@code item}
@return item at front of self.entries"""
def get_front(self):
return self.entries[0]
"""Returns the length of this.entries"""
def size(self):
return self.length

View File

@ -4,9 +4,26 @@ https://en.wikipedia.org/wiki/Reverse_Polish_notation
https://en.wikipedia.org/wiki/Shunting-yard_algorithm https://en.wikipedia.org/wiki/Shunting-yard_algorithm
""" """
from typing import Literal
from .balanced_parentheses import balanced_parentheses from .balanced_parentheses import balanced_parentheses
from .stack import Stack from .stack import Stack
PRECEDENCES: dict[str, int] = {
"+": 1,
"-": 1,
"*": 2,
"/": 2,
"^": 3,
}
ASSOCIATIVITIES: dict[str, Literal["LR", "RL"]] = {
"+": "LR",
"-": "LR",
"*": "LR",
"/": "LR",
"^": "RL",
}
def precedence(char: str) -> int: def precedence(char: str) -> int:
""" """
@ -14,7 +31,15 @@ def precedence(char: str) -> int:
order of operation. order of operation.
https://en.wikipedia.org/wiki/Order_of_operations https://en.wikipedia.org/wiki/Order_of_operations
""" """
return {"+": 1, "-": 1, "*": 2, "/": 2, "^": 3}.get(char, -1) return PRECEDENCES.get(char, -1)
def associativity(char: str) -> Literal["LR", "RL"]:
"""
Return the associativity of the operator `char`.
https://en.wikipedia.org/wiki/Operator_associativity
"""
return ASSOCIATIVITIES[char]
def infix_to_postfix(expression_str: str) -> str: def infix_to_postfix(expression_str: str) -> str:
@ -35,6 +60,8 @@ def infix_to_postfix(expression_str: str) -> str:
'a b c * + d e * f + g * +' 'a b c * + d e * f + g * +'
>>> infix_to_postfix("x^y/(5*z)+2") >>> infix_to_postfix("x^y/(5*z)+2")
'x y ^ 5 z * / 2 +' 'x y ^ 5 z * / 2 +'
>>> infix_to_postfix("2^3^2")
'2 3 2 ^ ^'
""" """
if not balanced_parentheses(expression_str): if not balanced_parentheses(expression_str):
raise ValueError("Mismatched parentheses") raise ValueError("Mismatched parentheses")
@ -50,9 +77,26 @@ def infix_to_postfix(expression_str: str) -> str:
postfix.append(stack.pop()) postfix.append(stack.pop())
stack.pop() stack.pop()
else: else:
while not stack.is_empty() and precedence(char) <= precedence(stack.peek()): while True:
if stack.is_empty():
stack.push(char)
break
char_precedence = precedence(char)
tos_precedence = precedence(stack.peek())
if char_precedence > tos_precedence:
stack.push(char)
break
if char_precedence < tos_precedence:
postfix.append(stack.pop())
continue
# Precedences are equal
if associativity(char) == "RL":
stack.push(char)
break
postfix.append(stack.pop()) postfix.append(stack.pop())
stack.push(char)
while not stack.is_empty(): while not stack.is_empty():
postfix.append(stack.pop()) postfix.append(stack.pop())
return " ".join(postfix) return " ".join(postfix)

View File

@ -54,10 +54,17 @@ class RadixNode:
word (str): word to insert word (str): word to insert
>>> RadixNode("myprefix").insert("mystring") >>> RadixNode("myprefix").insert("mystring")
>>> root = RadixNode()
>>> root.insert_many(['myprefix', 'myprefixA', 'myprefixAA'])
>>> root.print_tree()
- myprefix (leaf)
-- A (leaf)
--- A (leaf)
""" """
# Case 1: If the word is the prefix of the node # Case 1: If the word is the prefix of the node
# Solution: We set the current node as leaf # Solution: We set the current node as leaf
if self.prefix == word: if self.prefix == word and not self.is_leaf:
self.is_leaf = True self.is_leaf = True
# Case 2: The node has no edges that have a prefix to the word # Case 2: The node has no edges that have a prefix to the word
@ -156,7 +163,7 @@ class RadixNode:
del self.nodes[word[0]] del self.nodes[word[0]]
# We merge the current node with its only child # We merge the current node with its only child
if len(self.nodes) == 1 and not self.is_leaf: if len(self.nodes) == 1 and not self.is_leaf:
merging_node = list(self.nodes.values())[0] merging_node = next(iter(self.nodes.values()))
self.is_leaf = merging_node.is_leaf self.is_leaf = merging_node.is_leaf
self.prefix += merging_node.prefix self.prefix += merging_node.prefix
self.nodes = merging_node.nodes self.nodes = merging_node.nodes
@ -165,7 +172,7 @@ class RadixNode:
incoming_node.is_leaf = False incoming_node.is_leaf = False
# If there is 1 edge, we merge it with its child # If there is 1 edge, we merge it with its child
else: else:
merging_node = list(incoming_node.nodes.values())[0] merging_node = next(iter(incoming_node.nodes.values()))
incoming_node.is_leaf = merging_node.is_leaf incoming_node.is_leaf = merging_node.is_leaf
incoming_node.prefix += merging_node.prefix incoming_node.prefix += merging_node.prefix
incoming_node.nodes = merging_node.nodes incoming_node.nodes = merging_node.nodes

View File

@ -39,9 +39,18 @@ class Burkes:
def get_greyscale(cls, blue: int, green: int, red: int) -> float: def get_greyscale(cls, blue: int, green: int, red: int) -> float:
""" """
>>> Burkes.get_greyscale(3, 4, 5) >>> Burkes.get_greyscale(3, 4, 5)
3.753 4.185
>>> Burkes.get_greyscale(0, 0, 0)
0.0
>>> Burkes.get_greyscale(255, 255, 255)
255.0
""" """
return 0.114 * blue + 0.587 * green + 0.2126 * red """
Formula from https://en.wikipedia.org/wiki/HSL_and_HSV
cf Lightness section, and Fig 13c.
We use the first of four possible.
"""
return 0.114 * blue + 0.587 * green + 0.299 * red
def process(self) -> None: def process(self) -> None:
for y in range(self.height): for y in range(self.height):
@ -49,10 +58,10 @@ class Burkes:
greyscale = int(self.get_greyscale(*self.input_img[y][x])) greyscale = int(self.get_greyscale(*self.input_img[y][x]))
if self.threshold > greyscale + self.error_table[y][x]: if self.threshold > greyscale + self.error_table[y][x]:
self.output_img[y][x] = (0, 0, 0) self.output_img[y][x] = (0, 0, 0)
current_error = greyscale + self.error_table[x][y] current_error = greyscale + self.error_table[y][x]
else: else:
self.output_img[y][x] = (255, 255, 255) self.output_img[y][x] = (255, 255, 255)
current_error = greyscale + self.error_table[x][y] - 255 current_error = greyscale + self.error_table[y][x] - 255
""" """
Burkes error propagation (`*` is current pixel): Burkes error propagation (`*` is current pixel):

View File

@ -266,7 +266,7 @@ def convex_hull_bf(points: list[Point]) -> list[Point]:
points_left_of_ij = points_right_of_ij = False points_left_of_ij = points_right_of_ij = False
ij_part_of_convex_hull = True ij_part_of_convex_hull = True
for k in range(n): for k in range(n):
if k != i and k != j: if k not in {i, j}:
det_k = _det(points[i], points[j], points[k]) det_k = _det(points[i], points[j], points[k])
if det_k > 0: if det_k > 0:

View File

@ -0,0 +1,112 @@
"""
The maximum subarray problem is the task of finding the continuous subarray that has the
maximum sum within a given array of numbers. For example, given the array
[-2, 1, -3, 4, -1, 2, 1, -5, 4], the contiguous subarray with the maximum sum is
[4, -1, 2, 1], which has a sum of 6.
This divide-and-conquer algorithm finds the maximum subarray in O(n log n) time.
"""
from __future__ import annotations
import time
from collections.abc import Sequence
from random import randint
from matplotlib import pyplot as plt
def max_subarray(
arr: Sequence[float], low: int, high: int
) -> tuple[int | None, int | None, float]:
"""
Solves the maximum subarray problem using divide and conquer.
:param arr: the given array of numbers
:param low: the start index
:param high: the end index
:return: the start index of the maximum subarray, the end index of the
maximum subarray, and the maximum subarray sum
>>> nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
>>> max_subarray(nums, 0, len(nums) - 1)
(3, 6, 6)
>>> nums = [2, 8, 9]
>>> max_subarray(nums, 0, len(nums) - 1)
(0, 2, 19)
>>> nums = [0, 0]
>>> max_subarray(nums, 0, len(nums) - 1)
(0, 0, 0)
>>> nums = [-1.0, 0.0, 1.0]
>>> max_subarray(nums, 0, len(nums) - 1)
(2, 2, 1.0)
>>> nums = [-2, -3, -1, -4, -6]
>>> max_subarray(nums, 0, len(nums) - 1)
(2, 2, -1)
>>> max_subarray([], 0, 0)
(None, None, 0)
"""
if not arr:
return None, None, 0
if low == high:
return low, high, arr[low]
mid = (low + high) // 2
left_low, left_high, left_sum = max_subarray(arr, low, mid)
right_low, right_high, right_sum = max_subarray(arr, mid + 1, high)
cross_left, cross_right, cross_sum = max_cross_sum(arr, low, mid, high)
if left_sum >= right_sum and left_sum >= cross_sum:
return left_low, left_high, left_sum
elif right_sum >= left_sum and right_sum >= cross_sum:
return right_low, right_high, right_sum
return cross_left, cross_right, cross_sum
def max_cross_sum(
arr: Sequence[float], low: int, mid: int, high: int
) -> tuple[int, int, float]:
left_sum, max_left = float("-inf"), -1
right_sum, max_right = float("-inf"), -1
summ: int | float = 0
for i in range(mid, low - 1, -1):
summ += arr[i]
if summ > left_sum:
left_sum = summ
max_left = i
summ = 0
for i in range(mid + 1, high + 1):
summ += arr[i]
if summ > right_sum:
right_sum = summ
max_right = i
return max_left, max_right, (left_sum + right_sum)
def time_max_subarray(input_size: int) -> float:
arr = [randint(1, input_size) for _ in range(input_size)]
start = time.time()
max_subarray(arr, 0, input_size - 1)
end = time.time()
return end - start
def plot_runtimes() -> None:
input_sizes = [10, 100, 1000, 10000, 50000, 100000, 200000, 300000, 400000, 500000]
runtimes = [time_max_subarray(input_size) for input_size in input_sizes]
print("No of Inputs\t\tTime Taken")
for input_size, runtime in zip(input_sizes, runtimes):
print(input_size, "\t\t", runtime)
plt.plot(input_sizes, runtimes)
plt.xlabel("Number of Inputs")
plt.ylabel("Time taken in seconds")
plt.show()
if __name__ == "__main__":
"""
A random simulation of this algorithm.
"""
from doctest import testmod
testmod()

View File

@ -1,78 +0,0 @@
"""
Given a array of length n, max_subarray_sum() finds
the maximum of sum of contiguous sub-array using divide and conquer method.
Time complexity : O(n log n)
Ref : INTRODUCTION TO ALGORITHMS THIRD EDITION
(section : 4, sub-section : 4.1, page : 70)
"""
def max_sum_from_start(array):
"""This function finds the maximum contiguous sum of array from 0 index
Parameters :
array (list[int]) : given array
Returns :
max_sum (int) : maximum contiguous sum of array from 0 index
"""
array_sum = 0
max_sum = float("-inf")
for num in array:
array_sum += num
if array_sum > max_sum:
max_sum = array_sum
return max_sum
def max_cross_array_sum(array, left, mid, right):
"""This function finds the maximum contiguous sum of left and right arrays
Parameters :
array, left, mid, right (list[int], int, int, int)
Returns :
(int) : maximum of sum of contiguous sum of left and right arrays
"""
max_sum_of_left = max_sum_from_start(array[left : mid + 1][::-1])
max_sum_of_right = max_sum_from_start(array[mid + 1 : right + 1])
return max_sum_of_left + max_sum_of_right
def max_subarray_sum(array, left, right):
"""Maximum contiguous sub-array sum, using divide and conquer method
Parameters :
array, left, right (list[int], int, int) :
given array, current left index and current right index
Returns :
int : maximum of sum of contiguous sub-array
"""
# base case: array has only one element
if left == right:
return array[right]
# Recursion
mid = (left + right) // 2
left_half_sum = max_subarray_sum(array, left, mid)
right_half_sum = max_subarray_sum(array, mid + 1, right)
cross_sum = max_cross_array_sum(array, left, mid, right)
return max(left_half_sum, right_half_sum, cross_sum)
if __name__ == "__main__":
array = [-2, -5, 6, -2, -3, 1, 5, -6]
array_length = len(array)
print(
"Maximum sum of contiguous subarray:",
max_subarray_sum(array, 0, array_length - 1),
)

View File

@ -1,93 +0,0 @@
"""
author : Mayank Kumar Jha (mk9440)
"""
from __future__ import annotations
def find_max_sub_array(a, low, high):
if low == high:
return low, high, a[low]
else:
mid = (low + high) // 2
left_low, left_high, left_sum = find_max_sub_array(a, low, mid)
right_low, right_high, right_sum = find_max_sub_array(a, mid + 1, high)
cross_left, cross_right, cross_sum = find_max_cross_sum(a, low, mid, high)
if left_sum >= right_sum and left_sum >= cross_sum:
return left_low, left_high, left_sum
elif right_sum >= left_sum and right_sum >= cross_sum:
return right_low, right_high, right_sum
else:
return cross_left, cross_right, cross_sum
def find_max_cross_sum(a, low, mid, high):
left_sum, max_left = -999999999, -1
right_sum, max_right = -999999999, -1
summ = 0
for i in range(mid, low - 1, -1):
summ += a[i]
if summ > left_sum:
left_sum = summ
max_left = i
summ = 0
for i in range(mid + 1, high + 1):
summ += a[i]
if summ > right_sum:
right_sum = summ
max_right = i
return max_left, max_right, (left_sum + right_sum)
def max_sub_array(nums: list[int]) -> int:
"""
Finds the contiguous subarray which has the largest sum and return its sum.
>>> max_sub_array([-2, 1, -3, 4, -1, 2, 1, -5, 4])
6
An empty (sub)array has sum 0.
>>> max_sub_array([])
0
If all elements are negative, the largest subarray would be the empty array,
having the sum 0.
>>> max_sub_array([-1, -2, -3])
0
>>> max_sub_array([5, -2, -3])
5
>>> max_sub_array([31, -41, 59, 26, -53, 58, 97, -93, -23, 84])
187
"""
best = 0
current = 0
for i in nums:
current += i
current = max(current, 0)
best = max(best, current)
return best
if __name__ == "__main__":
"""
A random simulation of this algorithm.
"""
import time
from random import randint
from matplotlib import pyplot as plt
inputs = [10, 100, 1000, 10000, 50000, 100000, 200000, 300000, 400000, 500000]
tim = []
for i in inputs:
li = [randint(1, i) for j in range(i)]
strt = time.time()
(find_max_sub_array(li, 0, len(li) - 1))
end = time.time()
tim.append(end - strt)
print("No of Inputs Time Taken")
for i in range(len(inputs)):
print(inputs[i], "\t\t", tim[i])
plt.plot(inputs, tim)
plt.xlabel("Number of Inputs")
plt.ylabel("Time taken in seconds ")
plt.show()

View File

@ -0,0 +1,60 @@
"""
The maximum subarray sum problem is the task of finding the maximum sum that can be
obtained from a contiguous subarray within a given array of numbers. For example, given
the array [-2, 1, -3, 4, -1, 2, 1, -5, 4], the contiguous subarray with the maximum sum
is [4, -1, 2, 1], so the maximum subarray sum is 6.
Kadane's algorithm is a simple dynamic programming algorithm that solves the maximum
subarray sum problem in O(n) time and O(1) space.
Reference: https://en.wikipedia.org/wiki/Maximum_subarray_problem
"""
from collections.abc import Sequence
def max_subarray_sum(
arr: Sequence[float], allow_empty_subarrays: bool = False
) -> float:
"""
Solves the maximum subarray sum problem using Kadane's algorithm.
:param arr: the given array of numbers
:param allow_empty_subarrays: if True, then the algorithm considers empty subarrays
>>> max_subarray_sum([2, 8, 9])
19
>>> max_subarray_sum([0, 0])
0
>>> max_subarray_sum([-1.0, 0.0, 1.0])
1.0
>>> max_subarray_sum([1, 2, 3, 4, -2])
10
>>> max_subarray_sum([-2, 1, -3, 4, -1, 2, 1, -5, 4])
6
>>> max_subarray_sum([2, 3, -9, 8, -2])
8
>>> max_subarray_sum([-2, -3, -1, -4, -6])
-1
>>> max_subarray_sum([-2, -3, -1, -4, -6], allow_empty_subarrays=True)
0
>>> max_subarray_sum([])
0
"""
if not arr:
return 0
max_sum = 0 if allow_empty_subarrays else float("-inf")
curr_sum = 0.0
for num in arr:
curr_sum = max(0 if allow_empty_subarrays else num, curr_sum + num)
max_sum = max(max_sum, curr_sum)
return max_sum
if __name__ == "__main__":
from doctest import testmod
testmod()
nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
print(f"{max_subarray_sum(nums) = }")

View File

@ -1,20 +0,0 @@
def max_subarray_sum(nums: list) -> int:
"""
>>> max_subarray_sum([6 , 9, -1, 3, -7, -5, 10])
17
"""
if not nums:
return 0
n = len(nums)
res, s, s_pre = nums[0], nums[0], nums[0]
for i in range(1, n):
s = max(nums[i], s_pre + nums[i])
s_pre = s
res = max(res, s)
return res
if __name__ == "__main__":
nums = [6, 9, -1, 3, -7, -5, 10]
print(max_subarray_sum(nums))

View File

@ -0,0 +1,97 @@
"""
Regex matching check if a text matches pattern or not.
Pattern:
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
More info:
https://medium.com/trick-the-interviwer/regular-expression-matching-9972eb74c03
"""
def recursive_match(text: str, pattern: str) -> bool:
"""
Recursive matching algorithm.
Time complexity: O(2 ^ (|text| + |pattern|))
Space complexity: Recursion depth is O(|text| + |pattern|).
:param text: Text to match.
:param pattern: Pattern to match.
:return: True if text matches pattern, False otherwise.
>>> recursive_match('abc', 'a.c')
True
>>> recursive_match('abc', 'af*.c')
True
>>> recursive_match('abc', 'a.c*')
True
>>> recursive_match('abc', 'a.c*d')
False
>>> recursive_match('aa', '.*')
True
"""
if not pattern:
return not text
if not text:
return pattern[-1] == "*" and recursive_match(text, pattern[:-2])
if text[-1] == pattern[-1] or pattern[-1] == ".":
return recursive_match(text[:-1], pattern[:-1])
if pattern[-1] == "*":
return recursive_match(text[:-1], pattern) or recursive_match(
text, pattern[:-2]
)
return False
def dp_match(text: str, pattern: str) -> bool:
"""
Dynamic programming matching algorithm.
Time complexity: O(|text| * |pattern|)
Space complexity: O(|text| * |pattern|)
:param text: Text to match.
:param pattern: Pattern to match.
:return: True if text matches pattern, False otherwise.
>>> dp_match('abc', 'a.c')
True
>>> dp_match('abc', 'af*.c')
True
>>> dp_match('abc', 'a.c*')
True
>>> dp_match('abc', 'a.c*d')
False
>>> dp_match('aa', '.*')
True
"""
m = len(text)
n = len(pattern)
dp = [[False for _ in range(n + 1)] for _ in range(m + 1)]
dp[0][0] = True
for j in range(1, n + 1):
dp[0][j] = pattern[j - 1] == "*" and dp[0][j - 2]
for i in range(1, m + 1):
for j in range(1, n + 1):
if pattern[j - 1] in {".", text[i - 1]}:
dp[i][j] = dp[i - 1][j - 1]
elif pattern[j - 1] == "*":
dp[i][j] = dp[i][j - 2]
if pattern[j - 2] in {".", text[i - 1]}:
dp[i][j] |= dp[i - 1][j]
else:
dp[i][j] = False
return dp[m][n]
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -0,0 +1,24 @@
# Tribonacci sequence using Dynamic Programming
def tribonacci(num: int) -> list[int]:
"""
Given a number, return first n Tribonacci Numbers.
>>> tribonacci(5)
[0, 0, 1, 1, 2]
>>> tribonacci(8)
[0, 0, 1, 1, 2, 4, 7, 13]
"""
dp = [0] * num
dp[2] = 1
for i in range(3, num):
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]
return dp
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,7 +1,12 @@
# https://en.m.wikipedia.org/wiki/Electric_power # https://en.m.wikipedia.org/wiki/Electric_power
from __future__ import annotations from __future__ import annotations
from collections import namedtuple from typing import NamedTuple
class Result(NamedTuple):
name: str
value: float
def electric_power(voltage: float, current: float, power: float) -> tuple: def electric_power(voltage: float, current: float, power: float) -> tuple:
@ -10,11 +15,11 @@ def electric_power(voltage: float, current: float, power: float) -> tuple:
fundamental value of electrical system. fundamental value of electrical system.
examples are below: examples are below:
>>> electric_power(voltage=0, current=2, power=5) >>> electric_power(voltage=0, current=2, power=5)
result(name='voltage', value=2.5) Result(name='voltage', value=2.5)
>>> electric_power(voltage=2, current=2, power=0) >>> electric_power(voltage=2, current=2, power=0)
result(name='power', value=4.0) Result(name='power', value=4.0)
>>> electric_power(voltage=-2, current=3, power=0) >>> electric_power(voltage=-2, current=3, power=0)
result(name='power', value=6.0) Result(name='power', value=6.0)
>>> electric_power(voltage=2, current=4, power=2) >>> electric_power(voltage=2, current=4, power=2)
Traceback (most recent call last): Traceback (most recent call last):
... ...
@ -28,9 +33,8 @@ def electric_power(voltage: float, current: float, power: float) -> tuple:
... ...
ValueError: Power cannot be negative in any electrical/electronics system ValueError: Power cannot be negative in any electrical/electronics system
>>> electric_power(voltage=2.2, current=2.2, power=0) >>> electric_power(voltage=2.2, current=2.2, power=0)
result(name='power', value=4.84) Result(name='power', value=4.84)
""" """
result = namedtuple("result", "name value")
if (voltage, current, power).count(0) != 1: if (voltage, current, power).count(0) != 1:
raise ValueError("Only one argument must be 0") raise ValueError("Only one argument must be 0")
elif power < 0: elif power < 0:
@ -38,11 +42,11 @@ def electric_power(voltage: float, current: float, power: float) -> tuple:
"Power cannot be negative in any electrical/electronics system" "Power cannot be negative in any electrical/electronics system"
) )
elif voltage == 0: elif voltage == 0:
return result("voltage", power / current) return Result("voltage", power / current)
elif current == 0: elif current == 0:
return result("current", power / voltage) return Result("current", power / voltage)
elif power == 0: elif power == 0:
return result("power", float(round(abs(voltage * current), 2))) return Result("power", float(round(abs(voltage * current), 2)))
else: else:
raise ValueError("Exactly one argument must be 0") raise ValueError("Exactly one argument must be 0")

View File

@ -82,3 +82,4 @@ if __name__ == "__main__":
vertices = [(-175, -125), (0, 175), (175, -125)] # vertices of triangle vertices = [(-175, -125), (0, 175), (175, -125)] # vertices of triangle
triangle(vertices[0], vertices[1], vertices[2], int(sys.argv[1])) triangle(vertices[0], vertices[1], vertices[2], int(sys.argv[1]))
turtle.Screen().exitonclick()

View File

@ -26,8 +26,8 @@ def pass_and_relaxation(
cst_bwd: dict, cst_bwd: dict,
queue: PriorityQueue, queue: PriorityQueue,
parent: dict, parent: dict,
shortest_distance: float | int, shortest_distance: float,
) -> float | int: ) -> float:
for nxt, d in graph[v]: for nxt, d in graph[v]:
if nxt in visited_forward: if nxt in visited_forward:
continue continue

View File

@ -39,7 +39,7 @@ class DirectedGraph:
stack = [] stack = []
visited = [] visited = []
if s == -2: if s == -2:
s = list(self.graph)[0] s = next(iter(self.graph))
stack.append(s) stack.append(s)
visited.append(s) visited.append(s)
ss = s ss = s
@ -87,7 +87,7 @@ class DirectedGraph:
d = deque() d = deque()
visited = [] visited = []
if s == -2: if s == -2:
s = list(self.graph)[0] s = next(iter(self.graph))
d.append(s) d.append(s)
visited.append(s) visited.append(s)
while d: while d:
@ -114,7 +114,7 @@ class DirectedGraph:
stack = [] stack = []
visited = [] visited = []
if s == -2: if s == -2:
s = list(self.graph)[0] s = next(iter(self.graph))
stack.append(s) stack.append(s)
visited.append(s) visited.append(s)
ss = s ss = s
@ -146,7 +146,7 @@ class DirectedGraph:
def cycle_nodes(self): def cycle_nodes(self):
stack = [] stack = []
visited = [] visited = []
s = list(self.graph)[0] s = next(iter(self.graph))
stack.append(s) stack.append(s)
visited.append(s) visited.append(s)
parent = -2 parent = -2
@ -199,7 +199,7 @@ class DirectedGraph:
def has_cycle(self): def has_cycle(self):
stack = [] stack = []
visited = [] visited = []
s = list(self.graph)[0] s = next(iter(self.graph))
stack.append(s) stack.append(s)
visited.append(s) visited.append(s)
parent = -2 parent = -2
@ -305,7 +305,7 @@ class Graph:
stack = [] stack = []
visited = [] visited = []
if s == -2: if s == -2:
s = list(self.graph)[0] s = next(iter(self.graph))
stack.append(s) stack.append(s)
visited.append(s) visited.append(s)
ss = s ss = s
@ -353,7 +353,7 @@ class Graph:
d = deque() d = deque()
visited = [] visited = []
if s == -2: if s == -2:
s = list(self.graph)[0] s = next(iter(self.graph))
d.append(s) d.append(s)
visited.append(s) visited.append(s)
while d: while d:
@ -371,7 +371,7 @@ class Graph:
def cycle_nodes(self): def cycle_nodes(self):
stack = [] stack = []
visited = [] visited = []
s = list(self.graph)[0] s = next(iter(self.graph))
stack.append(s) stack.append(s)
visited.append(s) visited.append(s)
parent = -2 parent = -2
@ -424,7 +424,7 @@ class Graph:
def has_cycle(self): def has_cycle(self):
stack = [] stack = []
visited = [] visited = []
s = list(self.graph)[0] s = next(iter(self.graph))
stack.append(s) stack.append(s)
visited.append(s) visited.append(s)
parent = -2 parent = -2

View File

@ -113,7 +113,7 @@ class PushRelabelExecutor(MaximumFlowAlgorithmExecutor):
vertices_list = [ vertices_list = [
i i
for i in range(self.verticies_count) for i in range(self.verticies_count)
if i != self.source_index and i != self.sink_index if i not in {self.source_index, self.sink_index}
] ]
# move through list # move through list

View File

@ -20,7 +20,7 @@ def check_circuit_or_path(graph, max_node):
odd_degree_nodes = 0 odd_degree_nodes = 0
odd_node = -1 odd_node = -1
for i in range(max_node): for i in range(max_node):
if i not in graph.keys(): if i not in graph:
continue continue
if len(graph[i]) % 2 == 1: if len(graph[i]) % 2 == 1:
odd_degree_nodes += 1 odd_degree_nodes += 1

View File

@ -43,62 +43,43 @@ def points_to_polynomial(coordinates: list[list[int]]) -> str:
x = len(coordinates) x = len(coordinates)
count_of_line = 0
matrix: list[list[float]] = []
# put the x and x to the power values in a matrix # put the x and x to the power values in a matrix
while count_of_line < x: matrix: list[list[float]] = [
count_in_line = 0 [
a = coordinates[count_of_line][0] coordinates[count_of_line][0] ** (x - (count_in_line + 1))
count_line: list[float] = [] for count_in_line in range(x)
while count_in_line < x: ]
count_line.append(a ** (x - (count_in_line + 1))) for count_of_line in range(x)
count_in_line += 1 ]
matrix.append(count_line)
count_of_line += 1
count_of_line = 0
# put the y values into a vector # put the y values into a vector
vector: list[float] = [] vector: list[float] = [coordinates[count_of_line][1] for count_of_line in range(x)]
while count_of_line < x:
vector.append(coordinates[count_of_line][1])
count_of_line += 1
count = 0 for count in range(x):
for number in range(x):
while count < x: if count == number:
zahlen = 0 continue
while zahlen < x: fraction = matrix[number][count] / matrix[count][count]
if count == zahlen:
zahlen += 1
if zahlen == x:
break
bruch = matrix[zahlen][count] / matrix[count][count]
for counting_columns, item in enumerate(matrix[count]): for counting_columns, item in enumerate(matrix[count]):
# manipulating all the values in the matrix # manipulating all the values in the matrix
matrix[zahlen][counting_columns] -= item * bruch matrix[number][counting_columns] -= item * fraction
# manipulating the values in the vector # manipulating the values in the vector
vector[zahlen] -= vector[count] * bruch vector[number] -= vector[count] * fraction
zahlen += 1
count += 1
count = 0
# make solutions # make solutions
solution: list[str] = [] solution: list[str] = [
while count < x: str(vector[count] / matrix[count][count]) for count in range(x)
solution.append(str(vector[count] / matrix[count][count])) ]
count += 1
count = 0
solved = "f(x)=" solved = "f(x)="
while count < x: for count in range(x):
remove_e: list[str] = solution[count].split("E") remove_e: list[str] = solution[count].split("E")
if len(remove_e) > 1: if len(remove_e) > 1:
solution[count] = f"{remove_e[0]}*10^{remove_e[1]}" solution[count] = f"{remove_e[0]}*10^{remove_e[1]}"
solved += f"x^{x - (count + 1)}*{solution[count]}" solved += f"x^{x - (count + 1)}*{solution[count]}"
if count + 1 != x: if count + 1 != x:
solved += "+" solved += "+"
count += 1
return solved return solved

View File

@ -4,7 +4,7 @@ tabular form with
- `>=`, `<=`, and `=` constraints and - `>=`, `<=`, and `=` constraints and
- each variable `x1, x2, ...>= 0`. - each variable `x1, x2, ...>= 0`.
See https://gist.github.com/imengus/f9619a568f8da5bc74eaf20169a24d98 for how to See https://gist.github.com/imengus/f9619a568f7da5bc74eaf20169a24d98 for how to
convert linear programs to simplex tableaus, and the steps taken in the simplex convert linear programs to simplex tableaus, and the steps taken in the simplex
algorithm. algorithm.
@ -29,33 +29,44 @@ class Tableau:
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: RHS must be > 0 ValueError: RHS must be > 0
"""
def __init__(self, tableau: np.ndarray, n_vars: int, n_art_vars: int) -> None: >>> Tableau(np.array([[-1,-1,0,0,1],[1,3,1,0,4],[3,1,0,1,4.]]), -2, 2)
Traceback (most recent call last):
...
ValueError: number of (artificial) variables must be a natural number
"""
# Max iteration number to prevent cycling
maxiter = 100
def __init__(self, tableau: np.ndarray, n_vars: int, n_artificial_vars: int) -> None:
if tableau.dtype != 'float64': if tableau.dtype != 'float64':
raise TypeError('Tableau must have type float64') raise TypeError('Tableau must have type float64')
# Check if RHS is negative # Check if RHS is negative
if np.any(tableau[:, -1], where=tableau[:, -1] < 0): if not (tableau[:, -1] >= 0).all():
raise ValueError("RHS must be > 0") raise ValueError("RHS must be > 0")
if n_vars < 2 or n_artificial_vars < 0:
raise ValueError(
"number of (artificial) variables must be a natural number"
)
self.tableau = tableau self.tableau = tableau
self.n_rows, n_cols = tableau.shape self.n_rows, n_cols = tableau.shape
# Number of decision variables x1, x2, x3... # Number of decision variables x1, x2, x3...
self.n_vars, self.n_art_vars = n_vars, n_art_vars self.n_vars, self.n_artificial_vars = n_vars, n_artificial_vars
# 2 if there are >= or == constraints (nonstandard), 1 otherwise (std) # 2 if there are >= or == constraints (nonstandard), 1 otherwise (std)
self.n_stages = (self.n_art_vars > 0) + 1 self.n_stages = (self.n_artificial_vars > 0) + 1
# Number of slack variables added to make inequalities into equalities # Number of slack variables added to make inequalities into equalities
self.n_slack = n_cols - self.n_vars - self.n_art_vars - 1 self.n_slack = n_cols - self.n_vars - self.n_artificial_vars - 1
# Objectives for each stage # Objectives for each stage
self.objectives = ["max"] self.objectives = ["max"]
# In two stage simplex, first minimise then maximise # In two stage simplex, first minimise then maximise
if self.n_art_vars: if self.n_artificial_vars:
self.objectives.append("min") self.objectives.append("min")
self.col_titles = self.generate_col_titles() self.col_titles = self.generate_col_titles()
@ -166,8 +177,8 @@ class Tableau:
... [2, 1, 0, -1, 0, 1, 2] ... [2, 1, 0, -1, 0, 1, 2]
... ]), 2, 2).change_stage().tolist() ... ]), 2, 2).change_stage().tolist()
... # doctest: +NORMALIZE_WHITESPACE ... # doctest: +NORMALIZE_WHITESPACE
[[2.0, 1.0, 0.0, 0.0, 0.0], [[2.0, 1.0, 0.0, 0.0, 0.0],
[1.0, 2.0, -1.0, 0.0, 2.0], [1.0, 2.0, -1.0, 0.0, 2.0],
[2.0, 1.0, 0.0, -1.0, 2.0]] [2.0, 1.0, 0.0, -1.0, 2.0]]
""" """
# Objective of original objective row remains # Objective of original objective row remains
@ -177,7 +188,7 @@ class Tableau:
return self.tableau return self.tableau
# Slice containing ids for artificial columns # Slice containing ids for artificial columns
s = slice(-self.n_art_vars - 1, -1) s = slice(-self.n_artificial_vars - 1, -1)
# Delete the artificial variable columns # Delete the artificial variable columns
self.tableau = np.delete(self.tableau, s, axis=1) self.tableau = np.delete(self.tableau, s, axis=1)
@ -187,7 +198,7 @@ class Tableau:
self.n_stages = 1 self.n_stages = 1
self.n_rows -= 1 self.n_rows -= 1
self.n_art_vars = 0 self.n_artificial_vars = 0
self.stop_iter = False self.stop_iter = False
return self.tableau return self.tableau
@ -271,7 +282,7 @@ class Tableau:
{'P': 132.0, 'x1': 12.000... 'x2': 5.999...} {'P': 132.0, 'x1': 12.000... 'x2': 5.999...}
""" """
# Stop simplex algorithm from cycling. # Stop simplex algorithm from cycling.
for _ in range(100): for _ in range(Tableau.maxiter):
# Completion of each stage removes an objective. If both stages # Completion of each stage removes an objective. If both stages
# are complete, then no objectives are left # are complete, then no objectives are left
if not self.objectives: if not self.objectives:
@ -302,16 +313,16 @@ class Tableau:
output_dict = {"P": abs(self.tableau[0, -1])} output_dict = {"P": abs(self.tableau[0, -1])}
for i in range(self.n_vars): for i in range(self.n_vars):
# Gives ids of nonzero entries in the ith column # Gives indices of nonzero entries in the ith column
nonzero = np.nonzero(self.tableau[:, i]) nonzero = np.nonzero(self.tableau[:, i])
n_nonzero = len(nonzero[0]) n_nonzero = len(nonzero[0])
# First entry in the nonzero ids # First entry in the nonzero indices
nonzero_rowidx = nonzero[0][0] nonzero_rowidx = nonzero[0][0]
nonzero_val = self.tableau[nonzero_rowidx, i] nonzero_val = self.tableau[nonzero_rowidx, i]
# If there is only one nonzero value in column, which is one # If there is only one nonzero value in column, which is one
if n_nonzero == nonzero_val == 1: if n_nonzero == 1 and nonzero_val == 1:
rhs_val = self.tableau[nonzero_rowidx, -1] rhs_val = self.tableau[nonzero_rowidx, -1]
output_dict[self.col_titles[i]] = rhs_val output_dict[self.col_titles[i]] = rhs_val
return output_dict return output_dict

View File

@ -1,4 +1,4 @@
total_user,total_events,days total_users,total_events,days
18231,0.0,1 18231,0.0,1
22621,1.0,2 22621,1.0,2
15675,0.0,3 15675,0.0,3

1 total_user total_users total_events days
2 18231 0.0 1
3 22621 1.0 2
4 15675 0.0 3

View File

@ -1,6 +1,6 @@
""" """
this is code for forecasting this is code for forecasting
but i modified it and used it for safety checker of data but I modified it and used it for safety checker of data
for ex: you have an online shop and for some reason some data are for ex: you have an online shop and for some reason some data are
missing (the amount of data that u expected are not supposed to be) missing (the amount of data that u expected are not supposed to be)
then we can use it then we can use it
@ -11,6 +11,8 @@ missing (the amount of data that u expected are not supposed to be)
u can just adjust it for ur own purpose u can just adjust it for ur own purpose
""" """
from warnings import simplefilter
import numpy as np import numpy as np
import pandas as pd import pandas as pd
from sklearn.preprocessing import Normalizer from sklearn.preprocessing import Normalizer
@ -45,8 +47,10 @@ def sarimax_predictor(train_user: list, train_match: list, test_match: list) ->
>>> sarimax_predictor([4,2,6,8], [3,1,2,4], [2]) >>> sarimax_predictor([4,2,6,8], [3,1,2,4], [2])
6.6666671111109626 6.6666671111109626
""" """
# Suppress the User Warning raised by SARIMAX due to insufficient observations
simplefilter("ignore", UserWarning)
order = (1, 2, 1) order = (1, 2, 1)
seasonal_order = (1, 1, 0, 7) seasonal_order = (1, 1, 1, 7)
model = SARIMAX( model = SARIMAX(
train_user, exog=train_match, order=order, seasonal_order=seasonal_order train_user, exog=train_match, order=order, seasonal_order=seasonal_order
) )
@ -102,6 +106,10 @@ def data_safety_checker(list_vote: list, actual_result: float) -> bool:
""" """
safe = 0 safe = 0
not_safe = 0 not_safe = 0
if not isinstance(actual_result, float):
raise TypeError("Actual result should be float. Value passed is a list")
for i in list_vote: for i in list_vote:
if i > actual_result: if i > actual_result:
safe = not_safe + 1 safe = not_safe + 1
@ -114,16 +122,11 @@ def data_safety_checker(list_vote: list, actual_result: float) -> bool:
if __name__ == "__main__": if __name__ == "__main__":
# data_input_df = pd.read_csv("ex_data.csv", header=None)
data_input = [[18231, 0.0, 1], [22621, 1.0, 2], [15675, 0.0, 3], [23583, 1.0, 4]]
data_input_df = pd.DataFrame(
data_input, columns=["total_user", "total_even", "days"]
)
""" """
data column = total user in a day, how much online event held in one day, data column = total user in a day, how much online event held in one day,
what day is that(sunday-saturday) what day is that(sunday-saturday)
""" """
data_input_df = pd.read_csv("ex_data.csv")
# start normalization # start normalization
normalize_df = Normalizer().fit_transform(data_input_df.values) normalize_df = Normalizer().fit_transform(data_input_df.values)
@ -138,23 +141,23 @@ if __name__ == "__main__":
x_test = x[len(x) - 1 :] x_test = x[len(x) - 1 :]
# for linear regression & sarimax # for linear regression & sarimax
trn_date = total_date[: len(total_date) - 1] train_date = total_date[: len(total_date) - 1]
trn_user = total_user[: len(total_user) - 1] train_user = total_user[: len(total_user) - 1]
trn_match = total_match[: len(total_match) - 1] train_match = total_match[: len(total_match) - 1]
tst_date = total_date[len(total_date) - 1 :] test_date = total_date[len(total_date) - 1 :]
tst_user = total_user[len(total_user) - 1 :] test_user = total_user[len(total_user) - 1 :]
tst_match = total_match[len(total_match) - 1 :] test_match = total_match[len(total_match) - 1 :]
# voting system with forecasting # voting system with forecasting
res_vote = [ res_vote = [
linear_regression_prediction( linear_regression_prediction(
trn_date, trn_user, trn_match, tst_date, tst_match train_date, train_user, train_match, test_date, test_match
), ),
sarimax_predictor(trn_user, trn_match, tst_match), sarimax_predictor(train_user, train_match, test_match),
support_vector_regressor(x_train, x_test, trn_user), support_vector_regressor(x_train, x_test, train_user),
] ]
# check the safety of today's data # check the safety of today's data
not_str = "" if data_safety_checker(res_vote, tst_user) else "not " not_str = "" if data_safety_checker(res_vote, test_user[0]) else "not "
print("Today's data is {not_str}safe.") print(f"Today's data is {not_str}safe.")

View File

@ -1,44 +0,0 @@
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.linear_model import LinearRegression
# Splitting the dataset into the Training set and Test set
from sklearn.model_selection import train_test_split
# Fitting Polynomial Regression to the dataset
from sklearn.preprocessing import PolynomialFeatures
# Importing the dataset
dataset = pd.read_csv(
"https://s3.us-west-2.amazonaws.com/public.gamelab.fun/dataset/"
"position_salaries.csv"
)
X = dataset.iloc[:, 1:2].values
y = dataset.iloc[:, 2].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
poly_reg = PolynomialFeatures(degree=4)
X_poly = poly_reg.fit_transform(X)
pol_reg = LinearRegression()
pol_reg.fit(X_poly, y)
# Visualizing the Polymonial Regression results
def viz_polymonial():
plt.scatter(X, y, color="red")
plt.plot(X, pol_reg.predict(poly_reg.fit_transform(X)), color="blue")
plt.title("Truth or Bluff (Linear Regression)")
plt.xlabel("Position level")
plt.ylabel("Salary")
plt.show()
if __name__ == "__main__":
viz_polymonial()
# Predicting a new result with Polymonial Regression
pol_reg.predict(poly_reg.fit_transform([[5.5]]))
# output should be 132148.43750003

View File

@ -0,0 +1,213 @@
"""
Polynomial regression is a type of regression analysis that models the relationship
between a predictor x and the response y as an mth-degree polynomial:
y = β₀ + β₁x + β₂x² + ... + βₘxᵐ + ε
By treating x, , ..., xᵐ as distinct variables, we see that polynomial regression is a
special case of multiple linear regression. Therefore, we can use ordinary least squares
(OLS) estimation to estimate the vector of model parameters β = (β₀, β₁, β₂, ..., βₘ)
for polynomial regression:
β = (XᵀX)¹Xᵀy = Xy
where X is the design matrix, y is the response vector, and X denotes the MoorePenrose
pseudoinverse of X. In the case of polynomial regression, the design matrix is
|1 x₁ x₁² x₁ᵐ|
X = |1 x₂ x₂² x₂ᵐ|
| |
|1 xₙ xₙ² xₙᵐ|
In OLS estimation, inverting XᵀX to compute X can be very numerically unstable. This
implementation sidesteps this need to invert XᵀX by computing X using singular value
decomposition (SVD):
β = Uᵀy
where UΣVᵀ is an SVD of X.
References:
- https://en.wikipedia.org/wiki/Polynomial_regression
- https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse
- https://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
- https://en.wikipedia.org/wiki/Singular_value_decomposition
"""
import matplotlib.pyplot as plt
import numpy as np
class PolynomialRegression:
__slots__ = "degree", "params"
def __init__(self, degree: int) -> None:
"""
@raises ValueError: if the polynomial degree is negative
"""
if degree < 0:
raise ValueError("Polynomial degree must be non-negative")
self.degree = degree
self.params = None
@staticmethod
def _design_matrix(data: np.ndarray, degree: int) -> np.ndarray:
"""
Constructs a polynomial regression design matrix for the given input data. For
input data x = (x₁, x₂, ..., xₙ) and polynomial degree m, the design matrix is
the Vandermonde matrix
|1 x₁ x₁² x₁ᵐ|
X = |1 x₂ x₂² x₂ᵐ|
| |
|1 xₙ xₙ² xₙᵐ|
Reference: https://en.wikipedia.org/wiki/Vandermonde_matrix
@param data: the input predictor values x, either for model fitting or for
prediction
@param degree: the polynomial degree m
@returns: the Vandermonde matrix X (see above)
@raises ValueError: if input data is not N x 1
>>> x = np.array([0, 1, 2])
>>> PolynomialRegression._design_matrix(x, degree=0)
array([[1],
[1],
[1]])
>>> PolynomialRegression._design_matrix(x, degree=1)
array([[1, 0],
[1, 1],
[1, 2]])
>>> PolynomialRegression._design_matrix(x, degree=2)
array([[1, 0, 0],
[1, 1, 1],
[1, 2, 4]])
>>> PolynomialRegression._design_matrix(x, degree=3)
array([[1, 0, 0, 0],
[1, 1, 1, 1],
[1, 2, 4, 8]])
>>> PolynomialRegression._design_matrix(np.array([[0, 0], [0 , 0]]), degree=3)
Traceback (most recent call last):
...
ValueError: Data must have dimensions N x 1
"""
rows, *remaining = data.shape
if remaining:
raise ValueError("Data must have dimensions N x 1")
return np.vander(data, N=degree + 1, increasing=True)
def fit(self, x_train: np.ndarray, y_train: np.ndarray) -> None:
"""
Computes the polynomial regression model parameters using ordinary least squares
(OLS) estimation:
β = (XᵀX)¹Xᵀy = Xy
where X denotes the MoorePenrose pseudoinverse of the design matrix X. This
function computes X using singular value decomposition (SVD).
References:
- https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse
- https://en.wikipedia.org/wiki/Singular_value_decomposition
- https://en.wikipedia.org/wiki/Multicollinearity
@param x_train: the predictor values x for model fitting
@param y_train: the response values y for model fitting
@raises ArithmeticError: if X isn't full rank, then XᵀX is singular and β
doesn't exist
>>> x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
>>> y = x**3 - 2 * x**2 + 3 * x - 5
>>> poly_reg = PolynomialRegression(degree=3)
>>> poly_reg.fit(x, y)
>>> poly_reg.params
array([-5., 3., -2., 1.])
>>> poly_reg = PolynomialRegression(degree=20)
>>> poly_reg.fit(x, y)
Traceback (most recent call last):
...
ArithmeticError: Design matrix is not full rank, can't compute coefficients
Make sure errors don't grow too large:
>>> coefs = np.array([-250, 50, -2, 36, 20, -12, 10, 2, -1, -15, 1])
>>> y = PolynomialRegression._design_matrix(x, len(coefs) - 1) @ coefs
>>> poly_reg = PolynomialRegression(degree=len(coefs) - 1)
>>> poly_reg.fit(x, y)
>>> np.allclose(poly_reg.params, coefs, atol=10e-3)
True
"""
X = PolynomialRegression._design_matrix(x_train, self.degree) # noqa: N806
_, cols = X.shape
if np.linalg.matrix_rank(X) < cols:
raise ArithmeticError(
"Design matrix is not full rank, can't compute coefficients"
)
# np.linalg.pinv() computes the MoorePenrose pseudoinverse using SVD
self.params = np.linalg.pinv(X) @ y_train
def predict(self, data: np.ndarray) -> np.ndarray:
"""
Computes the predicted response values y for the given input data by
constructing the design matrix X and evaluating y = .
@param data: the predictor values x for prediction
@returns: the predicted response values y =
@raises ArithmeticError: if this function is called before the model
parameters are fit
>>> x = np.array([0, 1, 2, 3, 4])
>>> y = x**3 - 2 * x**2 + 3 * x - 5
>>> poly_reg = PolynomialRegression(degree=3)
>>> poly_reg.fit(x, y)
>>> poly_reg.predict(np.array([-1]))
array([-11.])
>>> poly_reg.predict(np.array([-2]))
array([-27.])
>>> poly_reg.predict(np.array([6]))
array([157.])
>>> PolynomialRegression(degree=3).predict(x)
Traceback (most recent call last):
...
ArithmeticError: Predictor hasn't been fit yet
"""
if self.params is None:
raise ArithmeticError("Predictor hasn't been fit yet")
return PolynomialRegression._design_matrix(data, self.degree) @ self.params
def main() -> None:
"""
Fit a polynomial regression model to predict fuel efficiency using seaborn's mpg
dataset
>>> pass # Placeholder, function is only for demo purposes
"""
import seaborn as sns
mpg_data = sns.load_dataset("mpg")
poly_reg = PolynomialRegression(degree=2)
poly_reg.fit(mpg_data.weight, mpg_data.mpg)
weight_sorted = np.sort(mpg_data.weight)
predictions = poly_reg.predict(weight_sorted)
plt.scatter(mpg_data.weight, mpg_data.mpg, color="gray", alpha=0.5)
plt.plot(weight_sorted, predictions, color="red", linewidth=3)
plt.title("Predicting Fuel Efficiency Using Polynomial Regression")
plt.xlabel("Weight (lbs)")
plt.ylabel("Fuel Efficiency (mpg)")
plt.show()
if __name__ == "__main__":
import doctest
doctest.testmod()
main()

View File

@ -7,9 +7,9 @@ from collections.abc import Callable
def trapezoidal_area( def trapezoidal_area(
fnc: Callable[[int | float], int | float], fnc: Callable[[float], float],
x_start: int | float, x_start: float,
x_end: int | float, x_end: float,
steps: int = 100, steps: int = 100,
) -> float: ) -> float:
""" """

View File

@ -57,7 +57,7 @@ def collatz_sequence(n: int) -> Generator[int, None, None]:
def main(): def main():
n = 43 n = int(input("Your number: "))
sequence = tuple(collatz_sequence(n)) sequence = tuple(collatz_sequence(n))
print(sequence) print(sequence)
print(f"Collatz sequence from {n} took {len(sequence)} steps.") print(f"Collatz sequence from {n} took {len(sequence)} steps.")

View File

@ -1,4 +1,4 @@
def decimal_to_fraction(decimal: int | float | str) -> tuple[int, int]: def decimal_to_fraction(decimal: float | str) -> tuple[int, int]:
""" """
Return a decimal number in its simplest fraction form Return a decimal number in its simplest fraction form
>>> decimal_to_fraction(2) >>> decimal_to_fraction(2)

View File

@ -55,7 +55,7 @@ def factorial_recursive(n: int) -> int:
raise ValueError("factorial() only accepts integral values") raise ValueError("factorial() only accepts integral values")
if n < 0: if n < 0:
raise ValueError("factorial() not defined for negative values") raise ValueError("factorial() not defined for negative values")
return 1 if n == 0 or n == 1 else n * factorial(n - 1) return 1 if n in {0, 1} else n * factorial(n - 1)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,23 +1,23 @@
from __future__ import annotations from __future__ import annotations
def find_max(nums: list[int | float]) -> int | float: def find_max_iterative(nums: list[int | float]) -> int | float:
""" """
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_max(nums) == max(nums) ... find_max_iterative(nums) == max(nums)
True True
True True
True True
True True
>>> find_max([2, 4, 9, 7, 19, 94, 5]) >>> find_max_iterative([2, 4, 9, 7, 19, 94, 5])
94 94
>>> find_max([]) >>> find_max_iterative([])
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: find_max() arg is an empty sequence ValueError: find_max_iterative() arg is an empty sequence
""" """
if len(nums) == 0: if len(nums) == 0:
raise ValueError("find_max() arg is an empty sequence") raise ValueError("find_max_iterative() arg is an empty sequence")
max_num = nums[0] max_num = nums[0]
for x in nums: for x in nums:
if x > max_num: if x > max_num:
@ -25,6 +25,59 @@ def find_max(nums: list[int | float]) -> int | float:
return max_num return max_num
# Divide and Conquer algorithm
def find_max_recursive(nums: list[int | float], left: int, right: int) -> int | float:
"""
find max value in list
:param nums: contains elements
:param left: index of first element
:param right: index of last element
:return: max in nums
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_max_recursive(nums, 0, len(nums) - 1) == max(nums)
True
True
True
True
>>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
>>> find_max_recursive(nums, 0, len(nums) - 1) == max(nums)
True
>>> find_max_recursive([], 0, 0)
Traceback (most recent call last):
...
ValueError: find_max_recursive() arg is an empty sequence
>>> find_max_recursive(nums, 0, len(nums)) == max(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
>>> find_max_recursive(nums, -len(nums), -1) == max(nums)
True
>>> find_max_recursive(nums, -len(nums) - 1, -1) == max(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
"""
if len(nums) == 0:
raise ValueError("find_max_recursive() arg is an empty sequence")
if (
left >= len(nums)
or left < -len(nums)
or right >= len(nums)
or right < -len(nums)
):
raise IndexError("list index out of range")
if left == right:
return nums[left]
mid = (left + right) >> 1 # the middle
left_max = find_max_recursive(nums, left, mid) # find max in range[left, mid]
right_max = find_max_recursive(
nums, mid + 1, right
) # find max in range[mid + 1, right]
return left_max if left_max >= right_max else right_max
if __name__ == "__main__": if __name__ == "__main__":
import doctest import doctest

View File

@ -1,58 +0,0 @@
from __future__ import annotations
# Divide and Conquer algorithm
def find_max(nums: list[int | float], left: int, right: int) -> int | float:
"""
find max value in list
:param nums: contains elements
:param left: index of first element
:param right: index of last element
:return: max in nums
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_max(nums, 0, len(nums) - 1) == max(nums)
True
True
True
True
>>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
>>> find_max(nums, 0, len(nums) - 1) == max(nums)
True
>>> find_max([], 0, 0)
Traceback (most recent call last):
...
ValueError: find_max() arg is an empty sequence
>>> find_max(nums, 0, len(nums)) == max(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
>>> find_max(nums, -len(nums), -1) == max(nums)
True
>>> find_max(nums, -len(nums) - 1, -1) == max(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
"""
if len(nums) == 0:
raise ValueError("find_max() arg is an empty sequence")
if (
left >= len(nums)
or left < -len(nums)
or right >= len(nums)
or right < -len(nums)
):
raise IndexError("list index out of range")
if left == right:
return nums[left]
mid = (left + right) >> 1 # the middle
left_max = find_max(nums, left, mid) # find max in range[left, mid]
right_max = find_max(nums, mid + 1, right) # find max in range[mid + 1, right]
return left_max if left_max >= right_max else right_max
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=True)

View File

@ -1,33 +1,86 @@
from __future__ import annotations from __future__ import annotations
def find_min(nums: list[int | float]) -> int | float: def find_min_iterative(nums: list[int | float]) -> int | float:
""" """
Find Minimum Number in a List Find Minimum Number in a List
:param nums: contains elements :param nums: contains elements
:return: min number in list :return: min number in list
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_min(nums) == min(nums) ... find_min_iterative(nums) == min(nums)
True True
True True
True True
True True
>>> find_min([0, 1, 2, 3, 4, 5, -3, 24, -56]) >>> find_min_iterative([0, 1, 2, 3, 4, 5, -3, 24, -56])
-56 -56
>>> find_min([]) >>> find_min_iterative([])
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: find_min() arg is an empty sequence ValueError: find_min_iterative() arg is an empty sequence
""" """
if len(nums) == 0: if len(nums) == 0:
raise ValueError("find_min() arg is an empty sequence") raise ValueError("find_min_iterative() arg is an empty sequence")
min_num = nums[0] min_num = nums[0]
for num in nums: for num in nums:
min_num = min(min_num, num) min_num = min(min_num, num)
return min_num return min_num
# Divide and Conquer algorithm
def find_min_recursive(nums: list[int | float], left: int, right: int) -> int | float:
"""
find min value in list
:param nums: contains elements
:param left: index of first element
:param right: index of last element
:return: min in nums
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_min_recursive(nums, 0, len(nums) - 1) == min(nums)
True
True
True
True
>>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
>>> find_min_recursive(nums, 0, len(nums) - 1) == min(nums)
True
>>> find_min_recursive([], 0, 0)
Traceback (most recent call last):
...
ValueError: find_min_recursive() arg is an empty sequence
>>> find_min_recursive(nums, 0, len(nums)) == min(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
>>> find_min_recursive(nums, -len(nums), -1) == min(nums)
True
>>> find_min_recursive(nums, -len(nums) - 1, -1) == min(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
"""
if len(nums) == 0:
raise ValueError("find_min_recursive() arg is an empty sequence")
if (
left >= len(nums)
or left < -len(nums)
or right >= len(nums)
or right < -len(nums)
):
raise IndexError("list index out of range")
if left == right:
return nums[left]
mid = (left + right) >> 1 # the middle
left_min = find_min_recursive(nums, left, mid) # find min in range[left, mid]
right_min = find_min_recursive(
nums, mid + 1, right
) # find min in range[mid + 1, right]
return left_min if left_min <= right_min else right_min
if __name__ == "__main__": if __name__ == "__main__":
import doctest import doctest

View File

@ -1,58 +0,0 @@
from __future__ import annotations
# Divide and Conquer algorithm
def find_min(nums: list[int | float], left: int, right: int) -> int | float:
"""
find min value in list
:param nums: contains elements
:param left: index of first element
:param right: index of last element
:return: min in nums
>>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]):
... find_min(nums, 0, len(nums) - 1) == min(nums)
True
True
True
True
>>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
>>> find_min(nums, 0, len(nums) - 1) == min(nums)
True
>>> find_min([], 0, 0)
Traceback (most recent call last):
...
ValueError: find_min() arg is an empty sequence
>>> find_min(nums, 0, len(nums)) == min(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
>>> find_min(nums, -len(nums), -1) == min(nums)
True
>>> find_min(nums, -len(nums) - 1, -1) == min(nums)
Traceback (most recent call last):
...
IndexError: list index out of range
"""
if len(nums) == 0:
raise ValueError("find_min() arg is an empty sequence")
if (
left >= len(nums)
or left < -len(nums)
or right >= len(nums)
or right < -len(nums)
):
raise IndexError("list index out of range")
if left == right:
return nums[left]
mid = (left + right) >> 1 # the middle
left_min = find_min(nums, left, mid) # find min in range[left, mid]
right_min = find_min(nums, mid + 1, right) # find min in range[mid + 1, right]
return left_min if left_min <= right_min else right_min
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=True)

View File

@ -0,0 +1,66 @@
"""
An implementation of interquartile range (IQR) which is a measure of statistical
dispersion, which is the spread of the data.
The function takes the list of numeric values as input and returns the IQR.
Script inspired by this Wikipedia article:
https://en.wikipedia.org/wiki/Interquartile_range
"""
from __future__ import annotations
def find_median(nums: list[int | float]) -> float:
"""
This is the implementation of the median.
:param nums: The list of numeric nums
:return: Median of the list
>>> find_median(nums=([1, 2, 2, 3, 4]))
2
>>> find_median(nums=([1, 2, 2, 3, 4, 4]))
2.5
>>> find_median(nums=([-1, 2, 0, 3, 4, -4]))
1.5
>>> find_median(nums=([1.1, 2.2, 2, 3.3, 4.4, 4]))
2.65
"""
div, mod = divmod(len(nums), 2)
if mod:
return nums[div]
return (nums[div] + nums[(div) - 1]) / 2
def interquartile_range(nums: list[int | float]) -> float:
"""
Return the interquartile range for a list of numeric values.
:param nums: The list of numeric values.
:return: interquartile range
>>> interquartile_range(nums=[4, 1, 2, 3, 2])
2.0
>>> interquartile_range(nums = [-2, -7, -10, 9, 8, 4, -67, 45])
17.0
>>> interquartile_range(nums = [-2.1, -7.1, -10.1, 9.1, 8.1, 4.1, -67.1, 45.1])
17.2
>>> interquartile_range(nums = [0, 0, 0, 0, 0])
0.0
>>> interquartile_range(nums=[])
Traceback (most recent call last):
...
ValueError: The list is empty. Provide a non-empty list.
"""
if not nums:
raise ValueError("The list is empty. Provide a non-empty list.")
nums.sort()
length = len(nums)
div, mod = divmod(length, 2)
q1 = find_median(nums[:div])
half_length = sum((div, mod))
q3 = find_median(nums[half_length:length])
return q3 - q1
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,63 +0,0 @@
"""
Kadane's algorithm to get maximum subarray sum
https://medium.com/@rsinghal757/kadanes-algorithm-dynamic-programming-how-and-why-does-it-work-3fd8849ed73d
https://en.wikipedia.org/wiki/Maximum_subarray_problem
"""
test_data: tuple = ([-2, -8, -9], [2, 8, 9], [-1, 0, 1], [0, 0], [])
def negative_exist(arr: list) -> int:
"""
>>> negative_exist([-2,-8,-9])
-2
>>> [negative_exist(arr) for arr in test_data]
[-2, 0, 0, 0, 0]
"""
arr = arr or [0]
max_number = arr[0]
for i in arr:
if i >= 0:
return 0
elif max_number <= i:
max_number = i
return max_number
def kadanes(arr: list) -> int:
"""
If negative_exist() returns 0 than this function will execute
else it will return the value return by negative_exist function
For example: arr = [2, 3, -9, 8, -2]
Initially we set value of max_sum to 0 and max_till_element to 0 than when
max_sum is less than max_till particular element it will assign that value to
max_sum and when value of max_till_sum is less than 0 it will assign 0 to i
and after that whole process, return the max_sum
So the output for above arr is 8
>>> kadanes([2, 3, -9, 8, -2])
8
>>> [kadanes(arr) for arr in test_data]
[-2, 19, 1, 0, 0]
"""
max_sum = negative_exist(arr)
if max_sum < 0:
return max_sum
max_sum = 0
max_till_element = 0
for i in arr:
max_till_element += i
max_sum = max(max_sum, max_till_element)
max_till_element = max(max_till_element, 0)
return max_sum
if __name__ == "__main__":
try:
print("Enter integer values sepatated by spaces")
arr = [int(x) for x in input().split()]
print(f"Maximum subarray sum of {arr} is {kadanes(arr)}")
except ValueError:
print("Please enter integer values.")

View File

@ -1,21 +0,0 @@
from sys import maxsize
def max_sub_array_sum(a: list, size: int = 0):
"""
>>> max_sub_array_sum([-13, -3, -25, -20, -3, -16, -23, -12, -5, -22, -15, -4, -7])
-3
"""
size = size or len(a)
max_so_far = -maxsize - 1
max_ending_here = 0
for i in range(0, size):
max_ending_here = max_ending_here + a[i]
max_so_far = max(max_so_far, max_ending_here)
max_ending_here = max(max_ending_here, 0)
return max_so_far
if __name__ == "__main__":
a = [-13, -3, -25, -20, 1, -16, -23, -12, -5, -22, -15, -4, -7]
print(("Maximum contiguous sum is", max_sub_array_sum(a, len(a))))

View File

@ -5,9 +5,9 @@ from collections.abc import Callable
def line_length( def line_length(
fnc: Callable[[int | float], int | float], fnc: Callable[[float], float],
x_start: int | float, x_start: float,
x_end: int | float, x_end: float,
steps: int = 100, steps: int = 100,
) -> float: ) -> float:
""" """

View File

@ -7,9 +7,9 @@ from collections.abc import Callable
def trapezoidal_area( def trapezoidal_area(
fnc: Callable[[int | float], int | float], fnc: Callable[[float], float],
x_start: int | float, x_start: float,
x_end: int | float, x_end: float,
steps: int = 100, steps: int = 100,
) -> float: ) -> float:
""" """

View File

@ -87,7 +87,7 @@ class Polynomial:
return Polynomial(self.degree + polynomial_2.degree, coefficients) return Polynomial(self.degree + polynomial_2.degree, coefficients)
def evaluate(self, substitution: int | float) -> int | float: def evaluate(self, substitution: float) -> float:
""" """
Evaluates the polynomial at x. Evaluates the polynomial at x.
>>> p = Polynomial(2, [1, 2, 3]) >>> p = Polynomial(2, [1, 2, 3])
@ -144,7 +144,7 @@ class Polynomial:
coefficients[i] = self.coefficients[i + 1] * (i + 1) coefficients[i] = self.coefficients[i + 1] * (i + 1)
return Polynomial(self.degree - 1, coefficients) return Polynomial(self.degree - 1, coefficients)
def integral(self, constant: int | float = 0) -> Polynomial: def integral(self, constant: float = 0) -> Polynomial:
""" """
Returns the integral of the polynomial. Returns the integral of the polynomial.
>>> p = Polynomial(2, [1, 2, 3]) >>> p = Polynomial(2, [1, 2, 3])

View File

@ -154,7 +154,7 @@ def prime_factorization(number):
quotient = number quotient = number
if number == 0 or number == 1: if number in {0, 1}:
ans.append(number) ans.append(number)
# if 'number' not prime then builds the prime factorization of 'number' # if 'number' not prime then builds the prime factorization of 'number'

View File

@ -14,10 +14,10 @@ from __future__ import annotations
def geometric_series( def geometric_series(
nth_term: float | int, nth_term: float,
start_term_a: float | int, start_term_a: float,
common_ratio_r: float | int, common_ratio_r: float,
) -> list[float | int]: ) -> list[float]:
""" """
Pure Python implementation of Geometric Series algorithm Pure Python implementation of Geometric Series algorithm
@ -48,7 +48,7 @@ def geometric_series(
""" """
if not all((nth_term, start_term_a, common_ratio_r)): if not all((nth_term, start_term_a, common_ratio_r)):
return [] return []
series: list[float | int] = [] series: list[float] = []
power = 1 power = 1
multiple = common_ratio_r multiple = common_ratio_r
for _ in range(int(nth_term)): for _ in range(int(nth_term)):

View File

@ -13,7 +13,7 @@ python3 p_series.py
from __future__ import annotations from __future__ import annotations
def p_series(nth_term: int | float | str, power: int | float | str) -> list[str]: def p_series(nth_term: float | str, power: float | str) -> list[str]:
""" """
Pure Python implementation of P-Series algorithm Pure Python implementation of P-Series algorithm
:return: The P-Series starting from 1 to last (nth) term :return: The P-Series starting from 1 to last (nth) term

View File

@ -8,7 +8,7 @@ from __future__ import annotations
from math import pi, pow from math import pi, pow
def vol_cube(side_length: int | float) -> float: def vol_cube(side_length: float) -> float:
""" """
Calculate the Volume of a Cube. Calculate the Volume of a Cube.
>>> vol_cube(1) >>> vol_cube(1)

View File

@ -141,7 +141,7 @@ class Matrix:
@property @property
def order(self) -> tuple[int, int]: def order(self) -> tuple[int, int]:
return (self.num_rows, self.num_columns) return self.num_rows, self.num_columns
@property @property
def is_square(self) -> bool: def is_square(self) -> bool:
@ -315,7 +315,7 @@ class Matrix:
] ]
) )
def __mul__(self, other: Matrix | int | float) -> Matrix: def __mul__(self, other: Matrix | float) -> Matrix:
if isinstance(other, (int, float)): if isinstance(other, (int, float)):
return Matrix( return Matrix(
[[int(element * other) for element in row] for row in self.rows] [[int(element * other) for element in row] for row in self.rows]

View File

@ -47,7 +47,7 @@ def subtract(matrix_a: list[list[int]], matrix_b: list[list[int]]) -> list[list[
raise TypeError("Expected a matrix, got int/list instead") raise TypeError("Expected a matrix, got int/list instead")
def scalar_multiply(matrix: list[list[int]], n: int | float) -> list[list[float]]: def scalar_multiply(matrix: list[list[int]], n: float) -> list[list[float]]:
""" """
>>> scalar_multiply([[1,2],[3,4]],5) >>> scalar_multiply([[1,2],[3,4]],5)
[[5, 10], [15, 20]] [[5, 10], [15, 20]]
@ -189,9 +189,7 @@ def main() -> None:
matrix_c = [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [41, 42, 43, 44]] matrix_c = [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [41, 42, 43, 44]]
matrix_d = [[3, 0, 2], [2, 0, -2], [0, 1, 1]] matrix_d = [[3, 0, 2], [2, 0, -2], [0, 1, 1]]
print(f"Add Operation, {add(matrix_a, matrix_b) = } \n") print(f"Add Operation, {add(matrix_a, matrix_b) = } \n")
print( print(f"Multiply Operation, {multiply(matrix_a, matrix_b) = } \n")
f"Multiply Operation, {multiply(matrix_a, matrix_b) = } \n",
)
print(f"Identity: {identity(5)}\n") print(f"Identity: {identity(5)}\n")
print(f"Minor of {matrix_c} = {minor(matrix_c, 1, 2)} \n") print(f"Minor of {matrix_c} = {minor(matrix_c, 1, 2)} \n")
print(f"Determinant of {matrix_b} = {determinant(matrix_b)} \n") print(f"Determinant of {matrix_b} = {determinant(matrix_b)} \n")

View File

@ -1,9 +1,7 @@
from __future__ import annotations from __future__ import annotations
def search_in_a_sorted_matrix( def search_in_a_sorted_matrix(mat: list[list[int]], m: int, n: int, key: float) -> None:
mat: list[list[int]], m: int, n: int, key: int | float
) -> None:
""" """
>>> search_in_a_sorted_matrix( >>> search_in_a_sorted_matrix(
... [[2, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 5) ... [[2, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 5)

View File

@ -22,7 +22,7 @@ class Matrix:
""" """
self.row, self.column = row, column self.row, self.column = row, column
self.array = [[default_value for c in range(column)] for r in range(row)] self.array = [[default_value for _ in range(column)] for _ in range(row)]
def __str__(self) -> str: def __str__(self) -> str:
""" """
@ -54,15 +54,15 @@ class Matrix:
def __repr__(self) -> str: def __repr__(self) -> str:
return str(self) return str(self)
def validate_indicies(self, loc: tuple[int, int]) -> bool: def validate_indices(self, loc: tuple[int, int]) -> bool:
""" """
<method Matrix.validate_indicies> <method Matrix.validate_indicies>
Check if given indices are valid to pick element from matrix. Check if given indices are valid to pick element from matrix.
Example: Example:
>>> a = Matrix(2, 6, 0) >>> a = Matrix(2, 6, 0)
>>> a.validate_indicies((2, 7)) >>> a.validate_indices((2, 7))
False False
>>> a.validate_indicies((0, 0)) >>> a.validate_indices((0, 0))
True True
""" """
if not (isinstance(loc, (list, tuple)) and len(loc) == 2): if not (isinstance(loc, (list, tuple)) and len(loc) == 2):
@ -81,7 +81,7 @@ class Matrix:
>>> a[1, 0] >>> a[1, 0]
7 7
""" """
assert self.validate_indicies(loc) assert self.validate_indices(loc)
return self.array[loc[0]][loc[1]] return self.array[loc[0]][loc[1]]
def __setitem__(self, loc: tuple[int, int], value: float) -> None: def __setitem__(self, loc: tuple[int, int], value: float) -> None:
@ -96,7 +96,7 @@ class Matrix:
[ 1, 1, 1] [ 1, 1, 1]
[ 1, 1, 51] [ 1, 1, 51]
""" """
assert self.validate_indicies(loc) assert self.validate_indices(loc)
self.array[loc[0]][loc[1]] = value self.array[loc[0]][loc[1]] = value
def __add__(self, another: Matrix) -> Matrix: def __add__(self, another: Matrix) -> Matrix:
@ -145,7 +145,7 @@ class Matrix:
def __sub__(self, another: Matrix) -> Matrix: def __sub__(self, another: Matrix) -> Matrix:
return self + (-another) return self + (-another)
def __mul__(self, another: int | float | Matrix) -> Matrix: def __mul__(self, another: float | Matrix) -> Matrix:
""" """
<method Matrix.__mul__> <method Matrix.__mul__>
Return self * another. Return self * another.
@ -233,7 +233,7 @@ class Matrix:
v_t = v.transpose() v_t = v.transpose()
numerator_factor = (v_t * self * u)[0, 0] + 1 numerator_factor = (v_t * self * u)[0, 0] + 1
if numerator_factor == 0: if numerator_factor == 0:
return None # It's not invertable return None # It's not invertible
return self - ((self * u) * (v_t * self) * (1.0 / numerator_factor)) return self - ((self * u) * (v_t * self) * (1.0 / numerator_factor))

View File

@ -263,9 +263,7 @@ def _maybe_download(filename, work_directory, source_url):
return filepath return filepath
@deprecated( @deprecated(None, "Please use alternatives such as: tensorflow_datasets.load('mnist')")
None, "Please use alternatives such as:" " tensorflow_datasets.load('mnist')"
)
def read_data_sets( def read_data_sets(
train_dir, train_dir,
fake_data=False, fake_data=False,

View File

@ -253,7 +253,7 @@ def find_unit_clauses(
unit_symbols = [] unit_symbols = []
for clause in clauses: for clause in clauses:
if len(clause) == 1: if len(clause) == 1:
unit_symbols.append(list(clause.literals.keys())[0]) unit_symbols.append(next(iter(clause.literals.keys())))
else: else:
f_count, n_count = 0, 0 f_count, n_count = 0, 0
for literal, value in clause.literals.items(): for literal, value in clause.literals.items():

View File

@ -1,32 +0,0 @@
from collections.abc import Sequence
def max_subarray_sum(nums: Sequence[int]) -> int:
"""Return the maximum possible sum amongst all non - empty subarrays.
Raises:
ValueError: when nums is empty.
>>> max_subarray_sum([1,2,3,4,-2])
10
>>> max_subarray_sum([-2,1,-3,4,-1,2,1,-5,4])
6
"""
if not nums:
raise ValueError("Input sequence should not be empty")
curr_max = ans = nums[0]
nums_len = len(nums)
for i in range(1, nums_len):
num = nums[i]
curr_max = max(curr_max + num, num)
ans = max(curr_max, ans)
return ans
if __name__ == "__main__":
n = int(input("Enter number of elements : ").strip())
array = list(map(int, input("\nEnter the numbers : ").strip().split()))[:n]
print(max_subarray_sum(array))

View File

@ -1,6 +1,6 @@
""" """
A number container system that uses binary search to delete and insert values into A number container system that uses binary search to delete and insert values into
arrays with O(n logn) write times and O(1) read times. arrays with O(log n) write times and O(1) read times.
This container system holds integers at indexes. This container system holds integers at indexes.

View File

@ -0,0 +1,52 @@
"""
Title : Calculate altitude using Pressure
Description :
The below algorithm approximates the altitude using Barometric formula
"""
def get_altitude_at_pressure(pressure: float) -> float:
"""
This method calculates the altitude from Pressure wrt to
Sea level pressure as reference .Pressure is in Pascals
https://en.wikipedia.org/wiki/Pressure_altitude
https://community.bosch-sensortec.com/t5/Question-and-answers/How-to-calculate-the-altitude-from-the-pressure-sensor-data/qaq-p/5702
H = 44330 * [1 - (P/p0)^(1/5.255) ]
Where :
H = altitude (m)
P = measured pressure
p0 = reference pressure at sea level 101325 Pa
Examples:
>>> get_altitude_at_pressure(pressure=100_000)
105.47836610778828
>>> get_altitude_at_pressure(pressure=101_325)
0.0
>>> get_altitude_at_pressure(pressure=80_000)
1855.873388064995
>>> get_altitude_at_pressure(pressure=201_325)
Traceback (most recent call last):
...
ValueError: Value Higher than Pressure at Sea Level !
>>> get_altitude_at_pressure(pressure=-80_000)
Traceback (most recent call last):
...
ValueError: Atmospheric Pressure can not be negative !
"""
if pressure > 101325:
raise ValueError("Value Higher than Pressure at Sea Level !")
if pressure < 0:
raise ValueError("Atmospheric Pressure can not be negative !")
return 44_330 * (1 - (pressure / 101_325) ** (1 / 5.5255))
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -0,0 +1,178 @@
from math import pow, sqrt
from scipy.constants import G, c, pi
"""
These two functions will return the radii of impact for a target object
of mass M and radius R as well as it's effective cross sectional area σ(sigma).
That is to say any projectile with velocity v passing within σ, will impact the
target object with mass M. The derivation of which is given at the bottom
of this file.
The derivation shows that a projectile does not need to aim directly at the target
body in order to hit it, as R_capture>R_target. Astronomers refer to the effective
cross section for capture as σ=π*R_capture**2.
This algorithm does not account for an N-body problem.
"""
def capture_radii(
target_body_radius: float, target_body_mass: float, projectile_velocity: float
) -> float:
"""
Input Params:
-------------
target_body_radius: Radius of the central body SI units: meters | m
target_body_mass: Mass of the central body SI units: kilograms | kg
projectile_velocity: Velocity of object moving toward central body
SI units: meters/second | m/s
Returns:
--------
>>> capture_radii(6.957e8, 1.99e30, 25000.0)
17209590691.0
>>> capture_radii(-6.957e8, 1.99e30, 25000.0)
Traceback (most recent call last):
...
ValueError: Radius cannot be less than 0
>>> capture_radii(6.957e8, -1.99e30, 25000.0)
Traceback (most recent call last):
...
ValueError: Mass cannot be less than 0
>>> capture_radii(6.957e8, 1.99e30, c+1)
Traceback (most recent call last):
...
ValueError: Cannot go beyond speed of light
Returned SI units:
------------------
meters | m
"""
if target_body_mass < 0:
raise ValueError("Mass cannot be less than 0")
if target_body_radius < 0:
raise ValueError("Radius cannot be less than 0")
if projectile_velocity > c:
raise ValueError("Cannot go beyond speed of light")
escape_velocity_squared = (2 * G * target_body_mass) / target_body_radius
capture_radius = target_body_radius * sqrt(
1 + escape_velocity_squared / pow(projectile_velocity, 2)
)
return round(capture_radius, 0)
def capture_area(capture_radius: float) -> float:
"""
Input Param:
------------
capture_radius: The radius of orbital capture and impact for a central body of
mass M and a projectile moving towards it with velocity v
SI units: meters | m
Returns:
--------
>>> capture_area(17209590691)
9.304455331329126e+20
>>> capture_area(-1)
Traceback (most recent call last):
...
ValueError: Cannot have a capture radius less than 0
Returned SI units:
------------------
meters*meters | m**2
"""
if capture_radius < 0:
raise ValueError("Cannot have a capture radius less than 0")
sigma = pi * pow(capture_radius, 2)
return round(sigma, 0)
if __name__ == "__main__":
from doctest import testmod
testmod()
"""
Derivation:
Let: Mt=target mass, Rt=target radius, v=projectile_velocity,
r_0=radius of projectile at instant 0 to CM of target
v_p=v at closest approach,
r_p=radius from projectile to target CM at closest approach,
R_capture= radius of impact for projectile with velocity v
(1)At time=0 the projectile's energy falling from infinity| E=K+U=0.5*m*(v**2)+0
E_initial=0.5*m*(v**2)
(2)at time=0 the angular momentum of the projectile relative to CM target|
L_initial=m*r_0*v*sin(Θ)->m*r_0*v*(R_capture/r_0)->m*v*R_capture
L_i=m*v*R_capture
(3)The energy of the projectile at closest approach will be its kinetic energy
at closest approach plus gravitational potential energy(-(GMm)/R)|
E_p=K_p+U_p->E_p=0.5*m*(v_p**2)-(G*Mt*m)/r_p
E_p=0.0.5*m*(v_p**2)-(G*Mt*m)/r_p
(4)The angular momentum of the projectile relative to the target at closest
approach will be L_p=m*r_p*v_p*sin(Θ), however relative to the target Θ=90°
sin(90°)=1|
L_p=m*r_p*v_p
(5)Using conservation of angular momentum and energy, we can write a quadratic
equation that solves for r_p|
(a)
Ei=Ep-> 0.5*m*(v**2)=0.5*m*(v_p**2)-(G*Mt*m)/r_p-> v**2=v_p**2-(2*G*Mt)/r_p
(b)
Li=Lp-> m*v*R_capture=m*r_p*v_p-> v*R_capture=r_p*v_p-> v_p=(v*R_capture)/r_p
(c) b plugs int a|
v**2=((v*R_capture)/r_p)**2-(2*G*Mt)/r_p->
v**2-(v**2)*(R_c**2)/(r_p**2)+(2*G*Mt)/r_p=0->
(v**2)*(r_p**2)+2*G*Mt*r_p-(v**2)*(R_c**2)=0
(d) Using the quadratic formula, we'll solve for r_p then rearrange to solve to
R_capture
r_p=(-2*G*Mt ± sqrt(4*G^2*Mt^2+ 4(v^4*R_c^2)))/(2*v^2)->
r_p=(-G*Mt ± sqrt(G^2*Mt+v^4*R_c^2))/v^2->
r_p<0 is something we can ignore, as it has no physical meaning for our purposes.->
r_p=(-G*Mt)/v^2 + sqrt(G^2*Mt^2/v^4 + R_c^2)
(e)We are trying to solve for R_c. We are looking for impact, so we want r_p=Rt
Rt + G*Mt/v^2 = sqrt(G^2*Mt^2/v^4 + R_c^2)->
(Rt + G*Mt/v^2)^2 = G^2*Mt^2/v^4 + R_c^2->
Rt^2 + 2*G*Mt*Rt/v^2 + G^2*Mt^2/v^4 = G^2*Mt^2/v^4 + R_c^2->
Rt**2 + 2*G*Mt*Rt/v**2 = R_c**2->
Rt**2 * (1 + 2*G*Mt/Rt *1/v**2) = R_c**2->
escape velocity = sqrt(2GM/R)= v_escape**2=2GM/R->
Rt**2 * (1 + v_esc**2/v**2) = R_c**2->
(6)
R_capture = Rt * sqrt(1 + v_esc**2/v**2)
Source: Problem Set 3 #8 c.Fall_2017|Honors Astronomy|Professor Rachel Bezanson
Source #2: http://www.nssc.ac.cn/wxzygx/weixin/201607/P020160718380095698873.pdf
8.8 Planetary Rendezvous: Pg.368
"""

View File

@ -53,6 +53,40 @@ def volume_of_gas_system(moles: float, kelvin: float, pressure: float) -> float:
return moles * kelvin * UNIVERSAL_GAS_CONSTANT / pressure return moles * kelvin * UNIVERSAL_GAS_CONSTANT / pressure
def temperature_of_gas_system(moles: float, volume: float, pressure: float) -> float:
"""
>>> temperature_of_gas_system(2, 100, 5)
30.068090996146232
>>> temperature_of_gas_system(11, 5009, 1000)
54767.66101807144
>>> temperature_of_gas_system(3, -0.46, 23.5)
Traceback (most recent call last):
...
ValueError: Invalid inputs. Enter positive value.
"""
if moles < 0 or volume < 0 or pressure < 0:
raise ValueError("Invalid inputs. Enter positive value.")
return pressure * volume / (moles * UNIVERSAL_GAS_CONSTANT)
def moles_of_gas_system(kelvin: float, volume: float, pressure: float) -> float:
"""
>>> moles_of_gas_system(100, 5, 10)
0.06013618199229246
>>> moles_of_gas_system(110, 5009, 1000)
5476.766101807144
>>> moles_of_gas_system(3, -0.46, 23.5)
Traceback (most recent call last):
...
ValueError: Invalid inputs. Enter positive value.
"""
if kelvin < 0 or volume < 0 or pressure < 0:
raise ValueError("Invalid inputs. Enter positive value.")
return pressure * volume / (kelvin * UNIVERSAL_GAS_CONSTANT)
if __name__ == "__main__": if __name__ == "__main__":
from doctest import testmod from doctest import testmod

View File

@ -60,7 +60,7 @@ def newtons_second_law_of_motion(mass: float, acceleration: float) -> float:
>>> newtons_second_law_of_motion(2.0, 1) >>> newtons_second_law_of_motion(2.0, 1)
2.0 2.0
""" """
force = float() force = 0.0
try: try:
force = mass * acceleration force = mass * acceleration
except Exception: except Exception:

View File

@ -28,12 +28,16 @@ def solution() -> int:
31875000 31875000
""" """
return [ return next(
a * b * (1000 - a - b) iter(
for a in range(1, 999) [
for b in range(a, 999) a * b * (1000 - a - b)
if (a * a + b * b == (1000 - a - b) ** 2) for a in range(1, 999)
][0] for b in range(a, 999)
if (a * a + b * b == (1000 - a - b) ** 2)
]
)
)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -49,6 +49,7 @@ select = [ # https://beta.ruff.rs/docs/rules
"ICN", # flake8-import-conventions "ICN", # flake8-import-conventions
"INP", # flake8-no-pep420 "INP", # flake8-no-pep420
"INT", # flake8-gettext "INT", # flake8-gettext
"ISC", # flake8-implicit-str-concat
"N", # pep8-naming "N", # pep8-naming
"NPY", # NumPy-specific rules "NPY", # NumPy-specific rules
"PGH", # pygrep-hooks "PGH", # pygrep-hooks
@ -72,7 +73,6 @@ select = [ # https://beta.ruff.rs/docs/rules
# "DJ", # flake8-django # "DJ", # flake8-django
# "ERA", # eradicate -- DO NOT FIX # "ERA", # eradicate -- DO NOT FIX
# "FBT", # flake8-boolean-trap # FIX ME # "FBT", # flake8-boolean-trap # FIX ME
# "ISC", # flake8-implicit-str-concat # FIX ME
# "PD", # pandas-vet # "PD", # pandas-vet
# "PT", # flake8-pytest-style # "PT", # flake8-pytest-style
# "PTH", # flake8-use-pathlib # FIX ME # "PTH", # flake8-use-pathlib # FIX ME

View File

@ -107,7 +107,7 @@ def ripple_adder(
res = qiskit.execute(circuit, backend, shots=1).result() res = qiskit.execute(circuit, backend, shots=1).result()
# The result is in binary. Convert it back to int # The result is in binary. Convert it back to int
return int(list(res.get_counts())[0], 2) return int(next(iter(res.get_counts())), 2)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -9,6 +9,7 @@ pandas
pillow pillow
projectq projectq
qiskit qiskit
qiskit-aer
requests requests
rich rich
scikit-fuzzy scikit-fuzzy

View File

@ -4,14 +4,28 @@ This algorithm iterates through a sorted collection with a step of n^(1/2),
until the element compared is bigger than the one searched. until the element compared is bigger than the one searched.
It will then perform a linear search until it matches the wanted number. It will then perform a linear search until it matches the wanted number.
If not found, it returns -1. If not found, it returns -1.
https://en.wikipedia.org/wiki/Jump_search
""" """
import math import math
from collections.abc import Sequence
from typing import Any, Protocol, TypeVar
def jump_search(arr: list, x: int) -> int: class Comparable(Protocol):
def __lt__(self, other: Any, /) -> bool:
...
T = TypeVar("T", bound=Comparable)
def jump_search(arr: Sequence[T], item: T) -> int:
""" """
Pure Python implementation of the jump search algorithm. Python implementation of the jump search algorithm.
Return the index if the `item` is found, otherwise return -1.
Examples: Examples:
>>> jump_search([0, 1, 2, 3, 4, 5], 3) >>> jump_search([0, 1, 2, 3, 4, 5], 3)
3 3
@ -21,31 +35,36 @@ def jump_search(arr: list, x: int) -> int:
-1 -1
>>> jump_search([0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610], 55) >>> jump_search([0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610], 55)
10 10
>>> jump_search(["aa", "bb", "cc", "dd", "ee", "ff"], "ee")
4
""" """
n = len(arr) arr_size = len(arr)
step = int(math.floor(math.sqrt(n))) block_size = int(math.sqrt(arr_size))
prev = 0 prev = 0
while arr[min(step, n) - 1] < x: step = block_size
while arr[min(step, arr_size) - 1] < item:
prev = step prev = step
step += int(math.floor(math.sqrt(n))) step += block_size
if prev >= n: if prev >= arr_size:
return -1 return -1
while arr[prev] < x: while arr[prev] < item:
prev = prev + 1 prev += 1
if prev == min(step, n): if prev == min(step, arr_size):
return -1 return -1
if arr[prev] == x: if arr[prev] == item:
return prev return prev
return -1 return -1
if __name__ == "__main__": if __name__ == "__main__":
user_input = input("Enter numbers separated by a comma:\n").strip() user_input = input("Enter numbers separated by a comma:\n").strip()
arr = [int(item) for item in user_input.split(",")] array = [int(item) for item in user_input.split(",")]
x = int(input("Enter the number to be searched:\n")) x = int(input("Enter the number to be searched:\n"))
res = jump_search(arr, x)
res = jump_search(array, x)
if res == -1: if res == -1:
print("Number not found!") print("Number not found!")
else: else:

View File

@ -15,7 +15,7 @@ def linear_search(sequence: list, target: int) -> int:
:param sequence: a collection with comparable items (as sorted items not required :param sequence: a collection with comparable items (as sorted items not required
in Linear Search) in Linear Search)
:param target: item value to search :param target: item value to search
:return: index of found item or None if item is not found :return: index of found item or -1 if item is not found
Examples: Examples:
>>> linear_search([0, 5, 7, 10, 15], 0) >>> linear_search([0, 5, 7, 10, 15], 0)

View File

@ -1,4 +1,7 @@
def bubble_sort(collection): from typing import Any
def bubble_sort(collection: list[Any]) -> list[Any]:
"""Pure implementation of bubble sort algorithm in Python """Pure implementation of bubble sort algorithm in Python
:param collection: some mutable ordered collection with heterogeneous :param collection: some mutable ordered collection with heterogeneous
@ -28,9 +31,9 @@ def bubble_sort(collection):
True True
""" """
length = len(collection) length = len(collection)
for i in range(length - 1): for i in reversed(range(length)):
swapped = False swapped = False
for j in range(length - 1 - i): for j in range(i):
if collection[j] > collection[j + 1]: if collection[j] > collection[j + 1]:
swapped = True swapped = True
collection[j], collection[j + 1] = collection[j + 1], collection[j] collection[j], collection[j + 1] = collection[j + 1], collection[j]

View File

@ -22,9 +22,7 @@ def is_sri_lankan_phone_number(phone: str) -> bool:
False False
""" """
pattern = re.compile( pattern = re.compile(r"^(?:0|94|\+94|0{2}94)7(0|1|2|4|5|6|7|8)(-| |)\d{7}$")
r"^(?:0|94|\+94|0{2}94)" r"7(0|1|2|4|5|6|7|8)" r"(-| |)" r"\d{7}$"
)
return bool(re.search(pattern, phone)) return bool(re.search(pattern, phone))

View File

@ -0,0 +1,117 @@
"""
Implements an is valid email address algorithm
@ https://en.wikipedia.org/wiki/Email_address
"""
import string
email_tests: tuple[tuple[str, bool], ...] = (
("simple@example.com", True),
("very.common@example.com", True),
("disposable.style.email.with+symbol@example.com", True),
("other-email-with-hyphen@and.subdomains.example.com", True),
("fully-qualified-domain@example.com", True),
("user.name+tag+sorting@example.com", True),
("x@example.com", True),
("example-indeed@strange-example.com", True),
("test/test@test.com", True),
(
"123456789012345678901234567890123456789012345678901234567890123@example.com",
True,
),
("admin@mailserver1", True),
("example@s.example", True),
("Abc.example.com", False),
("A@b@c@example.com", False),
("abc@example..com", False),
("a(c)d,e:f;g<h>i[j\\k]l@example.com", False),
(
"12345678901234567890123456789012345678901234567890123456789012345@example.com",
False,
),
("i.like.underscores@but_its_not_allowed_in_this_part", False),
("", False),
)
# The maximum octets (one character as a standard unicode character is one byte)
# that the local part and the domain part can have
MAX_LOCAL_PART_OCTETS = 64
MAX_DOMAIN_OCTETS = 255
def is_valid_email_address(email: str) -> bool:
"""
Returns True if the passed email address is valid.
The local part of the email precedes the singular @ symbol and
is associated with a display-name. For example, "john.smith"
The domain is stricter than the local part and follows the @ symbol.
Global email checks:
1. There can only be one @ symbol in the email address. Technically if the
@ symbol is quoted in the local-part, then it is valid, however this
implementation ignores "" for now.
(See https://en.wikipedia.org/wiki/Email_address#:~:text=If%20quoted,)
2. The local-part and the domain are limited to a certain number of octets. With
unicode storing a single character in one byte, each octet is equivalent to
a character. Hence, we can just check the length of the string.
Checks for the local-part:
3. The local-part may contain: upper and lowercase latin letters, digits 0 to 9,
and printable characters (!#$%&'*+-/=?^_`{|}~)
4. The local-part may also contain a "." in any place that is not the first or
last character, and may not have more than one "." consecutively.
Checks for the domain:
5. The domain may contain: upper and lowercase latin letters and digits 0 to 9
6. Hyphen "-", provided that it is not the first or last character
7. The domain may also contain a "." in any place that is not the first or
last character, and may not have more than one "." consecutively.
>>> for email, valid in email_tests:
... assert is_valid_email_address(email) == valid
"""
# (1.) Make sure that there is only one @ symbol in the email address
if email.count("@") != 1:
return False
local_part, domain = email.split("@")
# (2.) Check octet length of the local part and domain
if len(local_part) > MAX_LOCAL_PART_OCTETS or len(domain) > MAX_DOMAIN_OCTETS:
return False
# (3.) Validate the characters in the local-part
if any(
char not in string.ascii_letters + string.digits + ".(!#$%&'*+-/=?^_`{|}~)"
for char in local_part
):
return False
# (4.) Validate the placement of "." characters in the local-part
if local_part.startswith(".") or local_part.endswith(".") or ".." in local_part:
return False
# (5.) Validate the characters in the domain
if any(char not in string.ascii_letters + string.digits + ".-" for char in domain):
return False
# (6.) Validate the placement of "-" characters
if domain.startswith("-") or domain.endswith("."):
return False
# (7.) Validate the placement of "." characters
if domain.startswith(".") or domain.endswith(".") or ".." in domain:
return False
return True
if __name__ == "__main__":
import doctest
doctest.testmod()
for email, valid in email_tests:
is_valid = is_valid_email_address(email)
assert is_valid == valid, f"{email} is {is_valid}"
print(f"Email address {email} is {'not ' if not is_valid else ''}valid")

View File

@ -61,7 +61,7 @@ def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]:
if i == 0 and j == 0: if i == 0 and j == 0:
return [] return []
else: else:
if ops[i][j][0] == "C" or ops[i][j][0] == "R": if ops[i][j][0] in {"C", "R"}:
seq = assemble_transformation(ops, i - 1, j - 1) seq = assemble_transformation(ops, i - 1, j - 1)
seq.append(ops[i][j]) seq.append(ops[i][j])
return seq return seq

View File

@ -90,9 +90,7 @@ def convert(number: int) -> str:
else: else:
addition = "" addition = ""
if counter in placevalue: if counter in placevalue:
if current == 0 and ((temp_num % 100) // 10) == 0: if current != 0 and ((temp_num % 100) // 10) != 0:
addition = ""
else:
addition = placevalue[counter] addition = placevalue[counter]
if ((temp_num % 100) // 10) == 1: if ((temp_num % 100) // 10) == 1:
words = teens[current] + addition + words words = teens[current] + addition + words

View File

@ -4,17 +4,21 @@ This is to show simple COVID19 info fetching from worldometers site using lxml
more convenient to use in Python web projects (e.g. Django or Flask-based) more convenient to use in Python web projects (e.g. Django or Flask-based)
""" """
from collections import namedtuple from typing import NamedTuple
import requests import requests
from lxml import html # type: ignore from lxml import html # type: ignore
covid_data = namedtuple("covid_data", "cases deaths recovered")
class CovidData(NamedTuple):
cases: int
deaths: int
recovered: int
def covid_stats(url: str = "https://www.worldometers.info/coronavirus/") -> covid_data: def covid_stats(url: str = "https://www.worldometers.info/coronavirus/") -> CovidData:
xpath_str = '//div[@class = "maincounter-number"]/span/text()' xpath_str = '//div[@class = "maincounter-number"]/span/text()'
return covid_data(*html.fromstring(requests.get(url).content).xpath(xpath_str)) return CovidData(*html.fromstring(requests.get(url).content).xpath(xpath_str))
fmt = """Total COVID-19 cases in the world: {} fmt = """Total COVID-19 cases in the world: {}

View File

@ -3,12 +3,18 @@ from bs4 import BeautifulSoup
def stock_price(symbol: str = "AAPL") -> str: def stock_price(symbol: str = "AAPL") -> str:
url = f"https://in.finance.yahoo.com/quote/{symbol}?s={symbol}" url = f"https://finance.yahoo.com/quote/{symbol}?p={symbol}"
soup = BeautifulSoup(requests.get(url).text, "html.parser") yahoo_finance_source = requests.get(url, headers={"USER-AGENT": "Mozilla/5.0"}).text
class_ = "My(6px) Pos(r) smartphone_Mt(6px)" soup = BeautifulSoup(yahoo_finance_source, "html.parser")
return soup.find("div", class_=class_).find("span").text specific_fin_streamer_tag = soup.find("fin-streamer", {"data-test": "qsp-price"})
if specific_fin_streamer_tag:
text = specific_fin_streamer_tag.get_text()
return text
return "No <fin-streamer> tag with the specified data-test attribute found."
# Search for the symbol at https://finance.yahoo.com/lookup
if __name__ == "__main__": if __name__ == "__main__":
for symbol in "AAPL AMZN IBM GOOG MSFT ORCL".split(): for symbol in "AAPL AMZN IBM GOOG MSFT ORCL".split():
print(f"Current {symbol:<4} stock price is {stock_price(symbol):>8}") print(f"Current {symbol:<4} stock price is {stock_price(symbol):>8}")

View File

@ -22,6 +22,5 @@ def world_covid19_stats(url: str = "https://www.worldometers.info/coronavirus")
if __name__ == "__main__": if __name__ == "__main__":
print("\033[1m" + "COVID-19 Status of the World" + "\033[0m\n") print("\033[1m COVID-19 Status of the World \033[0m\n")
for key, value in world_covid19_stats().items(): print("\n".join(f"{key}\n{value}" for key, value in world_covid19_stats().items()))
print(f"{key}\n{value}\n")