Compare commits

...

25 Commits

Author SHA1 Message Date
BamaCharanChhandogi
3040f99707 remove comment 2023-08-17 09:59:25 +05:30
BamaCharanChhandogi
8be37da62a Merge branch 'OctalToBinary' of https://github.com/BamaCharanChhandogi/Python into OctalToBinary 2023-08-17 09:54:53 +05:30
Bama Charan Chhandogi
33f7f8e78d
Merge branch 'TheAlgorithms:master' into OctalToBinary 2023-08-17 09:54:48 +05:30
BamaCharanChhandogi
0f3e4bb3c4 Merge branch 'OctalToBinary' of https://github.com/BamaCharanChhandogi/Python into OctalToBinary 2023-08-17 09:54:27 +05:30
Kausthub Kannan
f6b12420ce
Added Leaky ReLU Activation Function (#8962)
* Added Leaky ReLU activation function

* Added Leaky ReLU activation function

* Added Leaky ReLU activation function

* Formatting and spelling fixes done
2023-08-16 18:22:15 -07:00
Caeden Perelli-Harris
fd7cc4cf8e
Rename norgate to nor_gate to keep consistency (#8968)
* refactor(boolean-algebra): Rename norgate to nor_gate

* updating DIRECTORY.md

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
2023-08-16 18:21:00 -07:00
Tianyi Zheng
beb43517c3
Fix mypy errors in maths/gaussian_error_linear_unit.py (#8610)
* updating DIRECTORY.md

* Fix mypy errors in gaussian_error_linear_unit.py

* updating DIRECTORY.md

* updating DIRECTORY.md

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
2023-08-16 07:36:10 -04:00
homsim
5c276a8377
Quick fix: fig.canvas.set_window_title deprecated (#8961)
Co-authored-by: homsim <simon@simon-ThinkPadE460.fritz.box>
2023-08-16 01:07:50 -07:00
Saksham1970
bfed2fb788
Added Continued fractions (#6846)
* updating DIRECTORY.md

* added continued fractions

* updating DIRECTORY.md

* Update maths/continued_fraction.py

Co-authored-by: Caeden Perelli-Harris <caedenperelliharris@gmail.com>

* Update maths/continued_fraction.py

Co-authored-by: Caeden Perelli-Harris <caedenperelliharris@gmail.com>

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: Caeden Perelli-Harris <caedenperelliharris@gmail.com>
Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
2023-08-16 00:24:12 -07:00
Maxim Smolskiy
f66568e981
Reduce the complexity of boolean_algebra/quine_mc_cluskey.py (#8604)
* Reduce the complexity of boolean_algebra/quine_mc_cluskey.py

* updating DIRECTORY.md

* Fix

* Fix review issues

* Fix

* Fix review issues

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
2023-08-15 16:10:22 -07:00
isidroas
efaf526737
BST and RSA doctest (#8693)
* rsa key doctest

* move doctest to module docstring

* all tests to doctest

* moved is_right to property

* is right test

* fixed rsa doctest import

* Test error when deleting non-existing element

* fixing ruff EM102

* convert property 'is_right' to one-liner

Also use 'is' instead of '=='

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* child instead of children

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* remove type hint

* Update data_structures/binary_tree/binary_search_tree.py

---------

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
2023-08-15 16:04:53 -07:00
Juyoung Kim
cecf1fdd52
Fix greedy_best_first (#8775)
* fix: typo
#8770

* refactor: delete unnecessary continue

* add test grids

* fix: add \_\_eq\_\_ in Node class
#8770

* fix: delete unnecessary code
- node in self.open_nodes is always better node
#8770

* fix: docstring

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix: docstring max length

* refactor: get the successors using a list comprehension

* Apply suggestions from code review

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
2023-08-15 15:52:51 -07:00
Caeden Perelli-Harris
490e645ed3
Fix minor typing errors in maths/ (#8959)
* updating DIRECTORY.md

* types(maths): Fix pylance issues in maths

* reset(vsc): Reset settings changes

* Update maths/jaccard_similarity.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* revert(erosion_operation): Revert erosion_operation

* test(jaccard_similarity): Add doctest to test alternative_union

* types(newton_raphson): Add typehints to func bodies

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
2023-08-15 14:27:41 -07:00
Erfan Alimohammadi
7618a92fee
Remove a slash in path to save the file correctly on Linux (#8053) 2023-08-15 16:37:49 -04:00
pre-commit-ci[bot]
7021afda04
[pre-commit.ci] pre-commit autoupdate (#8963)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.0.282 → v0.0.284](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.282...v0.0.284)
- [github.com/tox-dev/pyproject-fmt: 0.13.0 → 0.13.1](https://github.com/tox-dev/pyproject-fmt/compare/0.13.0...0.13.1)
- [github.com/pre-commit/mirrors-mypy: v1.4.1 → v1.5.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.4.1...v1.5.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-08-14 23:12:11 -04:00
Caeden Perelli-Harris
fb1b939a89
Consolidate find_min and find_min recursive and find_max and find_max_recursive (#8960)
* updating DIRECTORY.md

* refactor(min-max): Consolidate implementations

* updating DIRECTORY.md

* refactor(min-max): Append _iterative to func name

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
2023-08-14 04:17:27 -07:00
robertjcalistri
2ab3bf2689
Added functions to calculate temperature of an ideal gas and number o… (#8919)
* Added functions to calculate temperature of an ideal gas and number of moles of an ideal gas

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update physics/ideal_gas_law.py

Renamed function name

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update physics/ideal_gas_law.py

Updated formatting

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update physics/ideal_gas_law.py

Removed unnecessary parentheses

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update physics/ideal_gas_law.py

Removed unnecessary parentheses

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update ideal_gas_law.py

Updated incorrect function calls moles of gas system doctests

* Update physics/ideal_gas_law.py

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
2023-08-14 02:31:53 -07:00
Adithya Awati
ac68dc1128
Fixed Pytest warnings for machine_learning/forecasting (#8958)
* updating DIRECTORY.md

* Fixed pyTest Warnings

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
2023-08-14 01:34:16 -07:00
Caeden Perelli-Harris
4b7ecb6a81
Create is valid email address algorithm (#8907)
* feat(strings): Create is valid email address

* updating DIRECTORY.md

* feat(strings): Create is_valid_email_address algorithm

* chore(is_valid_email_address): Implement changes from code review

* Update strings/is_valid_email_address.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* chore(is_valid_email_address): Fix ruff error

* Update strings/is_valid_email_address.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-08-14 01:28:52 -07:00
Adithya Awati
c290dd6a43
Update run.py in machine_learning/forecasting (#8957)
* Fixed reading CSV file, added type check for data_safety_checker function

* Formatted run.py

* updating DIRECTORY.md

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
2023-08-14 00:16:24 -07:00
Ajinkya Chikhale
02d89bde67
Added implementation for Tribonacci sequence using dp (#6356)
* Added implementation for Tribonacci sequence using dp

* Updated parameter name

* Apply suggestions from code review

---------

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
2023-08-14 00:12:42 -07:00
Amir Hosseini
f24ab2c60d
Add: Two Regex match algorithm (Recursive & DP) (#6321)
* Add recursive solution to regex_match.py

* Add dp solution to regex_match.py

* Add link to regex_match.py

* Minor edit

* Minor change

* Minor change

* Update dynamic_programming/regex_match.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update dynamic_programming/regex_match.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Fix ruff formatting in if statements

* Update dynamic_programming/regex_match.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-08-13 22:37:41 -07:00
Caeden Perelli-Harris
9d86d4edaa
Create wa-tor algorithm (#8899)
* feat(cellular_automata): Create wa-tor algorithm

* updating DIRECTORY.md

* chore(quality): Implement algo-keeper bot changes

* Update cellular_automata/wa_tor.py

Co-authored-by: Christian Clauss <cclauss@me.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* refactor(repr): Return repr as python object

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* Update cellular_automata/wa_tor.py

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>

* refactor(display): Rename to display_visually to visualise

* refactor(wa-tor): Use double for loop

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* chore(wa-tor): Implement suggestions from code review

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: Christian Clauss <cclauss@me.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
2023-08-13 17:58:17 -07:00
Maxim Smolskiy
4f2a346c27
Reduce the complexity of linear_algebra/src/polynom_for_points.py (#8605)
* Reduce the complexity of linear_algebra/src/polynom_for_points.py

* updating DIRECTORY.md

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix

* Fix review issues

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-08-13 03:05:42 -07:00
BamaCharanChhandogi
7b0ac718b0 mentioned return type 2023-08-13 15:12:42 +05:30
34 changed files with 1337 additions and 409 deletions

View File

@ -16,7 +16,7 @@ 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.282 rev: v0.0.284
hooks: hooks:
- id: ruff - id: ruff
@ -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.13.0" 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

@ -62,7 +62,7 @@
## Boolean Algebra ## Boolean Algebra
* [And Gate](boolean_algebra/and_gate.py) * [And Gate](boolean_algebra/and_gate.py)
* [Nand Gate](boolean_algebra/nand_gate.py) * [Nand Gate](boolean_algebra/nand_gate.py)
* [Norgate](boolean_algebra/norgate.py) * [Nor Gate](boolean_algebra/nor_gate.py)
* [Not Gate](boolean_algebra/not_gate.py) * [Not Gate](boolean_algebra/not_gate.py)
* [Or Gate](boolean_algebra/or_gate.py) * [Or Gate](boolean_algebra/or_gate.py)
* [Quine Mc Cluskey](boolean_algebra/quine_mc_cluskey.py) * [Quine Mc Cluskey](boolean_algebra/quine_mc_cluskey.py)
@ -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)
@ -335,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)
@ -552,6 +555,7 @@
* [Chudnovsky Algorithm](maths/chudnovsky_algorithm.py) * [Chudnovsky Algorithm](maths/chudnovsky_algorithm.py)
* [Collatz Sequence](maths/collatz_sequence.py) * [Collatz Sequence](maths/collatz_sequence.py)
* [Combinations](maths/combinations.py) * [Combinations](maths/combinations.py)
* [Continued Fraction](maths/continued_fraction.py)
* [Decimal Isolate](maths/decimal_isolate.py) * [Decimal Isolate](maths/decimal_isolate.py)
* [Decimal To Fraction](maths/decimal_to_fraction.py) * [Decimal To Fraction](maths/decimal_to_fraction.py)
* [Dodecahedron](maths/dodecahedron.py) * [Dodecahedron](maths/dodecahedron.py)
@ -570,9 +574,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)
@ -1169,6 +1171,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

@ -74,10 +74,7 @@ def is_for_table(string1: str, string2: str, count: int) -> bool:
""" """
list1 = list(string1) list1 = list(string1)
list2 = list(string2) list2 = list(string2)
count_n = 0 count_n = sum(item1 != item2 for item1, item2 in zip(list1, list2))
for i in range(len(list1)):
if list1[i] != list2[i]:
count_n += 1
return count_n == count return count_n == count
@ -92,40 +89,34 @@ def selection(chart: list[list[int]], prime_implicants: list[str]) -> list[str]:
temp = [] temp = []
select = [0] * len(chart) select = [0] * len(chart)
for i in range(len(chart[0])): for i in range(len(chart[0])):
count = 0 count = sum(row[i] == 1 for row in chart)
rem = -1
for j in range(len(chart)):
if chart[j][i] == 1:
count += 1
rem = j
if count == 1: if count == 1:
rem = max(j for j, row in enumerate(chart) if row[i] == 1)
select[rem] = 1 select[rem] = 1
for i in range(len(select)): for i, item in enumerate(select):
if select[i] == 1: if item != 1:
continue
for j in range(len(chart[0])): for j in range(len(chart[0])):
if chart[i][j] == 1: if chart[i][j] != 1:
for k in range(len(chart)): continue
chart[k][j] = 0 for row in chart:
row[j] = 0
temp.append(prime_implicants[i]) temp.append(prime_implicants[i])
while True: while True:
max_n = 0 counts = [chart[i].count(1) for i in range(len(chart))]
rem = -1 max_n = max(counts)
count_n = 0 rem = counts.index(max_n)
for i in range(len(chart)):
count_n = chart[i].count(1)
if count_n > max_n:
max_n = count_n
rem = i
if max_n == 0: if max_n == 0:
return temp return temp
temp.append(prime_implicants[rem]) temp.append(prime_implicants[rem])
for i in range(len(chart[0])): for j in range(len(chart[0])):
if chart[rem][i] == 1: if chart[rem][j] != 1:
for j in range(len(chart)): continue
chart[j][i] = 0 for i in range(len(chart)):
chart[i][j] = 0
def prime_implicant_chart( def prime_implicant_chart(

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

@ -2,8 +2,7 @@ import os
import random import random
import sys import sys
from . import cryptomath_module as cryptoMath # noqa: N812 from . import cryptomath_module, rabin_miller
from . import rabin_miller as rabinMiller # noqa: N812
def main() -> None: def main() -> None:
@ -13,20 +12,26 @@ def main() -> None:
def generate_key(key_size: int) -> tuple[tuple[int, int], tuple[int, int]]: def generate_key(key_size: int) -> tuple[tuple[int, int], tuple[int, int]]:
print("Generating prime p...") """
p = rabinMiller.generate_large_prime(key_size) >>> random.seed(0) # for repeatability
print("Generating prime q...") >>> public_key, private_key = generate_key(8)
q = rabinMiller.generate_large_prime(key_size) >>> public_key
(26569, 239)
>>> private_key
(26569, 2855)
"""
p = rabin_miller.generate_large_prime(key_size)
q = rabin_miller.generate_large_prime(key_size)
n = p * q n = p * q
print("Generating e that is relatively prime to (p - 1) * (q - 1)...") # Generate e that is relatively prime to (p - 1) * (q - 1)
while True: while True:
e = random.randrange(2 ** (key_size - 1), 2 ** (key_size)) e = random.randrange(2 ** (key_size - 1), 2 ** (key_size))
if cryptoMath.gcd(e, (p - 1) * (q - 1)) == 1: if cryptomath_module.gcd(e, (p - 1) * (q - 1)) == 1:
break break
print("Calculating d that is mod inverse of e...") # Calculate d that is mod inverse of e
d = cryptoMath.find_mod_inverse(e, (p - 1) * (q - 1)) d = cryptomath_module.find_mod_inverse(e, (p - 1) * (q - 1))
public_key = (n, e) public_key = (n, e)
private_key = (n, d) private_key = (n, d)

View File

@ -32,13 +32,13 @@ def main() -> None:
letter_code = random_chars(32) letter_code = random_chars(32)
file_name = paths[index].split(os.sep)[-1].rsplit(".", 1)[0] file_name = paths[index].split(os.sep)[-1].rsplit(".", 1)[0]
file_root = f"{OUTPUT_DIR}/{file_name}_FLIP_{letter_code}" file_root = f"{OUTPUT_DIR}/{file_name}_FLIP_{letter_code}"
cv2.imwrite(f"/{file_root}.jpg", image, [cv2.IMWRITE_JPEG_QUALITY, 85]) cv2.imwrite(f"{file_root}.jpg", image, [cv2.IMWRITE_JPEG_QUALITY, 85])
print(f"Success {index+1}/{len(new_images)} with {file_name}") print(f"Success {index+1}/{len(new_images)} with {file_name}")
annos_list = [] annos_list = []
for anno in new_annos[index]: for anno in new_annos[index]:
obj = f"{anno[0]} {anno[1]} {anno[2]} {anno[3]} {anno[4]}" obj = f"{anno[0]} {anno[1]} {anno[2]} {anno[3]} {anno[4]}"
annos_list.append(obj) annos_list.append(obj)
with open(f"/{file_root}.txt", "w") as outfile: with open(f"{file_root}.txt", "w") as outfile:
outfile.write("\n".join(line for line in annos_list)) outfile.write("\n".join(line for line in annos_list))

View File

@ -1,13 +1,4 @@
def octal_to_binary(octal_number): def octal_to_binary(octal_number: str) -> str:
"""
Convert an octal number to binary.
Args:
octal_number (str): The octal number as a string.
Returns:
str: The binary representation of the octal number.
"""
binary_number = "" binary_number = ""
octal_digits = "01234567" octal_digits = "01234567"

View File

@ -1,5 +1,62 @@
""" r"""
A binary search Tree A binary search Tree
Example
8
/ \
3 10
/ \ \
1 6 14
/ \ /
4 7 13
>>> t = BinarySearchTree()
>>> t.insert(8, 3, 6, 1, 10, 14, 13, 4, 7)
>>> print(" ".join(repr(i.value) for i in t.traversal_tree()))
8 3 1 6 4 7 10 14 13
>>> print(" ".join(repr(i.value) for i in t.traversal_tree(postorder)))
1 4 7 6 3 13 14 10 8
>>> t.remove(20)
Traceback (most recent call last):
...
ValueError: Value 20 not found
>>> BinarySearchTree().search(6)
Traceback (most recent call last):
...
IndexError: Warning: Tree is empty! please use another.
Other example:
>>> testlist = (8, 3, 6, 1, 10, 14, 13, 4, 7)
>>> t = BinarySearchTree()
>>> for i in testlist:
... t.insert(i)
Prints all the elements of the list in order traversal
>>> print(t)
{'8': ({'3': (1, {'6': (4, 7)})}, {'10': (None, {'14': (13, None)})})}
Test existence
>>> t.search(6) is not None
True
>>> t.search(-1) is not None
False
>>> t.search(6).is_right
True
>>> t.search(1).is_right
False
>>> t.get_max().value
14
>>> t.get_min().value
1
>>> t.empty()
False
>>> for i in testlist:
... t.remove(i)
>>> t.empty()
True
""" """
from collections.abc import Iterable from collections.abc import Iterable
@ -20,6 +77,10 @@ class Node:
return str(self.value) return str(self.value)
return pformat({f"{self.value}": (self.left, self.right)}, indent=1) return pformat({f"{self.value}": (self.left, self.right)}, indent=1)
@property
def is_right(self) -> bool:
return self.parent is not None and self is self.parent.right
class BinarySearchTree: class BinarySearchTree:
def __init__(self, root: Node | None = None): def __init__(self, root: Node | None = None):
@ -35,18 +96,13 @@ class BinarySearchTree:
if new_children is not None: # reset its kids if new_children is not None: # reset its kids
new_children.parent = node.parent new_children.parent = node.parent
if node.parent is not None: # reset its parent if node.parent is not None: # reset its parent
if self.is_right(node): # If it is the right children if node.is_right: # If it is the right child
node.parent.right = new_children node.parent.right = new_children
else: else:
node.parent.left = new_children node.parent.left = new_children
else: else:
self.root = new_children self.root = new_children
def is_right(self, node: Node) -> bool:
if node.parent and node.parent.right:
return node == node.parent.right
return False
def empty(self) -> bool: def empty(self) -> bool:
return self.root is None return self.root is None
@ -119,8 +175,12 @@ class BinarySearchTree:
return node return node
def remove(self, value: int) -> None: def remove(self, value: int) -> None:
node = self.search(value) # Look for the node with that label # Look for the node with that label
if node is not None: node = self.search(value)
if node is None:
msg = f"Value {value} not found"
raise ValueError(msg)
if node.left is None and node.right is None: # If it has no children if node.left is None and node.right is None: # If it has no children
self.__reassign_nodes(node, None) self.__reassign_nodes(node, None)
elif node.left is None: # Has only right children elif node.left is None: # Has only right children
@ -128,12 +188,12 @@ class BinarySearchTree:
elif node.right is None: # Has only left children elif node.right is None: # Has only left children
self.__reassign_nodes(node, node.left) self.__reassign_nodes(node, node.left)
else: else:
tmp_node = self.get_max( predecessor = self.get_max(
node.left node.left
) # Gets the max value of the left branch ) # Gets the max value of the left branch
self.remove(tmp_node.value) # type: ignore self.remove(predecessor.value) # type: ignore
node.value = ( node.value = (
tmp_node.value # type: ignore predecessor.value # type: ignore
) # Assigns the value to the node to delete and keep tree structure ) # Assigns the value to the node to delete and keep tree structure
def preorder_traverse(self, node: Node | None) -> Iterable: def preorder_traverse(self, node: Node | None) -> Iterable:
@ -177,55 +237,6 @@ def postorder(curr_node: Node | None) -> list[Node]:
return node_list return node_list
def binary_search_tree() -> None:
r"""
Example
8
/ \
3 10
/ \ \
1 6 14
/ \ /
4 7 13
>>> t = BinarySearchTree()
>>> t.insert(8, 3, 6, 1, 10, 14, 13, 4, 7)
>>> print(" ".join(repr(i.value) for i in t.traversal_tree()))
8 3 1 6 4 7 10 14 13
>>> print(" ".join(repr(i.value) for i in t.traversal_tree(postorder)))
1 4 7 6 3 13 14 10 8
>>> BinarySearchTree().search(6)
Traceback (most recent call last):
...
IndexError: Warning: Tree is empty! please use another.
"""
testlist = (8, 3, 6, 1, 10, 14, 13, 4, 7)
t = BinarySearchTree()
for i in testlist:
t.insert(i)
# Prints all the elements of the list in order traversal
print(t)
if t.search(6) is not None:
print("The value 6 exists")
else:
print("The value 6 doesn't exist")
if t.search(-1) is not None:
print("The value -1 exists")
else:
print("The value -1 doesn't exist")
if not t.empty():
print("Max Value: ", t.get_max().value) # type: ignore
print("Min Value: ", t.get_min().value) # type: ignore
for i in testlist:
t.remove(i)
print(t)
if __name__ == "__main__": if __name__ == "__main__":
import doctest import doctest

View File

@ -21,6 +21,7 @@ def rgb2gray(rgb: np.array) -> np.array:
def gray2binary(gray: np.array) -> np.array: def gray2binary(gray: np.array) -> np.array:
""" """
Return binary image from gray image Return binary image from gray image
>>> gray2binary(np.array([[127, 255, 0]])) >>> gray2binary(np.array([[127, 255, 0]]))
array([[False, True, False]]) array([[False, True, False]])
>>> gray2binary(np.array([[0]])) >>> gray2binary(np.array([[0]]))

View File

@ -10,12 +10,12 @@ def get_rotation(
) -> np.ndarray: ) -> np.ndarray:
""" """
Get image rotation Get image rotation
:param img: np.array :param img: np.ndarray
:param pt1: 3x2 list :param pt1: 3x2 list
:param pt2: 3x2 list :param pt2: 3x2 list
:param rows: columns image shape :param rows: columns image shape
:param cols: rows image shape :param cols: rows image shape
:return: np.array :return: np.ndarray
""" """
matrix = cv2.getAffineTransform(pt1, pt2) matrix = cv2.getAffineTransform(pt1, pt2)
return cv2.warpAffine(img, matrix, (rows, cols)) return cv2.warpAffine(img, matrix, (rows, cols))

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

@ -6,14 +6,32 @@ from __future__ import annotations
Path = list[tuple[int, int]] Path = list[tuple[int, int]]
grid = [ # 0's are free path whereas 1's are obstacles
TEST_GRIDS = [
[
[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0], # 0 are free path whereas 1's are obstacles [0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0], [1, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0, 0],
],
[
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0],
],
[
[0, 0, 1, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 1],
[1, 0, 0, 1, 1],
[0, 0, 0, 0, 0],
],
] ]
delta = ([-1, 0], [0, -1], [1, 0], [0, 1]) # up, left, down, right delta = ([-1, 0], [0, -1], [1, 0], [0, 1]) # up, left, down, right
@ -65,10 +83,14 @@ class Node:
def __lt__(self, other) -> bool: def __lt__(self, other) -> bool:
return self.f_cost < other.f_cost return self.f_cost < other.f_cost
def __eq__(self, other) -> bool:
return self.pos == other.pos
class GreedyBestFirst: class GreedyBestFirst:
""" """
>>> gbf = GreedyBestFirst((0, 0), (len(grid) - 1, len(grid[0]) - 1)) >>> grid = TEST_GRIDS[2]
>>> gbf = GreedyBestFirst(grid, (0, 0), (len(grid) - 1, len(grid[0]) - 1))
>>> [x.pos for x in gbf.get_successors(gbf.start)] >>> [x.pos for x in gbf.get_successors(gbf.start)]
[(1, 0), (0, 1)] [(1, 0), (0, 1)]
>>> (gbf.start.pos_y + delta[3][0], gbf.start.pos_x + delta[3][1]) >>> (gbf.start.pos_y + delta[3][0], gbf.start.pos_x + delta[3][1])
@ -78,11 +100,14 @@ class GreedyBestFirst:
>>> gbf.retrace_path(gbf.start) >>> gbf.retrace_path(gbf.start)
[(0, 0)] [(0, 0)]
>>> gbf.search() # doctest: +NORMALIZE_WHITESPACE >>> gbf.search() # doctest: +NORMALIZE_WHITESPACE
[(0, 0), (1, 0), (2, 0), (3, 0), (3, 1), (4, 1), (5, 1), (6, 1), [(0, 0), (1, 0), (2, 0), (2, 1), (3, 1), (4, 1), (4, 2), (4, 3),
(6, 2), (6, 3), (5, 3), (5, 4), (5, 5), (6, 5), (6, 6)] (4, 4)]
""" """
def __init__(self, start: tuple[int, int], goal: tuple[int, int]): def __init__(
self, grid: list[list[int]], start: tuple[int, int], goal: tuple[int, int]
):
self.grid = grid
self.start = Node(start[1], start[0], goal[1], goal[0], 0, None) self.start = Node(start[1], start[0], goal[1], goal[0], 0, None)
self.target = Node(goal[1], goal[0], goal[1], goal[0], 99999, None) self.target = Node(goal[1], goal[0], goal[1], goal[0], 99999, None)
@ -114,14 +139,6 @@ class GreedyBestFirst:
if child_node not in self.open_nodes: if child_node not in self.open_nodes:
self.open_nodes.append(child_node) self.open_nodes.append(child_node)
else:
# retrieve the best current path
better_node = self.open_nodes.pop(self.open_nodes.index(child_node))
if child_node.g_cost < better_node.g_cost:
self.open_nodes.append(child_node)
else:
self.open_nodes.append(better_node)
if not self.reached: if not self.reached:
return [self.start.pos] return [self.start.pos]
@ -131,28 +148,22 @@ class GreedyBestFirst:
""" """
Returns a list of successors (both in the grid and free spaces) Returns a list of successors (both in the grid and free spaces)
""" """
successors = [] return [
for action in delta:
pos_x = parent.pos_x + action[1]
pos_y = parent.pos_y + action[0]
if not (0 <= pos_x <= len(grid[0]) - 1 and 0 <= pos_y <= len(grid) - 1):
continue
if grid[pos_y][pos_x] != 0:
continue
successors.append(
Node( Node(
pos_x, pos_x,
pos_y, pos_y,
self.target.pos_y,
self.target.pos_x, self.target.pos_x,
self.target.pos_y,
parent.g_cost + 1, parent.g_cost + 1,
parent, parent,
) )
for action in delta
if (
0 <= (pos_x := parent.pos_x + action[1]) < len(self.grid[0])
and 0 <= (pos_y := parent.pos_y + action[0]) < len(self.grid)
and self.grid[pos_y][pos_x] == 0
) )
return successors ]
def retrace_path(self, node: Node | None) -> Path: def retrace_path(self, node: Node | None) -> Path:
""" """
@ -168,6 +179,9 @@ class GreedyBestFirst:
if __name__ == "__main__": if __name__ == "__main__":
for idx, grid in enumerate(TEST_GRIDS):
print(f"==grid-{idx + 1}==")
init = (0, 0) init = (0, 0)
goal = (len(grid) - 1, len(grid[0]) - 1) goal = (len(grid) - 1, len(grid[0]) - 1)
for elem in grid: for elem in grid:
@ -175,7 +189,7 @@ if __name__ == "__main__":
print("------") print("------")
greedy_bf = GreedyBestFirst(init, goal) greedy_bf = GreedyBestFirst(grid, init, goal)
path = greedy_bf.search() path = greedy_bf.search()
if path: if path:
for pos_x, pos_y in path: for pos_x, pos_y in path:

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

@ -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

@ -19,7 +19,9 @@ def median(nums: list) -> int | float:
Returns: Returns:
Median. Median.
""" """
sorted_list = sorted(nums) # The sorted function returns list[SupportsRichComparisonT@sorted]
# which does not support `+`
sorted_list: list[int] = sorted(nums)
length = len(sorted_list) length = len(sorted_list)
mid_index = length >> 1 mid_index = length >> 1
return ( return (

View File

@ -0,0 +1,51 @@
"""
Finding the continuous fraction for a rational number using python
https://en.wikipedia.org/wiki/Continued_fraction
"""
from fractions import Fraction
def continued_fraction(num: Fraction) -> list[int]:
"""
:param num:
Fraction of the number whose continued fractions to be found.
Use Fraction(str(number)) for more accurate results due to
float inaccuracies.
:return:
The continued fraction of rational number.
It is the all commas in the (n + 1)-tuple notation.
>>> continued_fraction(Fraction(2))
[2]
>>> continued_fraction(Fraction("3.245"))
[3, 4, 12, 4]
>>> continued_fraction(Fraction("2.25"))
[2, 4]
>>> continued_fraction(1/Fraction("2.25"))
[0, 2, 4]
>>> continued_fraction(Fraction("415/93"))
[4, 2, 6, 7]
"""
numerator, denominator = num.as_integer_ratio()
continued_fraction_list: list[int] = []
while True:
integer_part = int(numerator / denominator)
continued_fraction_list.append(integer_part)
numerator -= integer_part * denominator
if numerator == 0:
break
numerator, denominator = denominator, numerator
return continued_fraction_list
if __name__ == "__main__":
import doctest
doctest.testmod()
print("Continued Fraction of 0.84375 is: ", continued_fraction(Fraction("0.84375")))

View File

@ -5,7 +5,7 @@ import numpy as np
def euler_modified( def euler_modified(
ode_func: Callable, y0: float, x0: float, step_size: float, x_end: float ode_func: Callable, y0: float, x0: float, step_size: float, x_end: float
) -> np.array: ) -> np.ndarray:
""" """
Calculate solution at each step to an ODE using Euler's Modified Method Calculate solution at each step to an ODE using Euler's Modified Method
The Euler Method is straightforward to implement, but can't give accurate solutions. The Euler Method is straightforward to implement, but can't give accurate solutions.

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

@ -13,7 +13,7 @@ This script is inspired by a corresponding research paper.
import numpy as np import numpy as np
def sigmoid(vector: np.array) -> np.array: def sigmoid(vector: np.ndarray) -> np.ndarray:
""" """
Mathematical function sigmoid takes a vector x of K real numbers as input and Mathematical function sigmoid takes a vector x of K real numbers as input and
returns 1/ (1 + e^-x). returns 1/ (1 + e^-x).
@ -25,17 +25,15 @@ def sigmoid(vector: np.array) -> np.array:
return 1 / (1 + np.exp(-vector)) return 1 / (1 + np.exp(-vector))
def gaussian_error_linear_unit(vector: np.array) -> np.array: def gaussian_error_linear_unit(vector: np.ndarray) -> np.ndarray:
""" """
Implements the Gaussian Error Linear Unit (GELU) function Implements the Gaussian Error Linear Unit (GELU) function
Parameters: Parameters:
vector (np.array): A numpy array of shape (1,n) vector (np.ndarray): A numpy array of shape (1, n) consisting of real values
consisting of real values
Returns: Returns:
gelu_vec (np.array): The input numpy array, after applying gelu_vec (np.ndarray): The input numpy array, after applying gelu
gelu.
Examples: Examples:
>>> gaussian_error_linear_unit(np.array([-1.0, 1.0, 2.0])) >>> gaussian_error_linear_unit(np.array([-1.0, 1.0, 2.0]))

View File

@ -14,7 +14,11 @@ Jaccard similarity is widely used with MinHashing.
""" """
def jaccard_similarity(set_a, set_b, alternative_union=False): def jaccard_similarity(
set_a: set[str] | list[str] | tuple[str],
set_b: set[str] | list[str] | tuple[str],
alternative_union=False,
):
""" """
Finds the jaccard similarity between two sets. Finds the jaccard similarity between two sets.
Essentially, its intersection over union. Essentially, its intersection over union.
@ -37,41 +41,52 @@ def jaccard_similarity(set_a, set_b, alternative_union=False):
>>> set_b = {'c', 'd', 'e', 'f', 'h', 'i'} >>> set_b = {'c', 'd', 'e', 'f', 'h', 'i'}
>>> jaccard_similarity(set_a, set_b) >>> jaccard_similarity(set_a, set_b)
0.375 0.375
>>> jaccard_similarity(set_a, set_a) >>> jaccard_similarity(set_a, set_a)
1.0 1.0
>>> jaccard_similarity(set_a, set_a, True) >>> jaccard_similarity(set_a, set_a, True)
0.5 0.5
>>> set_a = ['a', 'b', 'c', 'd', 'e'] >>> set_a = ['a', 'b', 'c', 'd', 'e']
>>> set_b = ('c', 'd', 'e', 'f', 'h', 'i') >>> set_b = ('c', 'd', 'e', 'f', 'h', 'i')
>>> jaccard_similarity(set_a, set_b) >>> jaccard_similarity(set_a, set_b)
0.375 0.375
>>> set_a = ('c', 'd', 'e', 'f', 'h', 'i')
>>> set_b = ['a', 'b', 'c', 'd', 'e']
>>> jaccard_similarity(set_a, set_b)
0.375
>>> set_a = ('c', 'd', 'e', 'f', 'h', 'i')
>>> set_b = ['a', 'b', 'c', 'd']
>>> jaccard_similarity(set_a, set_b, True)
0.2
>>> set_a = {'a', 'b'}
>>> set_b = ['c', 'd']
>>> jaccard_similarity(set_a, set_b)
Traceback (most recent call last):
...
ValueError: Set a and b must either both be sets or be either a list or a tuple.
""" """
if isinstance(set_a, set) and isinstance(set_b, set): if isinstance(set_a, set) and isinstance(set_b, set):
intersection = len(set_a.intersection(set_b)) intersection_length = len(set_a.intersection(set_b))
if alternative_union: if alternative_union:
union = len(set_a) + len(set_b) union_length = len(set_a) + len(set_b)
else: else:
union = len(set_a.union(set_b)) union_length = len(set_a.union(set_b))
return intersection / union return intersection_length / union_length
if isinstance(set_a, (list, tuple)) and isinstance(set_b, (list, tuple)): elif isinstance(set_a, (list, tuple)) and isinstance(set_b, (list, tuple)):
intersection = [element for element in set_a if element in set_b] intersection = [element for element in set_a if element in set_b]
if alternative_union: if alternative_union:
union = len(set_a) + len(set_b) return len(intersection) / (len(set_a) + len(set_b))
return len(intersection) / union
else: else:
union = set_a + [element for element in set_b if element not in set_a] # Cast set_a to list because tuples cannot be mutated
union = list(set_a) + [element for element in set_b if element not in set_a]
return len(intersection) / len(union) return len(intersection) / len(union)
raise ValueError(
return len(intersection) / len(union) "Set a and b must either both be sets or be either a list or a tuple."
return None )
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -7,10 +7,14 @@
limit is reached or the gradient f'(x[n]) approaches zero. In both cases, exception limit is reached or the gradient f'(x[n]) approaches zero. In both cases, exception
is raised. If iteration limit is reached, try increasing maxiter. is raised. If iteration limit is reached, try increasing maxiter.
""" """
import math as m import math as m
from collections.abc import Callable
DerivativeFunc = Callable[[float], float]
def calc_derivative(f, a, h=0.001): def calc_derivative(f: DerivativeFunc, a: float, h: float = 0.001) -> float:
""" """
Calculates derivative at point a for function f using finite difference Calculates derivative at point a for function f using finite difference
method method
@ -18,7 +22,14 @@ def calc_derivative(f, a, h=0.001):
return (f(a + h) - f(a - h)) / (2 * h) return (f(a + h) - f(a - h)) / (2 * h)
def newton_raphson(f, x0=0, maxiter=100, step=0.0001, maxerror=1e-6, logsteps=False): def newton_raphson(
f: DerivativeFunc,
x0: float = 0,
maxiter: int = 100,
step: float = 0.0001,
maxerror: float = 1e-6,
logsteps: bool = False,
) -> tuple[float, float, list[float]]:
a = x0 # set the initial guess a = x0 # set the initial guess
steps = [a] steps = [a]
error = abs(f(a)) error = abs(f(a))
@ -36,7 +47,7 @@ def newton_raphson(f, x0=0, maxiter=100, step=0.0001, maxerror=1e-6, logsteps=Fa
if logsteps: if logsteps:
# If logstep is true, then log intermediate steps # If logstep is true, then log intermediate steps
return a, error, steps return a, error, steps
return a, error return a, error, []
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,7 +1,7 @@
import numpy as np import numpy as np
def qr_householder(a): def qr_householder(a: np.ndarray):
"""Return a QR-decomposition of the matrix A using Householder reflection. """Return a QR-decomposition of the matrix A using Householder reflection.
The QR-decomposition decomposes the matrix A of shape (m, n) into an The QR-decomposition decomposes the matrix A of shape (m, n) into an

View File

@ -11,7 +11,7 @@ https://en.wikipedia.org/wiki/Sigmoid_function
import numpy as np import numpy as np
def sigmoid(vector: np.array) -> np.array: def sigmoid(vector: np.ndarray) -> np.ndarray:
""" """
Implements the sigmoid function Implements the sigmoid function

View File

@ -12,12 +12,12 @@ https://en.wikipedia.org/wiki/Activation_function
import numpy as np import numpy as np
def tangent_hyperbolic(vector: np.array) -> np.array: def tangent_hyperbolic(vector: np.ndarray) -> np.ndarray:
""" """
Implements the tanh function Implements the tanh function
Parameters: Parameters:
vector: np.array vector: np.ndarray
Returns: Returns:
tanh (np.array): The input numpy array after applying tanh. tanh (np.array): The input numpy array after applying tanh.

View File

@ -0,0 +1,39 @@
"""
Leaky Rectified Linear Unit (Leaky ReLU)
Use Case: Leaky ReLU addresses the problem of the vanishing gradient.
For more detailed information, you can refer to the following link:
https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Leaky_ReLU
"""
import numpy as np
def leaky_rectified_linear_unit(vector: np.ndarray, alpha: float) -> np.ndarray:
"""
Implements the LeakyReLU activation function.
Parameters:
vector (np.ndarray): The input array for LeakyReLU activation.
alpha (float): The slope for negative values.
Returns:
np.ndarray: The input array after applying the LeakyReLU activation.
Formula: f(x) = x if x > 0 else f(x) = alpha * x
Examples:
>>> leaky_rectified_linear_unit(vector=np.array([2.3,0.6,-2,-3.8]), alpha=0.3)
array([ 2.3 , 0.6 , -0.6 , -1.14])
>>> leaky_rectified_linear_unit(np.array([-9.2, -0.3, 0.45, -4.56]), alpha=0.067)
array([-0.6164 , -0.0201 , 0.45 , -0.30552])
"""
return np.where(vector > 0, vector, alpha * vector)
if __name__ == "__main__":
import doctest
doctest.testmod()

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

@ -226,7 +226,7 @@ def plot(
No doctest provided since this function does not have a return value. No doctest provided since this function does not have a return value.
""" """
fig = plt.figure() fig = plt.figure()
fig.canvas.set_window_title(title) fig.canvas.manager.set_window_title(title)
ax = plt.axes( ax = plt.axes(
xlim=(x_start, x_end), ylim=(y_start, y_end) xlim=(x_start, x_end), ylim=(y_start, y_end)
) # Set section to be plotted ) # Set section to be plotted

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")