Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 61 additions & 18 deletions algorithms/maths/polynomial.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,28 +438,19 @@ def __floordiv__(self, other: Union[int, float, Fraction, Monomial]):
# def __truediv__(self, other: Union[int, float, Fraction, Monomial, Polynomial]) -> Polynomial:
def __truediv__(self, other: Union[int, float, Fraction, Monomial]):
"""
For Polynomials, only division by a monomial
is defined.

TODO: Implement polynomial / polynomial.
For Polynomial division, no remainder is provided. Must use poly_long_division() to capture remainder
"""
if isinstance(other, int) or isinstance(other, float) or isinstance(other, Fraction):
return self.__truediv__( Monomial({}, other) )
elif isinstance(other, Monomial):
poly_temp = reduce(lambda acc, val: acc + val, map(lambda x: x / other, [z for z in self.all_monomials()]), Polynomial([Monomial({}, 0)]))
return poly_temp
elif isinstance(other, Polynomial):
if Monomial({}, 0) in other.all_monomials():
if len(other.all_monomials()) == 2:
temp_set = {x for x in other.all_monomials() if x != Monomial({}, 0)}
only = temp_set.pop()
return self.__truediv__(only)
elif len(other.all_monomials()) == 1:
temp_set = {x for x in other.all_monomials()}
only = temp_set.pop()
return self.__truediv__(only)

raise ValueError('Can only divide a polynomial by an int, float, Fraction, or a Monomial.')
# Call long division
quotient, remainder = self.poly_long_division(other)
return quotient # Return just the quotient, remainder is ignored here

raise ValueError('Can only divide a polynomial by an int, float, Fraction, Monomial, or Polynomial.')

return

Expand Down Expand Up @@ -526,7 +517,59 @@ def subs(self, substitutions: Union[int, float, Fraction, Dict[int, Union[int, f

def __str__(self) -> str:
"""
Get a string representation of
the polynomial.
Get a properly formatted string representation of the polynomial.
"""
sorted_monos = sorted(self.all_monomials(), key=lambda m: sorted(m.variables.items(), reverse=True),
reverse=True)
return ' + '.join(str(m) for m in sorted_monos if m.coeff != Fraction(0, 1))

def poly_long_division(self, other: 'Polynomial') -> tuple['Polynomial', 'Polynomial']:
"""
return ' + '.join(str(m) for m in self.all_monomials() if m.coeff != Fraction(0, 1))
Perform polynomial long division
Returns (quotient, remainder)
"""
if not isinstance(other, Polynomial):
raise ValueError("Can only divide by another Polynomial.")

if len(other.all_monomials()) == 0:
raise ValueError("Cannot divide by zero polynomial.")

quotient = Polynomial([])
remainder = self.clone()

divisor_monos = sorted(other.all_monomials(), key=lambda m: sorted(m.variables.items(), reverse=True),
reverse=True)
divisor_lead = divisor_monos[0]

while remainder.all_monomials() and max(remainder.variables(), default=-1) >= max(other.variables(),
default=-1):
remainder_monos = sorted(remainder.all_monomials(), key=lambda m: sorted(m.variables.items(), reverse=True),
reverse=True)
remainder_lead = remainder_monos[0]

if not all(remainder_lead.variables.get(var, 0) >= divisor_lead.variables.get(var, 0) for var in
divisor_lead.variables):
break

lead_quotient = remainder_lead / divisor_lead
quotient = quotient + Polynomial([lead_quotient]) # Convert Monomial to Polynomial

remainder = remainder - (
Polynomial([lead_quotient]) * other) # Convert Monomial to Polynomial before multiplication

return quotient, remainder

dividend = Polynomial([
Monomial({1: 3}, 4), # 4(a_1)^3
Monomial({1: 2}, 3), # 3(a_1)^2
Monomial({1: 1}, -2), # -2(a_1)
Monomial({}, 5) # +5
])

divisor = Polynomial([
Monomial({1: 1}, 2), # 2(a_1)
Monomial({}, -1) # -1
])

quotient = dividend / divisor
print("Quotient:", quotient)
26 changes: 26 additions & 0 deletions algorithms/sort/bead_sort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
Bead Sort (also known as Gravity Sort) is a natural sorting algorithm that simulates how beads would settle under gravity on an abacus. It is most useful for sorting positive integers, especially when the range of numbers isn't excessively large. However, it is not a comparison-based sort and is generally impractical for large inputs due to its reliance on physical modeling.
Time Complexity
- Best Case: O(n) if the numbers are already sorted
- Average Case: O(n^2) because each bead needs to be placed and then fall under gravity
- Worst Case: O(n^2) since each bead must "fall" individually
"""

def bead_sort(arr):
if any(num < 0 for num in arr):
raise ValueError("Bead sort only works with non-negative integers.")

max_num = max(arr) if arr else 0
grid = [[0] * len(arr) for _ in range(max_num)]

# Drop beads (place beads in columns)
for col, num in enumerate(arr):
for row in range(num):
grid[row][col] = 1

# Let the beads "fall" (count beads in each row)
for row in grid:
sum_beads = sum(row)
for col in range(len(arr)):
row[col] = 1 if col < sum_beads else 0

2 changes: 1 addition & 1 deletion tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
missing_ranges,
move_zeros,
plus_one_v1, plus_one_v2, plus_one_v3,
remove_duplicates
remove_duplicates,
rotate_v1, rotate_v2, rotate_v3,
summarize_ranges,
three_sum,
Expand Down
70 changes: 48 additions & 22 deletions tests/test_polynomial.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,27 +105,6 @@ def test_polynomial_multiplication(self):
]))
return

def test_polynomial_division(self):

# Should raise a ValueError if the divisor is not a monomial
# or a polynomial with only one term.
self.assertRaises(ValueError, lambda x, y: x / y, self.p5, self.p3)
self.assertRaises(ValueError, lambda x, y: x / y, self.p6, self.p4)

self.assertEqual(self.p3 / self.p2, Polynomial([
Monomial({}, 1),
Monomial({1: 1, 2: -1}, 0.75)
]))
self.assertEqual(self.p7 / self.m1, Polynomial([
Monomial({1: -1, 2: -3}, 2),
Monomial({1: 0, 2: -4}, 1.5)
]))
self.assertEqual(self.p7 / self.m1, Polynomial([
Monomial({1: -1, 2: -3}, 2),
Monomial({2: -4}, 1.5)
]))
return

def test_polynomial_variables(self):
# The zero polynomial has no variables.

Expand Down Expand Up @@ -172,4 +151,51 @@ def test_polynomial_clone(self):
self.assertEqual(self.p5.clone(), Polynomial([
Monomial({1: -1, 3: 2}, 1)
]))
return
return

def test_polynomial_long_division(self):
"""
Test polynomial long division
"""

# Dividend: 4a_1^3 + 3a_1^2 - 2a_1 + 5
dividend = Polynomial([
Monomial({1: 3}, 4), # 4(a_1)^3
Monomial({1: 2}, 3), # 3(a_1)^2
Monomial({1: 1}, -2), # -2(a_1)
Monomial({}, 5) # +5
])

# Divisor: 2a_1 - 1
divisor = Polynomial([
Monomial({1: 1}, 2), # 2(a_1)
Monomial({}, -1) # -1
])

# Expected Quotient: 2a_1^2 + (5/2)a_1 + 1/4
expected_quotient = Polynomial([
Monomial({1: 2}, 2), # 2(a_1)^2
Monomial({1: 1}, Fraction(5, 2)), # (5/2)(a_1)
Monomial({}, Fraction(1, 4)) # +1/4
])

# Expected Remainder: 21/4
expected_remainder = Polynomial([
Monomial({}, Fraction(21, 4)) # 21/4
])

quotient_long_div, remainder_long_div = dividend.poly_long_division(divisor)

quotient_truediv = dividend / divisor # Calls __truediv__, which returns only the quotient

# Check if quotient from poly_long_division matches expected
self.assertEqual(quotient_long_div, expected_quotient)

# Check if remainder from poly_long_division matches expected
self.assertEqual(remainder_long_div, expected_remainder)

# Check if quotient from __truediv__ matches quotient from poly_long_division
self.assertEqual(quotient_truediv, quotient_long_div)

return

Loading