Compare commits

...

9 Commits

Author SHA1 Message Date
NoodlesIAte
a1ed6eb566
Merge 6b60215fb5 into fcf82a1eda 2024-10-05 10:59:27 -07:00
Vineet Kumar
fcf82a1eda
Implemented Exponential Search with binary search for improved perfor… (#11666)
* Implemented Exponential Search with binary search for improved performance on large sorted arrays.

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

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

* Added type hints and doctests for binary_search and exponential_search functions. Improved code documentation and ensured testability.

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

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

* Update and rename Exponential_Search.py to exponential_search.py

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-10-05 10:34:48 -07:00
Andrey Ivanov
ad6395d340
Update ruff usage example in CONTRIBUTING.md (#11772)
* Update ruff usage example

* Update CONTRIBUTING.md

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

---------

Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
2024-10-05 10:24:58 -07:00
Jeel Rupapara
50aca04c67
feat: increase test coverage of longest_common_subsequence to 75% (#11777) 2024-10-05 10:21:43 -07:00
1227haran
5a8655d306
Added new algorithm to generate numbers in lexicographical order (#11674)
* Added algorithm to generate numbers in lexicographical order

* Removed the test cases

* Updated camelcase to snakecase

* Added doctest

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

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

* Added descriptive name for n

* Reduced the number of letters

* Updated the return type

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

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

* Updated import statement

* Updated return type to Iterator[int]

* removed parentheses

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-10-05 10:19:58 -07:00
ARNAV RAJ
9a572dec2b
feat: Implemented Matrix Exponentiation Method (#11747)
* feat: add Matrix Exponentiation method
docs: updated the header documentation and added new documentation for
the new function.

* feat: added new function matrix exponetiation method

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

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

* feat: This function uses the tail-recursive form of the Euclidean algorithm to calculate

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

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

* reduced the number of characters per line in the comments

* removed unwanted code

* feat: Implemented a new function to swaap numbers without dummy variable

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

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

* removed previos code

* Done with the required changes

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

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

* Done with the required changes

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

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

* Done with the required changes

* Done with the required changes

* Done with the required changes

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

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

* Update maths/fibonacci.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

* Done with the required changes

* Done with the required changes

* Done with the required changes

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
2024-10-04 09:29:39 -07:00
Lonercode
59ff87dc55
Added doctests to min_cost_string_conversion.py and removed :c specifier (#11721)
* Added doctests to min_cost_string_conversion.py and removed :c specifier

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

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

* resolved line length issues based on ruff requirements

* modified in compliance with ruff for line length

* Update strings/min_cost_string_conversion.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>
2024-10-04 02:36:14 -07:00
Sai Aswin Madhavan
917ad62105
Removed incorrect type hints (#11711) 2024-10-04 02:28:50 -07:00
Hardik Pawar
e20b503b24
Improve comments, add doctests for kahns_algorithm_topo.py (#11668)
* Improve comments, add doctests for kahns_algorithm_topo.py

* Improve function docstring

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

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

* Rename variables, remove print

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-10-04 02:06:08 -07:00
7 changed files with 342 additions and 26 deletions

View File

@ -96,7 +96,7 @@ We want your work to be readable by others; therefore, we encourage you to note
```bash
python3 -m pip install ruff # only required the first time
ruff .
ruff check
```
- Original code submission require docstrings or comments to describe your work.

View File

@ -0,0 +1,38 @@
from collections.abc import Iterator
def lexical_order(max_number: int) -> Iterator[int]:
"""
Generate numbers in lexical order from 1 to max_number.
>>> " ".join(map(str, lexical_order(13)))
'1 10 11 12 13 2 3 4 5 6 7 8 9'
>>> list(lexical_order(1))
[1]
>>> " ".join(map(str, lexical_order(20)))
'1 10 11 12 13 14 15 16 17 18 19 2 20 3 4 5 6 7 8 9'
>>> " ".join(map(str, lexical_order(25)))
'1 10 11 12 13 14 15 16 17 18 19 2 20 21 22 23 24 25 3 4 5 6 7 8 9'
>>> list(lexical_order(12))
[1, 10, 11, 12, 2, 3, 4, 5, 6, 7, 8, 9]
"""
stack = [1]
while stack:
num = stack.pop()
if num > max_number:
continue
yield num
if (num % 10) != 9:
stack.append(num + 1)
stack.append(num * 10)
if __name__ == "__main__":
from doctest import testmod
testmod()
print(f"Numbers from 1 to 25 in lexical order: {list(lexical_order(26))}")

View File

@ -28,6 +28,24 @@ def longest_common_subsequence(x: str, y: str):
(2, 'ph')
>>> longest_common_subsequence("computer", "food")
(1, 'o')
>>> longest_common_subsequence("", "abc") # One string is empty
(0, '')
>>> longest_common_subsequence("abc", "") # Other string is empty
(0, '')
>>> longest_common_subsequence("", "") # Both strings are empty
(0, '')
>>> longest_common_subsequence("abc", "def") # No common subsequence
(0, '')
>>> longest_common_subsequence("abc", "abc") # Identical strings
(3, 'abc')
>>> longest_common_subsequence("a", "a") # Single character match
(1, 'a')
>>> longest_common_subsequence("a", "b") # Single character no match
(0, '')
>>> longest_common_subsequence("abcdef", "ace") # Interleaved subsequence
(3, 'ace')
>>> longest_common_subsequence("ABCD", "ACBD") # No repeated characters
(3, 'ABD')
"""
# find the length of strings

View File

@ -1,36 +1,61 @@
def topological_sort(graph):
def topological_sort(graph: dict[int, list[int]]) -> list[int] | None:
"""
Kahn's Algorithm is used to find Topological ordering of Directed Acyclic Graph
using BFS
Perform topological sorting of a Directed Acyclic Graph (DAG)
using Kahn's Algorithm via Breadth-First Search (BFS).
Topological sorting is a linear ordering of vertices in a graph such that for
every directed edge u v, vertex u comes before vertex v in the ordering.
Parameters:
graph: Adjacency list representing the directed graph where keys are
vertices, and values are lists of adjacent vertices.
Returns:
The topologically sorted order of vertices if the graph is a DAG.
Returns None if the graph contains a cycle.
Example:
>>> graph = {0: [1, 2], 1: [3], 2: [3], 3: [4, 5], 4: [], 5: []}
>>> topological_sort(graph)
[0, 1, 2, 3, 4, 5]
>>> graph_with_cycle = {0: [1], 1: [2], 2: [0]}
>>> topological_sort(graph_with_cycle)
"""
indegree = [0] * len(graph)
queue = []
topo = []
cnt = 0
topo_order = []
processed_vertices_count = 0
# Calculate the indegree of each vertex
for values in graph.values():
for i in values:
indegree[i] += 1
# Add all vertices with 0 indegree to the queue
for i in range(len(indegree)):
if indegree[i] == 0:
queue.append(i)
# Perform BFS
while queue:
vertex = queue.pop(0)
cnt += 1
topo.append(vertex)
for x in graph[vertex]:
indegree[x] -= 1
if indegree[x] == 0:
queue.append(x)
processed_vertices_count += 1
topo_order.append(vertex)
if cnt != len(graph):
print("Cycle exists")
else:
print(topo)
# Traverse neighbors
for neighbor in graph[vertex]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
queue.append(neighbor)
if processed_vertices_count != len(graph):
return None # no topological ordering exists due to cycle
return topo_order # valid topological ordering
# Adjacency List of Graph
graph = {0: [1, 2], 1: [3], 2: [3], 3: [4, 5], 4: [], 5: []}
topological_sort(graph)
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -7,6 +7,8 @@ the Binet's formula function because the Binet formula function uses floats
NOTE 2: the Binet's formula function is much more limited in the size of inputs
that it can handle due to the size limitations of Python floats
NOTE 3: the matrix function is the fastest and most memory efficient for large n
See benchmark numbers in __main__ for performance comparisons/
https://en.wikipedia.org/wiki/Fibonacci_number for more information
@ -17,6 +19,9 @@ from collections.abc import Iterator
from math import sqrt
from time import time
import numpy as np
from numpy import ndarray
def time_func(func, *args, **kwargs):
"""
@ -230,6 +235,88 @@ def fib_binet(n: int) -> list[int]:
return [round(phi**i / sqrt_5) for i in range(n + 1)]
def matrix_pow_np(m: ndarray, power: int) -> ndarray:
"""
Raises a matrix to the power of 'power' using binary exponentiation.
Args:
m: Matrix as a numpy array.
power: The power to which the matrix is to be raised.
Returns:
The matrix raised to the power.
Raises:
ValueError: If power is negative.
>>> m = np.array([[1, 1], [1, 0]], dtype=int)
>>> matrix_pow_np(m, 0) # Identity matrix when raised to the power of 0
array([[1, 0],
[0, 1]])
>>> matrix_pow_np(m, 1) # Same matrix when raised to the power of 1
array([[1, 1],
[1, 0]])
>>> matrix_pow_np(m, 5)
array([[8, 5],
[5, 3]])
>>> matrix_pow_np(m, -1)
Traceback (most recent call last):
...
ValueError: power is negative
"""
result = np.array([[1, 0], [0, 1]], dtype=int) # Identity Matrix
base = m
if power < 0: # Negative power is not allowed
raise ValueError("power is negative")
while power:
if power % 2 == 1:
result = np.dot(result, base)
base = np.dot(base, base)
power //= 2
return result
def fib_matrix_np(n: int) -> int:
"""
Calculates the n-th Fibonacci number using matrix exponentiation.
https://www.nayuki.io/page/fast-fibonacci-algorithms#:~:text=
Summary:%20The%20two%20fast%20Fibonacci%20algorithms%20are%20matrix
Args:
n: Fibonacci sequence index
Returns:
The n-th Fibonacci number.
Raises:
ValueError: If n is negative.
>>> fib_matrix_np(0)
0
>>> fib_matrix_np(1)
1
>>> fib_matrix_np(5)
5
>>> fib_matrix_np(10)
55
>>> fib_matrix_np(-1)
Traceback (most recent call last):
...
ValueError: n is negative
"""
if n < 0:
raise ValueError("n is negative")
if n == 0:
return 0
m = np.array([[1, 1], [1, 0]], dtype=int)
result = matrix_pow_np(m, n - 1)
return int(result[0, 0])
if __name__ == "__main__":
from doctest import testmod
@ -242,3 +329,4 @@ if __name__ == "__main__":
time_func(fib_memoization, num) # 0.0100 ms
time_func(fib_recursive_cached, num) # 0.0153 ms
time_func(fib_recursive, num) # 257.0910 ms
time_func(fib_matrix_np, num) # 0.0000 ms

View File

@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""
Pure Python implementation of exponential search algorithm
For more information, see the Wikipedia page:
https://en.wikipedia.org/wiki/Exponential_search
For doctests run the following command:
python3 -m doctest -v exponential_search.py
For manual testing run:
python3 exponential_search.py
"""
from __future__ import annotations
def binary_search_by_recursion(
sorted_collection: list[int], item: int, left: int = 0, right: int = -1
) -> int:
"""Pure implementation of binary search algorithm in Python using recursion
Be careful: the collection must be ascending sorted otherwise, the result will be
unpredictable.
:param sorted_collection: some ascending sorted collection with comparable items
:param item: item value to search
:param left: starting index for the search
:param right: ending index for the search
:return: index of the found item or -1 if the item is not found
Examples:
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4)
0
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4)
4
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4)
1
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4)
-1
"""
if right < 0:
right = len(sorted_collection) - 1
if list(sorted_collection) != sorted(sorted_collection):
raise ValueError("sorted_collection must be sorted in ascending order")
if right < left:
return -1
midpoint = left + (right - left) // 2
if sorted_collection[midpoint] == item:
return midpoint
elif sorted_collection[midpoint] > item:
return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1)
else:
return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right)
def exponential_search(sorted_collection: list[int], item: int) -> int:
"""
Pure implementation of an exponential search algorithm in Python.
For more information, refer to:
https://en.wikipedia.org/wiki/Exponential_search
Be careful: the collection must be ascending sorted, otherwise the result will be
unpredictable.
:param sorted_collection: some ascending sorted collection with comparable items
:param item: item value to search
:return: index of the found item or -1 if the item is not found
The time complexity of this algorithm is O(log i) where i is the index of the item.
Examples:
>>> exponential_search([0, 5, 7, 10, 15], 0)
0
>>> exponential_search([0, 5, 7, 10, 15], 15)
4
>>> exponential_search([0, 5, 7, 10, 15], 5)
1
>>> exponential_search([0, 5, 7, 10, 15], 6)
-1
"""
if list(sorted_collection) != sorted(sorted_collection):
raise ValueError("sorted_collection must be sorted in ascending order")
if sorted_collection[0] == item:
return 0
bound = 1
while bound < len(sorted_collection) and sorted_collection[bound] < item:
bound *= 2
left = bound // 2
right = min(bound, len(sorted_collection) - 1)
return binary_search_by_recursion(sorted_collection, item, left, right)
if __name__ == "__main__":
import doctest
doctest.testmod()
# Manual testing
user_input = input("Enter numbers separated by commas: ").strip()
collection = sorted(int(item) for item in user_input.split(","))
target = int(input("Enter a number to search for: "))
result = exponential_search(sorted_collection=collection, item=target)
if result == -1:
print(f"{target} was not found in {collection}.")
else:
print(f"{target} was found at index {result} in {collection}.")

View File

@ -17,11 +17,27 @@ def compute_transform_tables(
delete_cost: int,
insert_cost: int,
) -> tuple[list[list[int]], list[list[str]]]:
"""
Finds the most cost efficient sequence
for converting one string into another.
>>> costs, operations = compute_transform_tables("cat", "cut", 1, 2, 3, 3)
>>> costs[0][:4]
[0, 3, 6, 9]
>>> costs[2][:4]
[6, 4, 3, 6]
>>> operations[0][:4]
['0', 'Ic', 'Iu', 'It']
>>> operations[3][:4]
['Dt', 'Dt', 'Rtu', 'Ct']
>>> compute_transform_tables("", "", 1, 2, 3, 3)
([[0]], [['0']])
"""
source_seq = list(source_string)
destination_seq = list(destination_string)
len_source_seq = len(source_seq)
len_destination_seq = len(destination_seq)
costs = [
[0 for _ in range(len_destination_seq + 1)] for _ in range(len_source_seq + 1)
]
@ -31,33 +47,51 @@ def compute_transform_tables(
for i in range(1, len_source_seq + 1):
costs[i][0] = i * delete_cost
ops[i][0] = f"D{source_seq[i - 1]:c}"
ops[i][0] = f"D{source_seq[i - 1]}"
for i in range(1, len_destination_seq + 1):
costs[0][i] = i * insert_cost
ops[0][i] = f"I{destination_seq[i - 1]:c}"
ops[0][i] = f"I{destination_seq[i - 1]}"
for i in range(1, len_source_seq + 1):
for j in range(1, len_destination_seq + 1):
if source_seq[i - 1] == destination_seq[j - 1]:
costs[i][j] = costs[i - 1][j - 1] + copy_cost
ops[i][j] = f"C{source_seq[i - 1]:c}"
ops[i][j] = f"C{source_seq[i - 1]}"
else:
costs[i][j] = costs[i - 1][j - 1] + replace_cost
ops[i][j] = f"R{source_seq[i - 1]:c}" + str(destination_seq[j - 1])
ops[i][j] = f"R{source_seq[i - 1]}" + str(destination_seq[j - 1])
if costs[i - 1][j] + delete_cost < costs[i][j]:
costs[i][j] = costs[i - 1][j] + delete_cost
ops[i][j] = f"D{source_seq[i - 1]:c}"
ops[i][j] = f"D{source_seq[i - 1]}"
if costs[i][j - 1] + insert_cost < costs[i][j]:
costs[i][j] = costs[i][j - 1] + insert_cost
ops[i][j] = f"I{destination_seq[j - 1]:c}"
ops[i][j] = f"I{destination_seq[j - 1]}"
return costs, ops
def assemble_transformation(ops: list[list[str]], i: int, j: int) -> list[str]:
"""
Assembles the transformations based on the ops table.
>>> ops = [['0', 'Ic', 'Iu', 'It'],
... ['Dc', 'Cc', 'Iu', 'It'],
... ['Da', 'Da', 'Rau', 'Rat'],
... ['Dt', 'Dt', 'Rtu', 'Ct']]
>>> x = len(ops) - 1
>>> y = len(ops[0]) - 1
>>> assemble_transformation(ops, x, y)
['Cc', 'Rau', 'Ct']
>>> ops1 = [['0']]
>>> x1 = len(ops1) - 1
>>> y1 = len(ops1[0]) - 1
>>> assemble_transformation(ops1, x1, y1)
[]
"""
if i == 0 and j == 0:
return []
elif ops[i][j][0] in {"C", "R"}: