Compare commits

..

No commits in common. "e4d90e2d5b92fdcff558f1848843dfbe20d81035" and "238fe8c494ab5be80c96441095d1c8958f95c04d" have entirely different histories.

8 changed files with 30 additions and 129 deletions

View File

@ -990,8 +990,6 @@
* [Sol1](project_euler/problem_174/sol1.py) * [Sol1](project_euler/problem_174/sol1.py)
* Problem 180 * Problem 180
* [Sol1](project_euler/problem_180/sol1.py) * [Sol1](project_euler/problem_180/sol1.py)
* Problem 187
* [Sol1](project_euler/problem_187/sol1.py)
* Problem 188 * Problem 188
* [Sol1](project_euler/problem_188/sol1.py) * [Sol1](project_euler/problem_188/sol1.py)
* Problem 191 * Problem 191

View File

@ -1,101 +1,62 @@
""" """Lower-Upper (LU) Decomposition.
Lowerupper (LU) decomposition factors a matrix as a product of a lower
triangular matrix and an upper triangular matrix. A square matrix has an LU
decomposition under the following conditions:
- If the matrix is invertible, then it has an LU decomposition if and only
if all of its leading principal minors are non-zero (see
https://en.wikipedia.org/wiki/Minor_(linear_algebra) for an explanation of
leading principal minors of a matrix).
- If the matrix is singular (i.e., not invertible) and it has a rank of k
(i.e., it has k linearly independent columns), then it has an LU
decomposition if its first k leading principal minors are non-zero.
This algorithm will simply attempt to perform LU decomposition on any square Reference:
matrix and raise an error if no such decomposition exists. - https://en.wikipedia.org/wiki/LU_decomposition
Reference: https://en.wikipedia.org/wiki/LU_decomposition
""" """
from __future__ import annotations from __future__ import annotations
import numpy as np import numpy as np
from numpy import float64
from numpy.typing import ArrayLike
def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray]: def lower_upper_decomposition(
""" table: ArrayLike[float64],
Perform LU decomposition on a given matrix and raises an error if the matrix ) -> tuple[ArrayLike[float64], ArrayLike[float64]]:
isn't square or if no such decomposition exists """Lower-Upper (LU) Decomposition
Example:
>>> matrix = np.array([[2, -2, 1], [0, 1, 2], [5, 3, 1]]) >>> matrix = np.array([[2, -2, 1], [0, 1, 2], [5, 3, 1]])
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix) >>> outcome = lower_upper_decomposition(matrix)
>>> lower_mat >>> outcome[0]
array([[1. , 0. , 0. ], array([[1. , 0. , 0. ],
[0. , 1. , 0. ], [0. , 1. , 0. ],
[2.5, 8. , 1. ]]) [2.5, 8. , 1. ]])
>>> upper_mat >>> outcome[1]
array([[ 2. , -2. , 1. ], array([[ 2. , -2. , 1. ],
[ 0. , 1. , 2. ], [ 0. , 1. , 2. ],
[ 0. , 0. , -17.5]]) [ 0. , 0. , -17.5]])
>>> matrix = np.array([[4, 3], [6, 3]])
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix)
>>> lower_mat
array([[1. , 0. ],
[1.5, 1. ]])
>>> upper_mat
array([[ 4. , 3. ],
[ 0. , -1.5]])
# Matrix is not square
>>> matrix = np.array([[2, -2, 1], [0, 1, 2]]) >>> matrix = np.array([[2, -2, 1], [0, 1, 2]])
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix) >>> lower_upper_decomposition(matrix)
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: 'table' has to be of square shaped array but got a 2x3 array: ValueError: 'table' has to be of square shaped array but got a 2x3 array:
[[ 2 -2 1] [[ 2 -2 1]
[ 0 1 2]] [ 0 1 2]]
# Matrix is invertible, but its first leading principal minor is 0
>>> matrix = np.array([[0, 1], [1, 0]])
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix)
Traceback (most recent call last):
...
ArithmeticError: No LU decomposition exists
# Matrix is singular, but its first leading principal minor is 1
>>> matrix = np.array([[1, 0], [1, 0]])
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix)
>>> lower_mat
array([[1., 0.],
[1., 1.]])
>>> upper_mat
array([[1., 0.],
[0., 0.]])
# Matrix is singular, but its first leading principal minor is 0
>>> matrix = np.array([[0, 1], [0, 1]])
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix)
Traceback (most recent call last):
...
ArithmeticError: No LU decomposition exists
""" """
# Ensure that table is a square array # Table that contains our data
# Table has to be a square array so we need to check first
rows, columns = np.shape(table) rows, columns = np.shape(table)
if rows != columns: if rows != columns:
raise ValueError( raise ValueError(
f"'table' has to be of square shaped array but got a " f"'table' has to be of square shaped array but got a {rows}x{columns} "
f"{rows}x{columns} array:\n{table}" + f"array:\n{table}"
) )
lower = np.zeros((rows, columns)) lower = np.zeros((rows, columns))
upper = np.zeros((rows, columns)) upper = np.zeros((rows, columns))
for i in range(columns): for i in range(columns):
for j in range(i): for j in range(i):
total = sum(lower[i][k] * upper[k][j] for k in range(j)) total = 0
if upper[j][j] == 0: for k in range(j):
raise ArithmeticError("No LU decomposition exists") total += lower[i][k] * upper[k][j]
lower[i][j] = (table[i][j] - total) / upper[j][j] lower[i][j] = (table[i][j] - total) / upper[j][j]
lower[i][i] = 1 lower[i][i] = 1
for j in range(i, columns): for j in range(i, columns):
total = sum(lower[i][k] * upper[k][j] for k in range(j)) total = 0
for k in range(i):
total += lower[i][k] * upper[k][j]
upper[i][j] = table[i][j] - total upper[i][j] = table[i][j] - total
return lower, upper return lower, upper

View File

@ -24,7 +24,7 @@ class CircularLinkedList:
break break
def __len__(self) -> int: def __len__(self) -> int:
return sum(1 for _ in self) return len(tuple(iter(self)))
def __repr__(self): def __repr__(self):
return "->".join(str(item) for item in iter(self)) return "->".join(str(item) for item in iter(self))

View File

@ -51,7 +51,7 @@ class DoublyLinkedList:
>>> len(linked_list) == 5 >>> len(linked_list) == 5
True True
""" """
return sum(1 for _ in self) return len(tuple(iter(self)))
def insert_at_head(self, data): def insert_at_head(self, data):
self.insert_at_nth(0, data) self.insert_at_nth(0, data)

View File

@ -44,7 +44,7 @@ class SortedLinkedList:
>>> len(SortedLinkedList(test_data_odd)) >>> len(SortedLinkedList(test_data_odd))
8 8
""" """
return sum(1 for _ in self) return len(tuple(iter(self)))
def __str__(self) -> str: def __str__(self) -> str:
""" """

View File

@ -72,7 +72,7 @@ class LinkedList:
>>> len(linked_list) >>> len(linked_list)
0 0
""" """
return sum(1 for _ in self) return len(tuple(iter(self)))
def __repr__(self) -> str: def __repr__(self) -> str:
""" """

View File

@ -1,58 +0,0 @@
"""
Project Euler Problem 187: https://projecteuler.net/problem=187
A composite is a number containing at least two prime factors.
For example, 15 = 3 x 5; 9 = 3 x 3; 12 = 2 x 2 x 3.
There are ten composites below thirty containing precisely two,
not necessarily distinct, prime factors: 4, 6, 9, 10, 14, 15, 21, 22, 25, 26.
How many composite integers, n < 10^8, have precisely two,
not necessarily distinct, prime factors?
"""
from math import isqrt
def calculate_prime_numbers(max_number: int) -> list[int]:
"""
Returns prime numbers below max_number
>>> calculate_prime_numbers(10)
[2, 3, 5, 7]
"""
is_prime = [True] * max_number
for i in range(2, isqrt(max_number - 1) + 1):
if is_prime[i]:
for j in range(i**2, max_number, i):
is_prime[j] = False
return [i for i in range(2, max_number) if is_prime[i]]
def solution(max_number: int = 10**8) -> int:
"""
Returns the number of composite integers below max_number have precisely two,
not necessarily distinct, prime factors
>>> solution(30)
10
"""
prime_numbers = calculate_prime_numbers(max_number // 2)
semiprimes_count = 0
left = 0
right = len(prime_numbers) - 1
while left <= right:
while prime_numbers[left] * prime_numbers[right] >= max_number:
right -= 1
semiprimes_count += right - left + 1
left += 1
return semiprimes_count
if __name__ == "__main__":
print(f"{solution() = }")