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
6 changes: 4 additions & 2 deletions Assignment0/basic_no_class_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

def test_something():
roots = quadratic.solve_quadratic_equation(1, -1000000.001, 1)
# print("Testing without classes/unittest, indeed...")
assert( abs(1000000 - roots[0] ) < 1e-7 )
# The smaller root is returned first. Its magnitude is around 1e-6 and
# the product of both roots should equal ``c/a`` which is 1.
assert abs(1e-6 - roots[0]) < 1e-12
assert abs(roots[0] * roots[1] - 1.0) < 1e-6

14 changes: 7 additions & 7 deletions Assignment0/basic_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ def test_easy_case(self):

def test_big_coefficient(self):
roots = quadratic.solve_quadratic_equation(1, -1000000.001, 1)
# print("Testing with unittest, indeed...")
self.assertAlmostEqual(roots[0], 1000000, places=7, msg = "Not enough places")
self.assertAlmostEqual(roots[0], 1000000, delta=1e-7, msg = "Delta not met")
# The function should return the smaller root first and avoid the
# catastrophic cancellation that occurs for large coefficients.
self.assertAlmostEqual(1e-6, roots[0], places=12)
self.assertAlmostEqual(1.0, roots[0] * roots[1], places=6)

def test_double_root_case(self):
"""Solving a quadratic equation with a repeated root."""
Expand All @@ -35,7 +36,6 @@ def test_double_root_case(self):
def test_degenerate_quadratic_case(self):
"""Solving a quadratic with a=0."""
a, b, c = 0, 1, 1
try:
x1, x2 = quadratic.solve_quadratic_equation(a, b, c)
except ZeroDivisionError:
self.fail("Unhandled division by 0 when a=0.")
x1, x2 = quadratic.solve_quadratic_equation(a, b, c)
self.assertAlmostEqual(-1, x1)
self.assertIsNone(x2)
71 changes: 48 additions & 23 deletions Assignment0/solve_quadratic_equation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,60 @@
# A basic quadratic equation solver. High-school method.

import math


def solve_quadratic_equation(a, b, c):
"""Solve the quadratic equation ``a*x^2 + b*x + c = 0``.

The implementation prefers numerical stability and always returns real
roots. A repeated root is represented by ``(root, None)``. Linear
equations (``a = 0``) are supported as a special case.

Parameters
----------
a : float
Coefficient of :math:`x^2`.
b : float
Coefficient of :math:`x`.
c : float
Constant term.

Returns
-------
tuple
``(root1, root2)`` where ``root1`` is the smaller root. ``root2`` is
``None`` when the equation has a repeated root or is linear.

Raises
------
ValueError
If the equation has complex roots or is degenerate (``a = b = 0``).
"""
Solve the quadratic equation a*x^2 + b*x + c = 0 using the standard quadratic formula.

This function calculates the roots using the basic quadratic formula without any adjustments
for numerical stability. It assumes real coefficients and only returns real roots.

Parameters:
a (float): Coefficient of x^2.
b (float): Coefficient of x.
c (float): Constant term.

Returns:
tuple:
- (float): The first root.
- (float or None): The second root, or None if there is only one distinct root due to a zero discriminant.

Raises:
"""
# Calculate the discriminant
discriminant = b**2 - 4*a*c

# Calculate the discriminant's square root
# Handle linear equations first to avoid division by zero.
if a == 0:
if b == 0:
raise ValueError("Not an equation: both 'a' and 'b' are zero")
return (-c / b, None)

# Calculate the discriminant and check for real roots
discriminant = b * b - 4 * a * c
if discriminant < 0:
raise ValueError("The equation has complex roots")

sqrt_discriminant = math.sqrt(discriminant)

# Compute both roots using the standard quadratic formula
root1 = (-b + sqrt_discriminant) / (2 * a)
root2 = (-b - sqrt_discriminant) / (2 * a)
# Repeated root
if discriminant == 0:
return (-b / (2 * a), None)

# Use a numerically stable version of the quadratic formula
q = -0.5 * (b + math.copysign(sqrt_discriminant, b))
root1 = q / a
root2 = c / q

# Ensure the roots are ordered from smallest to largest
root1, root2 = (root1, root2) if root1 <= root2 else (root2, root1)
return (root1, root2)
# Example usage:
# NOTE: Also, as simple testing framework.
Expand Down