Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
55 views
ubuntu2404
Kernel: SageMath 10.7

Elementary Number Theory with SageMath

Learning Objectives

By the end of this notebook, you will:

  • Master fundamental concepts of prime numbers and factorization

  • Understand and apply the Euclidean algorithm and its extensions

  • Work confidently with modular arithmetic and congruences

  • Solve systems of congruences using the Chinese Remainder Theorem

  • Explore important number theory functions like Euler's totient

  • Apply number theory to solve Diophantine equations

Prerequisites

  • Basic algebra and mathematical reasoning

  • Familiarity with SageMath (helpful but not required)

  • Interest in patterns and mathematical structures

Why Study Number Theory?

Number theory is the "Queen of Mathematics" - it reveals beautiful patterns in integers and has surprising applications in cryptography, computer science, and pure mathematics.

Historical Context: The Ancient Art of Numbers

Number theory has fascinated mathematicians for millennia:

Ancient Times: Greeks studied perfect numbers and prime patterns 300 BCE: Euclid proved infinitude of primes and developed the famous algorithm 1600s: Fermat posed famous conjectures (Fermat's Little Theorem, Last Theorem) 1700s: Euler introduced the totient function and proved many fundamental results 1800s: Gauss developed modular arithmetic and quadratic reciprocity 1900s: Number theory became essential for cryptography and computer science

The Fundamental Questions:

  • Which numbers are prime?

  • How do we find greatest common divisors?

  • What patterns exist in modular arithmetic?

  • How do we solve equations with integer solutions?

Let's explore these timeless questions using modern computational tools!

# Setup: Import standard Python libraries for number theory import numpy as np import sympy as sp import matplotlib.pyplot as plt from scipy import * import pandas as pd from collections import Counter import math print("Elementary Number Theory with Standard Python") print("=" * 45) print("Ready to explore the fascinating world of integers!") print("Using SymPy for advanced number theory computations.") print() print("Fun Fact: The integers were called 'God's gift to mathematics' by Kronecker!")
Elementary Number Theory with Standard Python ============================================= Ready to explore the fascinating world of integers! Using SymPy for advanced number theory computations. Fun Fact: The integers were called 'God's gift to mathematics' by Kronecker!

Chapter 1: Prime Numbers - The Building Blocks

Prime numbers are integers greater than 1 that have no positive divisors other than 1 and themselves.

Fundamental Theorem of Arithmetic:

Every integer greater than 1 either is prime or can be written as a unique product of primes.

This makes primes the "atoms" of the integers!

# Chapter 1: Prime Number Fundamentals import sympy as sp print("PRIME NUMBER EXPLORATION\n") # Test primality of various numbers test_numbers = [2, 17, 51, 97, 221, 1009] print("Prime Testing:") print("Number | Prime? | Factorization") print("-" * 35) for n in test_numbers: is_prime_result = sp.isprime(n) if not is_prime_result: factorization = sp.factorint(n) factor_str = " × ".join([f"{p}^{e}" if e > 1 else str(p) for p, e in factorization.items()]) else: factor_str = "prime" status = "✓" if is_prime_result else "✗" print(f"{int(n):6d} | {status} | {factor_str}") print("\n💡 Insight: Composite numbers reveal their structure through factorization!") print() # Generate first primes using SymPy first_primes = list(sp.primerange(2, 100)) print(f"First 20 primes: {first_primes[:20]}") print(f"Total primes under 100: {len(first_primes)}") # Prime counting function π(x) print("\nPrime Counting Function π(x):") print(" x | π(x) | Density") print("-" * 25) for x_val in [10, 50, 100, 500, 1000]: xv = int(x_val) pi_x = len(list(sp.primerange(2, xv + 1))) density = 100.0 * pi_x / xv # force Python float print(f"{xv:6d} | {pi_x:4d} | {float(density):5.1f}%") print("\nObservation: Prime density decreases as numbers get larger!")
PRIME NUMBER EXPLORATION Prime Testing: Number | Prime? | Factorization ----------------------------------- 2 | ✓ | prime 17 | ✓ | prime 51 | ✗ | 3 × 17 97 | ✓ | prime 221 | ✗ | 13 × 17 1009 | ✓ | prime 💡 Insight: Composite numbers reveal their structure through factorization! First 20 primes: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71] Total primes under 100: 25 Prime Counting Function π(x): x | π(x) | Density ------------------------- 10 | 4 | 40.0% 50 | 15 | 30.0% 100 | 25 | 25.0% 500 | 95 | 19.0% 1000 | 168 | 16.8% Observation: Prime density decreases as numbers get larger!
# Prime Factorization Deep Dive print("PRIME FACTORIZATION WORKSHOP\n") # Demonstrate unique factorization numbers_to_factor = [60, 315, 1001, 2310, 9999] print("Unique Prime Factorizations:") for n in numbers_to_factor: factorization = factor(n) # Verify by multiplication product_check = prod(p^e for p, e in factorization) print(f"{n:4d} = {factorization} {'✓' if product_check == n else '✗'}") print() # Divisor function analysis print("Divisors and the σ functions:") print("n | d(n) | σ(n) | Divisors") print("-" * 45) for n in [12, 24, 30, 60]: divs = divisors(n) num_divisors = len(divs) # d(n) sum_divisors = sum(divs) # σ(n) divs_str = ', '.join(map(str, divs)) print(f"{n:4d} | {num_divisors:4d} | {sum_divisors:4d} | {divs_str}") print() # Perfect numbers - where σ(n) = 2n def check_perfect_numbers(limit): perfect_nums = [] for n in range(1, limit): proper_divisors = [d for d in divisors(n) if d < n] if sum(proper_divisors) == n: perfect_nums.append(n) return perfect_nums perfect_numbers = check_perfect_numbers(10000) print(f"Perfect Numbers (up to 10,000): {perfect_numbers}") if perfect_numbers: n = perfect_numbers[0] # First perfect number proper_divs = [d for d in divisors(n) if d < n] print(f"\nExample: {n} = {' + '.join(map(str, proper_divs))} = {sum(proper_divs)}") print("\nPerfect numbers are rare gems in the integer landscape!")
PRIME FACTORIZATION WORKSHOP Unique Prime Factorizations: 60 = 2^2 * 3 * 5 ✓ 315 = 3^2 * 5 * 7 ✓ 1001 = 7 * 11 * 13 ✓ 2310 = 2 * 3 * 5 * 7 * 11 ✓ 9999 = 3^2 * 11 * 101 ✓ Divisors and the σ functions: n | d(n) | σ(n) | Divisors --------------------------------------------- 12 | 6 | 28 | 1, 2, 3, 4, 6, 12 24 | 8 | 60 | 1, 2, 3, 4, 6, 8, 12, 24 30 | 8 | 72 | 1, 2, 3, 5, 6, 10, 15, 30 60 | 12 | 168 | 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60 Perfect Numbers (up to 10,000): [6, 28, 496, 8128] Example: 6 = 1 + 2 + 3 = 6 Perfect numbers are rare gems in the integer landscape!
# Sieve of Eratosthenes - Ancient Algorithm, Modern Implementation print("SIEVE OF ERATOSTHENES (circa 250 BCE)\n") def sieve_of_eratosthenes_demo(n): """ Implement and demonstrate the Sieve of Eratosthenes """ print(f"Finding all primes up to {n} using the Sieve:") # Initialize: all numbers from 2 to n are potentially prime is_prime = [True] * (n + 1) is_prime[0] = is_prime[1] = False # 0 and 1 are not prime primes_found = [] for p in range(2, int(n**0.5) + 1): if is_prime[p]: print(f"\nSieving with prime {p}:") # Mark all multiples of p as composite multiples_marked = 0 for i in range(p * p, n + 1, p): if is_prime[i]: is_prime[i] = False multiples_marked += 1 print(f" Marked {multiples_marked} multiples of {p}") # Collect all remaining primes primes = [i for i in range(2, n + 1) if is_prime[i]] print(f"\nPrimes found: {primes}") print(f"Total count: {len(primes)}") return primes # Demonstrate with small example sieve_primes = sieve_of_eratosthenes_demo(30) # Compare with SageMath's built-in prime generation sagemath_primes = [p for p in range(2, 31) if is_prime(p)] print(f"\nVerification with SageMath: {sagemath_primes}") print(f"Results match: {sieve_primes == sagemath_primes}") print("\n⚡ The Sieve is incredibly efficient for finding many primes at once!") print(" Time complexity: O(n log log n) - much faster than testing each number individually.")
SIEVE OF ERATOSTHENES (circa 250 BCE) Finding all primes up to 30 using the Sieve: Sieving with prime 2: Marked 14 multiples of 2 Sieving with prime 3: Marked 4 multiples of 3 Sieving with prime 5: Marked 1 multiples of 5 Primes found: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] Total count: 10 Verification with SageMath: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] Results match: True ⚡ The Sieve is incredibly efficient for finding many primes at once! Time complexity: O(n log log n) - much faster than testing each number individually.

Chapter 2: The Euclidean Algorithm - Finding Common Ground

The Euclidean Algorithm is one of the oldest algorithms in mathematics (circa 300 BCE). It efficiently finds the greatest common divisor of two numbers.

Key Insight:

gcd(a,b)=gcd(b,amodb)\gcd(a, b) = \gcd(b, a \bmod b)

This simple recursive relationship leads to profound mathematical insights!

# Chapter 2: The Euclidean Algorithm print("THE EUCLIDEAN ALGORITHM\n") def euclidean_algorithm_detailed(a, b, show_steps=True): """ Perform Euclidean algorithm with detailed step-by-step output """ if show_steps: print(f"Finding gcd({a}, {b}):") print(f"{'Step':>4} | {'Equation':>20} | {'Remainder':>10}") print("-" * 40) original_a, original_b = a, b step = 1 while b != 0: quotient = a // b remainder = a % b if show_steps: equation = f"{a} = {b}×{quotient} + {remainder}" print(f"{step:4d} | {equation:>20} | {remainder:>10}") a, b = b, remainder step += 1 if show_steps: print(f"\nResult: gcd({original_a}, {original_b}) = {a}") return a # Demonstrate with classic examples test_pairs = [(1071, 462), (252, 198), (48, 18)] for a, b in test_pairs: our_result = euclidean_algorithm_detailed(a, b) sage_result = gcd(a, b) print(f"Verification: SageMath gcd({a}, {b}) = {sage_result} {'✓' if our_result == sage_result else '✗'}") print("=" * 50) # GCD properties and applications print("\nGCD PROPERTIES AND RELATIONSHIPS\n") print("Key Identity: gcd(a,b) × lcm(a,b) = a × b") print("Verification:") for a, b in [(12, 18), (15, 25), (7, 11)]: gcd_ab = gcd(a, b) lcm_ab = lcm(a, b) product1 = gcd_ab * lcm_ab product2 = a * b print(f" gcd({a},{b}) × lcm({a},{b}) = {gcd_ab} × {lcm_ab} = {product1}") print(f" {a} × {b} = {product2} {'✓' if product1 == product2 else '✗'}") print() # Coprimality (relatively prime) print("Coprime Numbers (gcd = 1):") test_coprime = [(8, 15), (21, 22), (25, 49), (17, 19)] for a, b in test_coprime: gcd_result = gcd(a, b) coprime_status = "coprime" if gcd_result == 1 else "not coprime" print(f" gcd({a:2d}, {b:2d}) = {gcd_result}{coprime_status}")
THE EUCLIDEAN ALGORITHM Finding gcd(1071, 462): Step | Equation | Remainder ---------------------------------------- 1 | 1071 = 462×2 + 147 | 147 2 | 462 = 147×3 + 21 | 21 3 | 147 = 21×7 + 0 | 0 Result: gcd(1071, 462) = 21 Verification: SageMath gcd(1071, 462) = 21 ✓ ================================================== Finding gcd(252, 198): Step | Equation | Remainder ---------------------------------------- 1 | 252 = 198×1 + 54 | 54 2 | 198 = 54×3 + 36 | 36 3 | 54 = 36×1 + 18 | 18 4 | 36 = 18×2 + 0 | 0 Result: gcd(252, 198) = 18 Verification: SageMath gcd(252, 198) = 18 ✓ ================================================== Finding gcd(48, 18): Step | Equation | Remainder ---------------------------------------- 1 | 48 = 18×2 + 12 | 12 2 | 18 = 12×1 + 6 | 6 3 | 12 = 6×2 + 0 | 0 Result: gcd(48, 18) = 6 Verification: SageMath gcd(48, 18) = 6 ✓ ================================================== GCD PROPERTIES AND RELATIONSHIPS Key Identity: gcd(a,b) × lcm(a,b) = a × b Verification: gcd(12,18) × lcm(12,18) = 6 × 36 = 216 12 × 18 = 216 ✓ gcd(15,25) × lcm(15,25) = 5 × 75 = 375 15 × 25 = 375 ✓ gcd(7,11) × lcm(7,11) = 1 × 77 = 77 7 × 11 = 77 ✓ Coprime Numbers (gcd = 1): gcd( 8, 15) = 1 → coprime gcd(21, 22) = 1 → coprime gcd(25, 49) = 1 → coprime gcd(17, 19) = 1 → coprime
# Extended Euclidean Algorithm - Bézout's Identity print("🔧 EXTENDED EUCLIDEAN ALGORITHM\n") print("Finds integers x, y such that ax + by = gcd(a, b)") print("This is called Bézout's Identity!") print() def extended_euclidean_demo(a, b): """ Demonstrate the extended Euclidean algorithm """ print(f"Finding x, y such that {a}x + {b}y = gcd({a}, {b})") # Use SageMath's extended GCD g, x, y = xgcd(a, b) print(f"\nResult:") print(f" gcd({a}, {b}) = {g}") print(f" Bézout coefficients: x = {x}, y = {y}") print(f" Verification: {a}({x}) + {b}({y}) = {a*x + b*y}") # Check the identity check = a*x + b*y == g print(f" Identity holds: {check} {'✓' if check else '✗'}") return g, x, y # Examples of extended Euclidean algorithm examples = [(240, 46), (161, 28), (1071, 462)] for a, b in examples: extended_euclidean_demo(a, b) print("-" * 40) print("\nApplications of Extended Euclidean Algorithm:") print(" • Solving linear Diophantine equations") print(" • Computing modular inverses") print(" • Cryptographic algorithms (RSA key generation)") print(" • Simplifying fractions") # Practical example: Modular inverse print("\nComputing Modular Inverse:") print("To find a⁻¹ mod m, we solve ax + my = 1") a, m = 7, 26 g, x, y = xgcd(a, m) if g == 1: inverse = x % m print(f"\nFinding {a}⁻¹ mod {m}:") print(f" {a}({x}) + {m}({y}) = {g}") print(f" Therefore, {a}⁻¹ ≡ {inverse} (mod {m})") # Verify verification = (a * inverse) % m print(f" Check: {a} × {inverse}{verification} (mod {m}) {'✓' if verification == 1 else '✗'}") else: print(f" No inverse exists since gcd({a}, {m}) = {g} ≠ 1")
🔧 EXTENDED EUCLIDEAN ALGORITHM Finds integers x, y such that ax + by = gcd(a, b) This is called Bézout's Identity! Finding x, y such that 240x + 46y = gcd(240, 46) Result: gcd(240, 46) = 2 Bézout coefficients: x = -9, y = 47 Verification: 240(-9) + 46(47) = 2 Identity holds: True ✓ ---------------------------------------- Finding x, y such that 161x + 28y = gcd(161, 28) Result: gcd(161, 28) = 7 Bézout coefficients: x = -1, y = 6 Verification: 161(-1) + 28(6) = 7 Identity holds: True ✓ ---------------------------------------- Finding x, y such that 1071x + 462y = gcd(1071, 462) Result: gcd(1071, 462) = 21 Bézout coefficients: x = -3, y = 7 Verification: 1071(-3) + 462(7) = 21 Identity holds: True ✓ ---------------------------------------- Applications of Extended Euclidean Algorithm: • Solving linear Diophantine equations • Computing modular inverses • Cryptographic algorithms (RSA key generation) • Simplifying fractions Computing Modular Inverse: To find a⁻¹ mod m, we solve ax + my = 1 Finding 7⁻¹ mod 26: 7(-11) + 26(3) = 1 Therefore, 7⁻¹ ≡ 15 (mod 26) Check: 7 × 15 ≡ 1 (mod 26) ✓

Chapter 3: Modular Arithmetic - Mathematics with Wrap-Around

Modular arithmetic is arithmetic "modulo" a fixed positive integer called the modulus.

We write: ab(modm)a \equiv b \pmod{m}

This means "a and b have the same remainder when divided by m".

Why It Matters:

  • Clock arithmetic (12-hour cycles)

  • Computer arithmetic (fixed bit sizes)

  • Cryptography (RSA, elliptic curves)

  • Abstract algebra (groups, rings)

# Chapter 3: Modular Arithmetic Fundamentals print(" MODULAR ARITHMETIC FUNDAMENTALS\n") # Basic modular arithmetic operations def create_modular_table(operation, mod): """ Create and display modular arithmetic table """ print(f"Modular {operation} table (mod {mod}):") # Header print(f" {operation[0]}", end="") for j in range(mod): print(f"{j:3d}", end="") print() # Rows for i in range(mod): print(f"{i:3d}", end="") for j in range(mod): if operation == "addition": result = (i + j) % mod elif operation == "multiplication": result = (i * j) % mod print(f"{result:3d}", end="") print() print() # Show modular arithmetic tables create_modular_table("addition", 7) create_modular_table("multiplication", 7) # Modular exponentiation patterns print("Powers modulo 7:") print("Base | Powers mod 7") print("-" * 25) mod = 7 for base in range(1, mod): powers = [] for exp in range(1, mod): power_result = power_mod(base, exp, mod) powers.append(str(power_result)) powers_str = ', '.join(powers) print(f"{base:4d} | {powers_str}") print("\n🎯 Notice: Some bases generate all non-zero remainders!") print(" These are called 'primitive roots' or 'generators'.") # Find primitive roots def find_primitive_roots(p): """Find primitive roots modulo prime p""" if not is_prime(p): return [] primitive_roots = [] phi_p = p - 1 # Euler's totient of prime p for g in range(2, p): # Check if g generates all non-zero elements generated = set() for k in range(1, p): generated.add(power_mod(g, k, p)) if len(generated) == phi_p: primitive_roots.append(g) return primitive_roots p = 7 roots = find_primitive_roots(p) print(f"\nPrimitive roots modulo {p}: {roots}") print(f"These bases generate all {p-1} non-zero remainders when raised to different powers.")
MODULAR ARITHMETIC FUNDAMENTALS Modular addition table (mod 7): a 0 1 2 3 4 5 6 0 0 1 2 3 4 5 6 1 1 2 3 4 5 6 0 2 2 3 4 5 6 0 1 3 3 4 5 6 0 1 2 4 4 5 6 0 1 2 3 5 5 6 0 1 2 3 4 6 6 0 1 2 3 4 5 Modular multiplication table (mod 7): m 0 1 2 3 4 5 6 0 0 0 0 0 0 0 0 1 0 1 2 3 4 5 6 2 0 2 4 6 1 3 5 3 0 3 6 2 5 1 4 4 0 4 1 5 2 6 3 5 0 5 3 1 6 4 2 6 0 6 5 4 3 2 1 Powers modulo 7: Base | Powers mod 7 ------------------------- 1 | 1, 1, 1, 1, 1, 1 2 | 2, 4, 1, 2, 4, 1 3 | 3, 2, 6, 4, 5, 1 4 | 4, 2, 1, 4, 2, 1 5 | 5, 4, 6, 2, 3, 1 6 | 6, 1, 6, 1, 6, 1 🎯 Notice: Some bases generate all non-zero remainders! These are called 'primitive roots' or 'generators'. Primitive roots modulo 7: [3, 5] These bases generate all 6 non-zero remainders when raised to different powers.
# Fast Modular Exponentiation print("⚡ FAST MODULAR EXPONENTIATION\n") def modular_exp_binary_demo(base, exp, mod): """ Demonstrate binary exponentiation for modular arithmetic """ print(f"Computing {base}^{exp} mod {mod} using binary method:") # Show why this is necessary if exp > 20: digits = int(exp * log(base, 10)) if base > 1 else 1 print(f"Direct computation of {base}^{exp} would have ~{digits} digits!") # Convert exponent to binary binary_exp = bin(exp)[2:] # Remove '0b' prefix print(f"\nExponent {exp} in binary: {binary_exp}") # Binary exponentiation algorithm result = 1 base_power = base % mod print(f"\nStep-by-step computation:") print(f"Bit | Power | Result") print("-" * 25) for i, bit in enumerate(reversed(binary_exp)): if bit == '1': result = (result * base_power) % mod print(f" {i} | {base_power:4d} | {result:4d}") if i < len(binary_exp) - 1: # Don't square on last iteration base_power = (base_power * base_power) % mod print(f"\nFinal result: {base}^{exp}{result} (mod {mod})") return result # Examples of fast modular exponentiation examples = [(3, 13, 7), (2, 100, 17), (123, 456, 789)] for base, exp, mod in examples: our_result = modular_exp_binary_demo(base, exp, mod) sage_result = power_mod(base, exp, mod) print(f"SageMath verification: {sage_result} {'✓' if our_result == sage_result else '✗'}") print("=" * 50) print("\n Why Fast Exponentiation Matters:") print(" • RSA encryption uses 1024+ bit exponents") print(" • Naive method would take longer than age of universe") print(" • Binary method reduces O(n) to O(log n) multiplications") print(" • Essential for modern cryptography")
⚡ FAST MODULAR EXPONENTIATION Computing 3^13 mod 7 using binary method: Exponent 13 in binary: 1101 Step-by-step computation: Bit | Power | Result ------------------------- 0 | 3 | 3 2 | 4 | 5 3 | 2 | 3 Final result: 3^13 ≡ 3 (mod 7) SageMath verification: 3 ✓ ================================================== Computing 2^100 mod 17 using binary method: Direct computation of 2^100 would have ~30 digits! Exponent 100 in binary: 1100100 Step-by-step computation: Bit | Power | Result ------------------------- 2 | 16 | 16 5 | 1 | 16 6 | 1 | 16 Final result: 2^100 ≡ 16 (mod 17) SageMath verification: 16 ✓ ================================================== Computing 123^456 mod 789 using binary method: Direct computation of 123^456 would have ~952 digits! Exponent 456 in binary: 111001000 Step-by-step computation: Bit | Power | Result ------------------------- 3 | 618 | 618 6 | 24 | 630 7 | 576 | 729 8 | 396 | 699 Final result: 123^456 ≡ 699 (mod 789) SageMath verification: 699 ✓ ================================================== Why Fast Exponentiation Matters: • RSA encryption uses 1024+ bit exponents • Naive method would take longer than age of universe • Binary method reduces O(n) to O(log n) multiplications • Essential for modern cryptography
# Fermat's Little Theorem print("FERMAT'S LITTLE THEOREM\n") print("If p is prime and gcd(a, p) = 1, then a^(p-1) ≡ 1 (mod p)") print("This is one of the most beautiful results in number theory!") print() # Verify Fermat's Little Theorem def verify_fermat_little_theorem(p): """Verify FLT for all valid bases modulo prime p""" if not is_prime(p): print(f"Error: {p} is not prime") return False print(f"Testing Fermat's Little Theorem for prime p = {p}:") print(f"{'Base a':>6} | {'a^{}'.format(p-1):>8} | {'Result mod {}'.format(p):>12}") print("-" * 35) all_pass = True for a in range(1, min(p, 10)): # Test first few bases if gcd(a, p) == 1: # Should always be true for prime p result = power_mod(a, p-1, p) status = "✓" if result == 1 else "✗" print(f"{a:6d} | {p-1:8d} | {result:8d} {status:>3}") if result != 1: all_pass = False print(f"\nAll tests pass: {all_pass}") return all_pass # Test with several primes test_primes = [5, 7, 11, 13] for p in test_primes: verify_fermat_little_theorem(p) print() # Fermat primality test and pseudoprimes print("FERMAT PRIMALITY TEST\n") print("Idea: If n is composite, then a^(n-1) ≢ 1 (mod n) for most bases a") print("But some composites fool us - these are Fermat pseudoprimes!") print() def fermat_test(n, base=2): """Fermat primality test with given base""" if gcd(base, n) != 1: return False return power_mod(base, n-1, n) == 1 # Test some Carmichael numbers (fool all coprime bases) carmichael_numbers = [561, 1105, 1729, 2465, 2821] print("Testing Carmichael numbers (composite but pass Fermat test):") print(f"{'Number':>6} | {'Prime?':>7} | {'Fermat(base=2)':>13} | {'Pseudoprime?':>12}") print("-" * 45) for n in carmichael_numbers: is_prime_actual = is_prime(n) fermat_result = fermat_test(n, 2) is_pseudoprime = fermat_result and not is_prime_actual print(f"{n:6d} | {str(is_prime_actual):>7} | {str(fermat_result):>13} | {str(is_pseudoprime):>12}") print("\n⚠Lesson: Fermat's test is probabilistic, not definitive!") print(" Modern primality tests use more sophisticated methods.")
FERMAT'S LITTLE THEOREM If p is prime and gcd(a, p) = 1, then a^(p-1) ≡ 1 (mod p) This is one of the most beautiful results in number theory! Testing Fermat's Little Theorem for prime p = 5: Base a | a^4 | Result mod 5 ----------------------------------- 1 | 4 | 1 ✓ 2 | 4 | 1 ✓ 3 | 4 | 1 ✓ 4 | 4 | 1 ✓ All tests pass: True Testing Fermat's Little Theorem for prime p = 7: Base a | a^6 | Result mod 7 ----------------------------------- 1 | 6 | 1 ✓ 2 | 6 | 1 ✓ 3 | 6 | 1 ✓ 4 | 6 | 1 ✓ 5 | 6 | 1 ✓ 6 | 6 | 1 ✓ All tests pass: True Testing Fermat's Little Theorem for prime p = 11: Base a | a^10 | Result mod 11 ----------------------------------- 1 | 10 | 1 ✓ 2 | 10 | 1 ✓ 3 | 10 | 1 ✓ 4 | 10 | 1 ✓ 5 | 10 | 1 ✓ 6 | 10 | 1 ✓ 7 | 10 | 1 ✓ 8 | 10 | 1 ✓ 9 | 10 | 1 ✓ All tests pass: True Testing Fermat's Little Theorem for prime p = 13: Base a | a^12 | Result mod 13 ----------------------------------- 1 | 12 | 1 ✓ 2 | 12 | 1 ✓ 3 | 12 | 1 ✓ 4 | 12 | 1 ✓ 5 | 12 | 1 ✓ 6 | 12 | 1 ✓ 7 | 12 | 1 ✓ 8 | 12 | 1 ✓ 9 | 12 | 1 ✓ All tests pass: True FERMAT PRIMALITY TEST Idea: If n is composite, then a^(n-1) ≢ 1 (mod n) for most bases a But some composites fool us - these are Fermat pseudoprimes! Testing Carmichael numbers (composite but pass Fermat test): Number | Prime? | Fermat(base=Integer(2)) | Pseudoprime? --------------------------------------------- 561 | False | True | True 1105 | False | True | True 1729 | False | True | True 2465 | False | True | True 2821 | False | True | True ⚠Lesson: Fermat's test is probabilistic, not definitive! Modern primality tests use more sophisticated methods.

Chapter 4: Congruences and the Chinese Remainder Theorem

Congruences are equations involving modular arithmetic. The Chinese Remainder Theorem is a powerful tool for solving systems of congruences.

The Big Idea:

If we know the remainders of a number when divided by several pairwise coprime moduli, we can uniquely determine the number modulo their product.

This has applications in:

  • Computer arithmetic

  • Cryptography

  • Calendar systems

  • Error-correcting codes

# Chapter 4: Linear Congruences print("SOLVING LINEAR CONGRUENCES\n") print("Form: ax ≡ b (mod m)") print("Goal: Find all values of x that satisfy the congruence") print() def solve_linear_congruence_detailed(a, b, m): """ Solve ax ≡ b (mod m) with detailed explanation """ print(f"Solving {a}x ≡ {b} (mod {m})") print("-" * 30) # Step 1: Check if solution exists g = gcd(a, m) print(f"Step 1: gcd({a}, {m}) = {g}") if b % g != 0: print(f"No solution: {g} does not divide {b}") return [] print(f"Solution exists: {g} divides {b}") # Step 2: Reduce the congruence a_reduced = a // g b_reduced = b // g m_reduced = m // g print(f"Step 2: Reduce by dividing by {g}") print(f" {a_reduced}x ≡ {b_reduced} (mod {m_reduced})") # Step 3: Find modular inverse gcd_result, inv_a, _ = xgcd(a_reduced, m_reduced) inv_a = inv_a % m_reduced print(f"Step 3: Find inverse of {a_reduced} mod {m_reduced}") print(f" {a_reduced}^(-1) ≡ {inv_a} (mod {m_reduced})") # Step 4: Solve for x x0 = (inv_a * b_reduced) % m_reduced print(f"Step 4: x ≡ {inv_a} × {b_reduced}{x0} (mod {m_reduced})") # Step 5: Generate all solutions solutions = [(x0 + i * m_reduced) % m for i in range(g)] solutions.sort() print(f"Step 5: All solutions modulo {m}:") for i, sol in enumerate(solutions): print(f" x ≡ {sol} (mod {m})") # Verification print(f"Verification:") for sol in solutions: result = (a * sol) % m status = "✓" if result == b % m else "✗" print(f" {a} × {sol}{result}{b} (mod {m}) {status}") return solutions # Examples of linear congruences examples = [ (3, 7, 10), # Unique solution (6, 9, 15), # Multiple solutions (4, 6, 10), # No solution (5, 3, 7) # Coprime case ] for a, b, m in examples: solve_linear_congruence_detailed(a, b, m) print("=" * 60) print("\n💡 Key Insight: Linear congruences generalize linear equations to modular arithmetic!")
SOLVING LINEAR CONGRUENCES Form: ax ≡ b (mod m) Goal: Find all values of x that satisfy the congruence Solving 3x ≡ 7 (mod 10) ------------------------------ Step 1: gcd(3, 10) = 1 Solution exists: 1 divides 7 Step 2: Reduce by dividing by 1 3x ≡ 7 (mod 10) Step 3: Find inverse of 3 mod 10 3^(-1) ≡ 7 (mod 10) Step 4: x ≡ 7 × 7 ≡ 9 (mod 10) Step 5: All solutions modulo 10: x ≡ 9 (mod 10) Verification: 3 × 9 ≡ 7 ≡ 7 (mod 10) ✓ ============================================================ Solving 6x ≡ 9 (mod 15) ------------------------------ Step 1: gcd(6, 15) = 3 Solution exists: 3 divides 9 Step 2: Reduce by dividing by 3 2x ≡ 3 (mod 5) Step 3: Find inverse of 2 mod 5 2^(-1) ≡ 3 (mod 5) Step 4: x ≡ 3 × 3 ≡ 4 (mod 5) Step 5: All solutions modulo 15: x ≡ 4 (mod 15) x ≡ 9 (mod 15) x ≡ 14 (mod 15) Verification: 6 × 4 ≡ 9 ≡ 9 (mod 15) ✓ 6 × 9 ≡ 9 ≡ 9 (mod 15) ✓ 6 × 14 ≡ 9 ≡ 9 (mod 15) ✓ ============================================================ Solving 4x ≡ 6 (mod 10) ------------------------------ Step 1: gcd(4, 10) = 2 Solution exists: 2 divides 6 Step 2: Reduce by dividing by 2 2x ≡ 3 (mod 5) Step 3: Find inverse of 2 mod 5 2^(-1) ≡ 3 (mod 5) Step 4: x ≡ 3 × 3 ≡ 4 (mod 5) Step 5: All solutions modulo 10: x ≡ 4 (mod 10) x ≡ 9 (mod 10) Verification: 4 × 4 ≡ 6 ≡ 6 (mod 10) ✓ 4 × 9 ≡ 6 ≡ 6 (mod 10) ✓ ============================================================ Solving 5x ≡ 3 (mod 7) ------------------------------ Step 1: gcd(5, 7) = 1 Solution exists: 1 divides 3 Step 2: Reduce by dividing by 1 5x ≡ 3 (mod 7) Step 3: Find inverse of 5 mod 7 5^(-1) ≡ 3 (mod 7) Step 4: x ≡ 3 × 3 ≡ 2 (mod 7) Step 5: All solutions modulo 7: x ≡ 2 (mod 7) Verification: 5 × 2 ≡ 3 ≡ 3 (mod 7) ✓ ============================================================ 💡 Key Insight: Linear congruences generalize linear equations to modular arithmetic!
# Chinese Remainder Theorem print("CHINESE REMAINDER THEOREM\n") print("Ancient Chinese text: 'There are certain things whose number is unknown.'") print("'When divided by 3, the remainder is 2.'") print("'When divided by 5, the remainder is 3.'") print("'When divided by 7, the remainder is 2.'") print("'What will be the number of things?'") print() def chinese_remainder_theorem_detailed(remainders, moduli): """ Solve system of congruences with detailed steps """ n = len(remainders) print("System of congruences:") for i, (a, m) in enumerate(zip(remainders, moduli)): print(f" x ≡ {a} (mod {m})") print() # Check pairwise coprimality print("Step 1: Check that moduli are pairwise coprime") for i in range(n): for j in range(i+1, n): g = gcd(moduli[i], moduli[j]) if g != 1: print(f"Error: gcd({moduli[i]}, {moduli[j]}) = {g} ≠ 1") return None print(f" gcd({moduli[i]}, {moduli[j]}) = {g} ✓") # Compute M M = 1 for m in moduli: M *= m print(f"\nStep 2: Compute M = {' × '.join(map(str, moduli))} = {M}") # CRT construction print(f"\nStep 3: CRT construction") x = 0 for i, (a, m) in enumerate(zip(remainders, moduli)): Mi = M // m print(f" For x ≡ {a} (mod {m}):") print(f" M_{i+1} = M/m_{i+1} = {M}/{m} = {Mi}") # Find modular inverse g, yi, _ = xgcd(Mi, m) yi = yi % m print(f" y_{i+1} = M_{i+1}^(-1) mod m_{i+1} = {yi}") # Verify inverse check = (Mi * yi) % m print(f" Verify: {Mi} × {yi}{check} (mod {m}) {'✓' if check == 1 else '✗'}") # Add contribution contribution = a * Mi * yi x += contribution print(f" Contribution: {a} × {Mi} × {yi} = {contribution}") print() # Final answer x = x % M print(f"Step 4: Final solution") print(f" x ≡ {x} (mod {M})") # Verification print(f"\nStep 5: Verification") all_correct = True for a, m in zip(remainders, moduli): remainder = x % m correct = remainder == a status = "✓" if correct else "✗" print(f" {x}{remainder}{a} (mod {m}) {status}") if not correct: all_correct = False print(f"\nAll verifications pass: {all_correct}") return x # Solve the classic Chinese problem print("CLASSIC CHINESE PROBLEM:") remainders_classic = [2, 3, 2] moduli_classic = [3, 5, 7] solution_classic = chinese_remainder_theorem_detailed(remainders_classic, moduli_classic) print("=" * 70) # Modern example print("\nMODERN EXAMPLE:") remainders_modern = [1, 2, 3, 4] moduli_modern = [5, 7, 9, 11] solution_modern = chinese_remainder_theorem_detailed(remainders_modern, moduli_modern) # Verify with SageMath print("\n🔧 SAGEMATH VERIFICATION:") sage_solution1 = crt(remainders_classic, moduli_classic) sage_solution2 = crt(remainders_modern, moduli_modern) print(f"Classic problem: Our solution = {solution_classic}, SageMath = {sage_solution1}") print(f"Modern example: Our solution = {solution_modern}, SageMath = {sage_solution2}") match1 = solution_classic == sage_solution1 match2 = solution_modern == sage_solution2 print(f"Results match: {match1 and match2} {'✓' if match1 and match2 else '✗'}")
CHINESE REMAINDER THEOREM Ancient Chinese text: 'There are certain things whose number is unknown.' 'When divided by 3, the remainder is 2.' 'When divided by 5, the remainder is 3.' 'When divided by 7, the remainder is 2.' 'What will be the number of things?' CLASSIC CHINESE PROBLEM: System of congruences: x ≡ 2 (mod 3) x ≡ 3 (mod 5) x ≡ 2 (mod 7) Step 1: Check that moduli are pairwise coprime gcd(3, 5) = 1 ✓ gcd(3, 7) = 1 ✓ gcd(5, 7) = 1 ✓ Step 2: Compute M = 3 × 5 × 7 = 105 Step 3: CRT construction For x ≡ 2 (mod 3): M_1 = M/m_1 = 105/3 = 35 y_1 = M_1^(-1) mod m_1 = 2 Verify: 35 × 2 ≡ 1 (mod 3) ✓ Contribution: 2 × 35 × 2 = 140 For x ≡ 3 (mod 5): M_2 = M/m_2 = 105/5 = 21 y_2 = M_2^(-1) mod m_2 = 1 Verify: 21 × 1 ≡ 1 (mod 5) ✓ Contribution: 3 × 21 × 1 = 63 For x ≡ 2 (mod 7): M_3 = M/m_3 = 105/7 = 15 y_3 = M_3^(-1) mod m_3 = 1 Verify: 15 × 1 ≡ 1 (mod 7) ✓ Contribution: 2 × 15 × 1 = 30 Step 4: Final solution x ≡ 23 (mod 105) Step 5: Verification 23 ≡ 2 ≡ 2 (mod 3) ✓ 23 ≡ 3 ≡ 3 (mod 5) ✓ 23 ≡ 2 ≡ 2 (mod 7) ✓ All verifications pass: True ====================================================================== MODERN EXAMPLE: System of congruences: x ≡ 1 (mod 5) x ≡ 2 (mod 7) x ≡ 3 (mod 9) x ≡ 4 (mod 11) Step 1: Check that moduli are pairwise coprime gcd(5, 7) = 1 ✓ gcd(5, 9) = 1 ✓ gcd(5, 11) = 1 ✓ gcd(7, 9) = 1 ✓ gcd(7, 11) = 1 ✓ gcd(9, 11) = 1 ✓ Step 2: Compute M = 5 × 7 × 9 × 11 = 3465 Step 3: CRT construction For x ≡ 1 (mod 5): M_1 = M/m_1 = 3465/5 = 693 y_1 = M_1^(-1) mod m_1 = 2 Verify: 693 × 2 ≡ 1 (mod 5) ✓ Contribution: 1 × 693 × 2 = 1386 For x ≡ 2 (mod 7): M_2 = M/m_2 = 3465/7 = 495 y_2 = M_2^(-1) mod m_2 = 3 Verify: 495 × 3 ≡ 1 (mod 7) ✓ Contribution: 2 × 495 × 3 = 2970 For x ≡ 3 (mod 9): M_3 = M/m_3 = 3465/9 = 385 y_3 = M_3^(-1) mod m_3 = 4 Verify: 385 × 4 ≡ 1 (mod 9) ✓ Contribution: 3 × 385 × 4 = 4620 For x ≡ 4 (mod 11): M_4 = M/m_4 = 3465/11 = 315 y_4 = M_4^(-1) mod m_4 = 8 Verify: 315 × 8 ≡ 1 (mod 11) ✓ Contribution: 4 × 315 × 8 = 10080 Step 4: Final solution x ≡ 1731 (mod 3465) Step 5: Verification 1731 ≡ 1 ≡ 1 (mod 5) ✓ 1731 ≡ 2 ≡ 2 (mod 7) ✓ 1731 ≡ 3 ≡ 3 (mod 9) ✓ 1731 ≡ 4 ≡ 4 (mod 11) ✓ All verifications pass: True 🔧 SAGEMATH VERIFICATION: Classic problem: Our solution = 23, SageMath = 23 Modern example: Our solution = 1731, SageMath = 1731 Results match: True ✓

Chapter 5: Number Theory Functions

Several important functions appear throughout number theory:

Euler's Totient Function φ(n):

Counts integers from 1 to n that are coprime to n

Divisor Functions:

  • τ(n) or d(n): Number of divisors

  • σ(n): Sum of divisors

Möbius Function μ(n):

Detects square-free numbers and their prime factor count

These functions reveal deep arithmetic properties!

# Chapter 5: Euler's Totient Function print("φ EULER'S TOTIENT FUNCTION\n") print("φ(n) = number of integers from 1 to n that are coprime to n") print("This function is fundamental to RSA cryptography!") print() # Compute φ(n) and show coprime numbers print("Totient values with coprime numbers:") print(f"{'n':>3} | {'φ(n)':>4} | Coprime numbers") print("-" * 50) for n in range(1, 16): phi_n = euler_phi(n) coprimes = [i for i in range(1, n+1) if gcd(i, n) == 1] coprime_str = ', '.join(map(str, coprimes)) print(f"{n:3d} | {phi_n:4d} | {coprime_str}") print() # Properties of Euler's totient function print("🔍 PROPERTIES OF φ(n):\n") # Property 1: φ(p) = p-1 for prime p print("1. For prime p: φ(p) = p - 1") small_primes = [p for p in range(2, 20) if is_prime(p)] for p in small_primes[:6]: phi_p = euler_phi(p) print(f" φ({p:2d}) = {phi_p:2d} = {p} - 1") # Property 2: φ(p^k) formula print("\n2. For prime power p^k: φ(p^k) = p^(k-1)(p-1)") prime_power_examples = [(2, 3), (3, 2), (5, 2)] for p, k in prime_power_examples: n = p**k phi_n = euler_phi(n) formula_result = p**(k-1) * (p-1) print(f" φ({p}^{k}) = φ({n:2d}) = {phi_n:2d} = {p}^{k-1} × ({p}-1) = {formula_result}") # Property 3: Multiplicativity print("\n3. φ is multiplicative: if gcd(m,n) = 1, then φ(mn) = φ(m)φ(n)") mult_examples = [(3, 5), (4, 9), (7, 8)] for m, n in mult_examples: if gcd(m, n) == 1: phi_m = euler_phi(m) phi_n = euler_phi(n) phi_mn = euler_phi(m * n) product = phi_m * phi_n status = "✓" if phi_mn == product else "✗" print(f" φ({m} × {n}) = φ({m*n:2d}) = {phi_mn:2d}, φ({m}) × φ({n}) = {phi_m} × {phi_n} = {product} {status}") # General formula using prime factorization print("\n4. General formula: φ(n) = n × ∏(1 - 1/p) for prime factors p") def show_totient_formula(n): factorization = factor(n) # e.g. 12 -> 2^2 * 3 factors_list = list(factorization) # [(2,2), (3,1)] print(f" n = {n}, factorization = {factorization}") # Apply $φ(n)=n\prod_{p|n}\left(1-\frac{1}{p}\right)$ result = n step_terms = [str(n)] for p, e in factors_list: result = result * (p - 1) // p step_terms.append(f"(1 - 1/{p})") formula_str = " × ".join(step_terms) print(f" φ({n}) = {formula_str} = {result}") for n in [12, 30, 60]: show_totient_formula(n) print("\n🔐 Application: In RSA cryptography, we use φ(pq) = (p-1)(q-1) where p, q are large primes!")
φ EULER'S TOTIENT FUNCTION φ(n) = number of integers from 1 to n that are coprime to n This function is fundamental to RSA cryptography! Totient values with coprime numbers: n | φ(n) | Coprime numbers -------------------------------------------------- 1 | 1 | 1 2 | 1 | 1 3 | 2 | 1, 2 4 | 2 | 1, 3 5 | 4 | 1, 2, 3, 4 6 | 2 | 1, 5 7 | 6 | 1, 2, 3, 4, 5, 6 8 | 4 | 1, 3, 5, 7 9 | 6 | 1, 2, 4, 5, 7, 8 10 | 4 | 1, 3, 7, 9 11 | 10 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 12 | 4 | 1, 5, 7, 11 13 | 12 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 14 | 6 | 1, 3, 5, 9, 11, 13 15 | 8 | 1, 2, 4, 7, 8, 11, 13, 14 🔍 PROPERTIES OF φ(n): 1. For prime p: φ(p) = p - 1 φ( 2) = 1 = 2 - 1 φ( 3) = 2 = 3 - 1 φ( 5) = 4 = 5 - 1 φ( 7) = 6 = 7 - 1 φ(11) = 10 = 11 - 1 φ(13) = 12 = 13 - 1 2. For prime power p^k: φ(p^k) = p^(k-1)(p-1) φ(2^3) = φ( 8) = 4 = 2^2 × (2-1) = 4 φ(3^2) = φ( 9) = 6 = 3^1 × (3-1) = 6 φ(5^2) = φ(25) = 20 = 5^1 × (5-1) = 20 3. φ is multiplicative: if gcd(m,n) = 1, then φ(mn) = φ(m)φ(n) φ(3 × 5) = φ(15) = 8, φ(3) × φ(5) = 2 × 4 = 8 ✓ φ(4 × 9) = φ(36) = 12, φ(4) × φ(9) = 2 × 6 = 12 ✓ φ(7 × 8) = φ(56) = 24, φ(7) × φ(8) = 6 × 4 = 24 ✓ 4. General formula: φ(n) = n × ∏(1 - 1/p) for prime factors p n = 12, factorization = 2^2 * 3 φ(12) = 12 × (1 - 1/2) × (1 - 1/3) = 4 n = 30, factorization = 2 * 3 * 5 φ(30) = 30 × (1 - 1/2) × (1 - 1/3) × (1 - 1/5) = 8 n = 60, factorization = 2^2 * 3 * 5 φ(60) = 60 × (1 - 1/2) × (1 - 1/3) × (1 - 1/5) = 16 🔐 Application: In RSA cryptography, we use φ(pq) = (p-1)(q-1) where p, q are large primes!
# Other Important Number Theory Functions print("OTHER NUMBER THEORY FUNCTIONS\n") # Divisor functions print("Divisor Functions:") print(f"{'n':>3} | {'d(n)':>4} | {'σ(n)':>5} | Divisors") print("-" * 45) for n in range(1, 21): divs = divisors(n) d_n = len(divs) # τ(n) or d(n) - number of divisors sigma_n = sum(divs) # σ(n) - sum of divisors divs_str = ', '.join(map(str, divs)) if len(divs_str) > 20: divs_str = divs_str[:17] + "..." print(f"{n:3d} | {d_n:4d} | {sigma_n:5d} | {divs_str}") print() # Perfect and related numbers print("SPECIAL NUMBERS BASED ON DIVISOR FUNCTIONS:\n") def classify_by_divisor_sum(limit): """Classify numbers by relationship between n and sum of proper divisors""" perfect = [] abundant = [] deficient = [] for n in range(2, limit): proper_divisors = [d for d in divisors(n) if d < n] divisor_sum = sum(proper_divisors) if divisor_sum == n: perfect.append(n) elif divisor_sum > n: abundant.append(n) else: deficient.append(n) return perfect, abundant[:10], deficient[:15] # Limit output perfect, abundant, deficient = classify_by_divisor_sum(100) print(f"Perfect numbers (σ(n) = 2n): {perfect}") if perfect: n = perfect[0] proper_divs = [d for d in divisors(n) if d < n] print(f" Example: {n} = {' + '.join(map(str, proper_divs))} = {sum(proper_divs)}") print(f"\nAbundant numbers (σ(n) > 2n): {abundant}") print(f"Deficient numbers (σ(n) < 2n): {deficient}") print() # Möbius function print("μ MÖBIUS FUNCTION:\n") print("μ(n) = 0 if n has a squared prime factor") print("μ(n) = (-1)^k if n is a product of k distinct primes") print("μ(1) = 1") print() print(f"{'n':>3} | {'μ(n)':>4} | {'Prime factorization':>20} | {'Square-free?':>12}") print("-" * 55) for n in range(1, 21): mu_n = moebius(n) factorization = factor(n) # Check if square-free (no repeated prime factors) is_square_free = all(exp == 1 for _, exp in factorization) if n > 1 else True print(f"{n:3d} | {mu_n:4d} | {str(factorization):>20} | {str(is_square_free):>12}") print("\n💡 Key insight: μ(n) ≠ 0 exactly when n is square-free!") # Möbius inversion formula example print("\nMöbius Inversion Formula:") print("If f(n) = Σ g(d) for d|n, then g(n) = Σ μ(d)f(n/d) for d|n") print("This powerful tool connects many number theory functions!") # Example: φ(n) in terms of divisor sums def verify_totient_mobius_formula(n): """Verify φ(n) = Σ μ(d) × (n/d) for d|n""" divs = divisors(n) mobius_sum = sum(moebius(d) * (n // d) for d in divs) actual_phi = euler_phi(n) print(f"φ({n}) = {actual_phi}, Möbius formula gives: {mobius_sum} {'✓' if mobius_sum == actual_phi else '✗'}") print("\nVerifying φ(n) = Σ μ(d) × (n/d) for divisors d of n:") for n in [6, 12, 15, 20]: verify_totient_mobius_formula(n)
OTHER NUMBER THEORY FUNCTIONS Divisor Functions: n | d(n) | σ(n) | Divisors --------------------------------------------- 1 | 1 | 1 | 1 2 | 2 | 3 | 1, 2 3 | 2 | 4 | 1, 3 4 | 3 | 7 | 1, 2, 4 5 | 2 | 6 | 1, 5 6 | 4 | 12 | 1, 2, 3, 6 7 | 2 | 8 | 1, 7 8 | 4 | 15 | 1, 2, 4, 8 9 | 3 | 13 | 1, 3, 9 10 | 4 | 18 | 1, 2, 5, 10 11 | 2 | 12 | 1, 11 12 | 6 | 28 | 1, 2, 3, 4, 6, 12 13 | 2 | 14 | 1, 13 14 | 4 | 24 | 1, 2, 7, 14 15 | 4 | 24 | 1, 3, 5, 15 16 | 5 | 31 | 1, 2, 4, 8, 16 17 | 2 | 18 | 1, 17 18 | 6 | 39 | 1, 2, 3, 6, 9, 18 19 | 2 | 20 | 1, 19 20 | 6 | 42 | 1, 2, 4, 5, 10, 20 SPECIAL NUMBERS BASED ON DIVISOR FUNCTIONS: Perfect numbers (σ(n) = 2n): [6, 28] Example: 6 = 1 + 2 + 3 = 6 Abundant numbers (σ(n) > 2n): [12, 18, 20, 24, 30, 36, 40, 42, 48, 54] Deficient numbers (σ(n) < 2n): [2, 3, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 19] μ MÖBIUS FUNCTION: μ(n) = 0 if n has a squared prime factor μ(n) = (-1)^k if n is a product of k distinct primes μ(1) = 1 n | μ(n) | Prime factorization | Square-free? ------------------------------------------------------- 20 | 0 | 2^2 * 5 | False 💡 Key insight: μ(n) ≠ 0 exactly when n is square-free! Möbius Inversion Formula: If f(n) = Σ g(d) for d|n, then g(n) = Σ μ(d)f(n/d) for d|n This powerful tool connects many number theory functions! Verifying φ(n) = Σ μ(d) × (n/d) for divisors d of n: φ(6) = 2, Möbius formula gives: 2 ✓ φ(12) = 4, Möbius formula gives: 4 ✓ φ(15) = 8, Möbius formula gives: 8 ✓ φ(20) = 8, Möbius formula gives: 8 ✓

Chapter 6: Diophantine Equations

Diophantine equations seek integer solutions to polynomial equations. Named after Diophantus of Alexandria (circa 250 CE), these problems bridge number theory and algebra.

Types We'll Explore:

  1. Linear: ax + by = c

  2. Pythagorean triples: x² + y² = z²

  3. Pell's equation: x² - Dy² = 1

Why They Matter:

  • Fundamental to algebraic number theory

  • Applications in cryptography

  • Beautiful mathematical structures

  • Connect to famous conjectures (Fermat's Last Theorem)

# Chapter 6: Linear Diophantine Equations print("LINEAR DIOPHANTINE EQUATIONS\n") print("Form: ax + by = c") print("Goal: Find integer solutions (x, y)") print("Method: Use Extended Euclidean Algorithm") print() def solve_linear_diophantine_complete(a, b, c): """ Completely solve linear Diophantine equation ax + by = c """ print(f"Solving {a}x + {b}y = {c}") print("-" * 40) # Step 1: Check solvability g = gcd(a, b) print(f"Step 1: gcd({a}, {b}) = {g}") if c % g != 0: print(f"No integer solutions: {g} does not divide {c}") return None print(f"Solutions exist: {g} divides {c}") # Step 2: Find particular solution using extended Euclidean algorithm gcd_result, x0, y0 = xgcd(a, b) # Scale to match c scale = c // g x_particular = x0 * scale y_particular = y0 * scale print(f"Step 2: Extended GCD gives {a}×{x0} + {b}×{y0} = {gcd_result}") print(f" Scaling by {scale}: {a}×{x_particular} + {b}×{y_particular} = {c}") # Verify particular solution check = a * x_particular + b * y_particular print(f" Verification: {a}×{x_particular} + {b}×{y_particular} = {check} {'✓' if check == c else '✗'}") # Step 3: General solution b_coeff = b // g a_coeff = a // g print(f"Step 3: General solution (for any integer t):") print(f" x = {x_particular} + {b_coeff}t") print(f" y = {y_particular} - {a_coeff}t") # Step 4: Show specific solutions print(f"Step 4: Specific solutions:") print(f"{'t':>3} | {'x':>6} | {'y':>6} | {'Check':>8}") print("-" * 30) for t in range(-3, 4): x_t = x_particular + b_coeff * t y_t = y_particular - a_coeff * t check_t = a * x_t + b * y_t print(f"{t:3d} | {x_t:6d} | {y_t:6d} | {check_t:8d}") return (x_particular, y_particular, b_coeff, -a_coeff) # Examples with different characteristics examples = [ (3, 5, 1), # Coprime coefficients (6, 9, 15), # Common factor that divides c (12, 18, 30), # Larger common factor (4, 6, 5), # No solution case ] for a, b, c in examples: result = solve_linear_diophantine_complete(a, b, c) print("="*60) print("\nApplications of Linear Diophantine Equations:") print(" • Making change with different coin denominations") print(" • Scheduling problems with time constraints") print(" • Resource allocation in optimization") print(" • Solving systems of congruences") # Practical example: Coin change problem print("\n💰 PRACTICAL EXAMPLE: Coin Change") print("How many ways can we make $1.00 using quarters (25¢) and dimes (10¢)?") print("Equation: 25x + 10y = 100 or simplified: 5x + 2y = 20") result = solve_linear_diophantine_complete(5, 2, 20) if result: x_part, y_part, b_coeff, a_coeff = result print("\nNon-negative solutions (valid coin counts):") valid_solutions = [] for t in range(-10, 11): x = x_part + b_coeff * t y = y_part + a_coeff * t if x >= 0 and y >= 0: valid_solutions.append((x, y)) print(f" {x} quarters + {y} dimes = ${float((25*x + 10*y)/100):.2f}") print(f"\nTotal ways: {len(valid_solutions)}")
LINEAR DIOPHANTINE EQUATIONS Form: ax + by = c Goal: Find integer solutions (x, y) Method: Use Extended Euclidean Algorithm Solving 3x + 5y = 1 ---------------------------------------- Step 1: gcd(3, 5) = 1 Solutions exist: 1 divides 1 Step 2: Extended GCD gives 3×2 + 5×-1 = 1 Scaling by 1: 3×2 + 5×-1 = 1 Verification: 3×2 + 5×-1 = 1 ✓ Step 3: General solution (for any integer t): x = 2 + 5t y = -1 - 3t Step 4: Specific solutions: t | x | y | Check ------------------------------ -3 | -13 | 8 | 1 -2 | -8 | 5 | 1 -1 | -3 | 2 | 1 0 | 2 | -1 | 1 1 | 7 | -4 | 1 2 | 12 | -7 | 1 3 | 17 | -10 | 1 ============================================================ Solving 6x + 9y = 15 ---------------------------------------- Step 1: gcd(6, 9) = 3 Solutions exist: 3 divides 15 Step 2: Extended GCD gives 6×-1 + 9×1 = 3 Scaling by 5: 6×-5 + 9×5 = 15 Verification: 6×-5 + 9×5 = 15 ✓ Step 3: General solution (for any integer t): x = -5 + 3t y = 5 - 2t Step 4: Specific solutions: t | x | y | Check ------------------------------ -3 | -14 | 11 | 15 -2 | -11 | 9 | 15 -1 | -8 | 7 | 15 0 | -5 | 5 | 15 1 | -2 | 3 | 15 2 | 1 | 1 | 15 3 | 4 | -1 | 15 ============================================================ Solving 12x + 18y = 30 ---------------------------------------- Step 1: gcd(12, 18) = 6 Solutions exist: 6 divides 30 Step 2: Extended GCD gives 12×-1 + 18×1 = 6 Scaling by 5: 12×-5 + 18×5 = 30 Verification: 12×-5 + 18×5 = 30 ✓ Step 3: General solution (for any integer t): x = -5 + 3t y = 5 - 2t Step 4: Specific solutions: t | x | y | Check ------------------------------ -3 | -14 | 11 | 30 -2 | -11 | 9 | 30 -1 | -8 | 7 | 30 0 | -5 | 5 | 30 1 | -2 | 3 | 30 2 | 1 | 1 | 30 3 | 4 | -1 | 30 ============================================================ Solving 4x + 6y = 5 ---------------------------------------- Step 1: gcd(4, 6) = 2 No integer solutions: 2 does not divide 5 ============================================================ Applications of Linear Diophantine Equations: • Making change with different coin denominations • Scheduling problems with time constraints • Resource allocation in optimization • Solving systems of congruences 💰 PRACTICAL EXAMPLE: Coin Change How many ways can we make $1.00 using quarters (25¢) and dimes (10¢)? Equation: 25x + 10y = 100 or simplified: 5x + 2y = 20 Solving 5x + 2y = 20 ---------------------------------------- Step 1: gcd(5, 2) = 1 Solutions exist: 1 divides 20 Step 2: Extended GCD gives 5×1 + 2×-2 = 1 Scaling by 20: 5×20 + 2×-40 = 20 Verification: 5×20 + 2×-40 = 20 ✓ Step 3: General solution (for any integer t): x = 20 + 2t y = -40 - 5t Step 4: Specific solutions: t | x | y | Check ------------------------------ -3 | 14 | -25 | 20 -2 | 16 | -30 | 20 -1 | 18 | -35 | 20 0 | 20 | -40 | 20 1 | 22 | -45 | 20 2 | 24 | -50 | 20 3 | 26 | -55 | 20 Non-negative solutions (valid coin counts): 0 quarters + 10 dimes = $1.00 2 quarters + 5 dimes = $1.00 4 quarters + 0 dimes = $1.00 Total ways: 3
# Pythagorean Triples print("📐 PYTHAGOREAN TRIPLES\n") print("Finding integer solutions to x² + y² = z²") print("These represent right triangles with integer side lengths!") print() def generate_primitive_pythagorean_triples(limit): """ Generate primitive Pythagorean triples using the parametric method Formula: a = m² - n², b = 2mn, c = m² + n² where m > n > 0, gcd(m,n) = 1, and m,n not both odd """ print("Parametric Generation Method:") print("For m > n > 0 with gcd(m,n) = 1 and m,n not both odd:") print(" a = m² - n²") print(" b = 2mn") print(" c = m² + n²") print() triples = [] print(f"{'m':>2} {'n':>2} | {'a':>3} {'b':>3} {'c':>3} | {'a²+b²':>6} {'c²':>6} | {'Check':>5}") print("-" * 50) m = 2 while m * m + 1 <= limit: # Ensure c ≤ limit for n in range(1, m): # Check conditions for primitive triple if gcd(m, n) == 1 and (m % 2 != n % 2): # Not both odd a = m*m - n*n b = 2*m*n c = m*m + n*n if c <= limit: # Ensure a ≤ b by convention if a > b: a, b = b, a # Verify it's a Pythagorean triple check = a*a + b*b == c*c check_symbol = "✓" if check else "✗" print(f"{m:2d} {n:2d} | {a:3d} {b:3d} {c:3d} | {a*a + b*b:6d} {c*c:6d} | {check_symbol:>5}") if check: triples.append((a, b, c, m, n)) m += 1 return triples # Generate primitive triples limit = 50 primitive_triples = generate_primitive_pythagorean_triples(limit) print(f"\nFound {len(primitive_triples)} primitive Pythagorean triples with c ≤ {limit}") print() # Famous examples print("FAMOUS PYTHAGOREAN TRIPLES:\n") famous_triples = [(3, 4, 5), (5, 12, 13), (8, 15, 17), (7, 24, 25), (20, 21, 29)] print(f"{'Triple':>12} | {'a²':>4} {'b²':>4} {'c²':>4} | {'Sum':>6} | {'Area':>5}") print("-" * 45) for a, b, c in famous_triples: area = (a * b) // 2 # Right triangle area sum_squares = a*a + b*b c_squared = c*c status = "✓" if sum_squares == c_squared else "✗" print(f"({a:2d},{b:2d},{c:2d}) | {a*a:4d} {b*b:4d} {c*c:4d} | {sum_squares:6d} | {area:5d}") print() # All Pythagorean triples from primitive ones print("ALL PYTHAGOREAN TRIPLES:\n") print("Every Pythagorean triple is either primitive or a multiple of a primitive triple.") # Show multiples of the first primitive triple if primitive_triples: a, b, c, m, n = primitive_triples[0] print(f"\nMultiples of primitive triple ({a}, {b}, {c}):") for k in range(1, 5): ka, kb, kc = k*a, k*b, k*c print(f" k={k}: ({ka:2d}, {kb:2d}, {kc:2d})") print("\n🏛️ Historical Note: Pythagorean triples were known to Babylonians around 1800 BCE!") print(" Tablet Plimpton 322 lists many examples, predating Pythagoras by over 1000 years.") # Connection to rational points on unit circle print("\nGEOMETRIC INTERPRETATION:") print("Pythagorean triples (a,b,c) correspond to rational points on the unit circle:") print(" (x,y) = (a/c, b/c) satisfies x² + y² = 1") if primitive_triples: a, b, c, m, n = primitive_triples[0] x_coord = Rational(a, c) y_coord = Rational(b, c) print(f"\nExample: ({a}, {b}, {c}) → ({x_coord}, {y_coord}) on unit circle") print(f"Check: ({x_coord})² + ({y_coord})² = {x_coord**2 + y_coord**2} = 1 ✓")
📐 PYTHAGOREAN TRIPLES Finding integer solutions to x² + y² = z² These represent right triangles with integer side lengths! Parametric Generation Method: For m > n > 0 with gcd(m,n) = 1 and m,n not both odd: a = m² - n² b = 2mn c = m² + n² m n | a b c | a²+b² c² | Check -------------------------------------------------- 2 1 | 3 4 5 | 25 25 | ✓ 3 2 | 5 12 13 | 169 169 | ✓ 4 1 | 8 15 17 | 289 289 | ✓ 4 3 | 7 24 25 | 625 625 | ✓ 5 2 | 20 21 29 | 841 841 | ✓ 5 4 | 9 40 41 | 1681 1681 | ✓ 6 1 | 12 35 37 | 1369 1369 | ✓ Found 7 primitive Pythagorean triples with c ≤ 50 FAMOUS PYTHAGOREAN TRIPLES: Triple | a² b² c² | Sum | Area --------------------------------------------- ( 3, 4, 5) | 9 16 25 | 25 | 6 ( 5,12,13) | 25 144 169 | 169 | 30 ( 8,15,17) | 64 225 289 | 289 | 60 ( 7,24,25) | 49 576 625 | 625 | 84 (20,21,29) | 400 441 841 | 841 | 210 ALL PYTHAGOREAN TRIPLES: Every Pythagorean triple is either primitive or a multiple of a primitive triple. Multiples of primitive triple (3, 4, 5): k=1: ( 3, 4, 5) k=2: ( 6, 8, 10) k=3: ( 9, 12, 15) k=4: (12, 16, 20) 🏛️ Historical Note: Pythagorean triples were known to Babylonians around 1800 BCE! Tablet Plimpton 322 lists many examples, predating Pythagoras by over 1000 years. GEOMETRIC INTERPRETATION: Pythagorean triples (a,b,c) correspond to rational points on the unit circle: (x,y) = (a/c, b/c) satisfies x² + y² = 1 Example: (3, 4, 5) → (3, 4) on unit circle Check: (3)² + (4)² = 25 = 1 ✓

Practice Problems

Test your understanding of elementary number theory:

# Practice Problems print("🎯 PRACTICE PROBLEMS\n") # Problem 1: Prime factorization and divisors print("Problem 1: Prime Factorization Analysis") print("Find the prime factorization of 2520 and count its divisors.") print() n = 2520 factorization = factor(n) print(f"Solution:") print(f" 2520 = {factorization}") # Count divisors using formula: d(n) = ∏(αᵢ + 1) where n = ∏pᵢ^αᵢ divisor_count = 1 for prime, exp in factorization: # CHANGED divisor_count *= (exp + 1) actual_divisors = divisors(n) print(f" Number of divisors: d(2520) = {divisor_count}") print(f" Verification: {len(actual_divisors)} divisors found ✓") print(f" First 10 divisors: {actual_divisors[:10]}") print() # Problem 2: Extended Euclidean Algorithm print("Problem 2: Bézout's Identity") print("Find integers x, y such that 252x + 198y = gcd(252, 198)") print() a, b = 252, 198 g, x, y = xgcd(a, b) print(f"Solution:") print(f" gcd(252, 198) = {g}") print(f" 252 × {x} + 198 × {y} = {g}") print(f" Verification: 252 × {x} + 198 × {y} = {252*x + 198*y} ✓") print() # Problem 3: Chinese Remainder Theorem print("Problem 3: System of Congruences") print("Solve the system:") print(" x ≡ 2 (mod 5)") print(" x ≡ 3 (mod 7)") print(" x ≡ 4 (mod 11)") print() remainders = [2, 3, 4] moduli = [5, 7, 11] solution = crt(remainders, moduli) M = 5 * 7 * 11 print(f"Solution:") print(f" x ≡ {solution} (mod {M})") print(f" Verification:") for r, m in zip(remainders, moduli): check = solution % m status = "✓" if check == r else "✗" print(f" {solution}{check}{r} (mod {m}) {status}") print() # Problem 4: Euler's totient function print("Problem 4: Totient Function") print("Compute φ(720) using the prime factorization method.") print() n = 720 factorization = factor(n) phi_n = euler_phi(n) print(f"Solution:") print(f" 720 = {factorization}") # Apply φ(n) = n × ∏(1 - 1/p) result = n calculation_steps = [f"φ(720) = 720"] for prime, exp in factorization: # CHANGED result = result * (prime - 1) // prime calculation_steps.append(f" × (1 - 1/{prime})") formula = "".join(calculation_steps) print(f" {formula} = {phi_n}") print(f" This means {phi_n} integers from 1 to 720 are coprime to 720.") print() # Problem 5: Linear Diophantine equation print("Problem 5: Diophantine Equation") print("Find all integer solutions to 15x + 25y = 5") print() a, b, c = 15, 25, 5 g = gcd(a, b) print(f"Solution:") print(f" gcd(15, 25) = {g}") print(f" Since {g} divides {c}, solutions exist.") # Find particular solution gcd_result, x0, y0 = xgcd(a, b) scale = c // g x_part = x0 * scale y_part = y0 * scale print(f" Particular solution: x = {x_part}, y = {y_part}") print(f" General solution: x = {x_part} + {b//g}t, y = {y_part} - {a//g}t") print(f" Check: 15×{x_part} + 25×{y_part} = {15*x_part + 25*y_part} = 5 ✓") print("\n🏆 Challenge Problems:") print("1. Find the next primitive Pythagorean triple after (20, 21, 29)") print("2. Prove that φ(n) is even for all n > 2") print("3. Show that if gcd(a,n) = 1, then a^φ(n) ≡ 1 (mod n) (Euler's theorem)") print("4. Find the smallest positive integer that leaves remainder 1 when divided by") print(" 2, 3, 4, 5, and 6") print("5. Characterize all integers n such that φ(n) = n/2")
🎯 PRACTICE PROBLEMS Problem 1: Prime Factorization Analysis Find the prime factorization of 2520 and count its divisors. Solution: 2520 = 2^3 * 3^2 * 5 * 7 Number of divisors: d(2520) = 48 Verification: 48 divisors found ✓ First 10 divisors: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Problem 2: Bézout's Identity Find integers x, y such that 252x + 198y = gcd(252, 198) Solution: gcd(252, 198) = 18 252 × 4 + 198 × -5 = 18 Verification: 252 × 4 + 198 × -5 = 18 ✓ Problem 3: System of Congruences Solve the system: x ≡ 2 (mod 5) x ≡ 3 (mod 7) x ≡ 4 (mod 11) Solution: x ≡ 367 (mod 385) Verification: 367 ≡ 2 ≡ 2 (mod 5) ✓ 367 ≡ 3 ≡ 3 (mod 7) ✓ 367 ≡ 4 ≡ 4 (mod 11) ✓ Problem 4: Totient Function Compute φ(720) using the prime factorization method. Solution: 720 = 2^4 * 3^2 * 5 φ(720) = 720 × (1 - 1/2) × (1 - 1/3) × (1 - 1/5) = 192 This means 192 integers from 1 to 720 are coprime to 720. Problem 5: Diophantine Equation Find all integer solutions to 15x + 25y = 5 Solution: gcd(15, 25) = 5 Since 5 divides 5, solutions exist. Particular solution: x = 2, y = -1 General solution: x = 2 + 5t, y = -1 - 3t Check: 15×2 + 25×-1 = 5 = 5 ✓ 🏆 Challenge Problems: 1. Find the next primitive Pythagorean triple after (20, 21, 29) 2. Prove that φ(n) is even for all n > 2 3. Show that if gcd(a,n) = 1, then a^φ(n) ≡ 1 (mod n) (Euler's theorem) 4. Find the smallest positive integer that leaves remainder 1 when divided by 2, 3, 4, 5, and 6 5. Characterize all integers n such that φ(n) = n/2

CoCalc Platform Features for Number Theory

This notebook leverages CoCalc's powerful features:

Number Theory Specific Benefits:

  • SageMath integration: Built-in number theory functions

  • Symbolic computation: Exact arithmetic with large integers

  • Collaborative research: Share proofs and computations

  • LaTeX support: Beautiful mathematical typesetting

Advanced Features:

  1. Large integer arithmetic: Handle RSA-sized numbers

  2. Primality testing: Miller-Rabin and other advanced tests

  3. Factorization algorithms: Pollard rho, quadratic sieve

  4. Elliptic curves: Modern cryptographic applications

Key Theorems and Results:

  • Fundamental Theorem of Arithmetic: Unique prime factorization

  • Euclidean Algorithm: Ancient but efficient GCD method

  • Fermat's Little Theorem: a^(p-1) ≡ 1 (mod p) for prime p

  • Chinese Remainder Theorem: Solving congruence systems

  • Euler's Theorem: Generalization of Fermat's Little Theorem

Noteworthy Connections:

  • Number theory connects to cryptography (RSA, elliptic curves)

  • Geometry appears in Pythagorean triples and rational points

  • Abstract algebra emerges from modular arithmetic

  • Analysis enters through prime distribution and L-functions

Applications:

  • Computer Security: RSA encryption, digital signatures

  • Error Correction: Coding theory and data integrity

  • Algorithm Design: Hashing, pseudorandom generation

  • Pure Mathematics: Foundation for advanced number theory

The Journey Continues:

Elementary number theory is just the beginning! Advanced topics await:

  • Quadratic forms and reciprocity laws

  • Algebraic integers and class field theory

  • L-functions and the Riemann hypothesis

  • Elliptic curves and modern arithmetic geometry

"Mathematics is the queen of sciences, and number theory is the queen of mathematics." - Carl Friedrich Gauss

You've now explored the royal court of mathematics!