GCD and LCM Euclidean Algorithm in DSA Rust - Time & Space Complexity
We want to understand how fast the Euclidean algorithm finds the greatest common divisor (GCD) of two numbers.
This helps us know how the steps grow when numbers get bigger.
Analyze the time complexity of the following code snippet.
fn gcd(mut a: u64, mut b: u64) -> u64 {
while b != 0 {
let temp = b;
b = a % b;
a = temp;
}
a
}
fn lcm(a: u64, b: u64) -> u64 {
(a / gcd(a, b)) * b
}
This code finds the GCD using the Euclidean algorithm and then calculates the LCM using the GCD.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The while loop repeatedly calculates the remainder (modulus) of two numbers.
- How many times: The loop runs until the second number becomes zero, which depends on how quickly the numbers reduce.
Each step reduces the size of the numbers significantly, so the number of steps grows slowly even if numbers get large.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 5-6 steps |
| 100 | About 7-8 steps |
| 1000 | About 10-12 steps |
Pattern observation: The steps grow roughly proportional to the number of digits, not the number size itself.
Time Complexity: O(log min(a, b))
This means the number of steps grows slowly, roughly with the number of digits of the smaller input.
[X] Wrong: "The Euclidean algorithm takes as many steps as the size of the numbers themselves."
[OK] Correct: Actually, the algorithm reduces the numbers quickly by using remainders, so steps grow with the number of digits, not the full number size.
Understanding this algorithm's time complexity shows you can analyze efficient math-based loops, a useful skill for many coding problems.
"What if we used subtraction instead of modulus in the loop? How would the time complexity change?"
Practice
a and b?Solution
Step 1: Understand Euclidean algorithm purpose
The Euclidean algorithm is designed to find the greatest common divisor (GCD) of two integers by repeatedly applying the remainder operation.Step 2: Differentiate GCD from LCM
While GCD is the largest number dividing both without remainder, LCM is the smallest number divisible by both. Euclidean algorithm directly finds GCD.Final Answer:
The greatest common divisor (GCD) ofaandb-> Option AQuick Check:
Euclidean algorithm = GCD [OK]
- Confusing GCD with LCM
- Thinking it calculates sum or difference
- Assuming it returns both GCD and LCM
Solution
Step 1: Identify correct parameter and return types
GCD function takes two unsigned integers (u32) and returns an unsigned integer (u32) representing the GCD.Step 2: Check function signature correctness
fn gcd(a: u32, b: u32) -> u32 matches this: inputs are u32, output is u32. fn gcd(a: i32, b: i32) -> i32 uses signed integers, which is less common for GCD. fn gcd(a: u32, b: u32) -> bool returns bool, incorrect. fn gcd(a: u32, b: u32) lacks return type.Final Answer:
fn gcd(a: u32, b: u32) -> u32 -> Option AQuick Check:
Correct signature returns u32 [OK]
- Using signed integers instead of unsigned
- Missing return type in function signature
- Returning wrong type like bool
fn gcd(mut a: u32, mut b: u32) -> u32 {
while b != 0 {
let temp = b;
b = a % b;
a = temp;
}
a
}
fn main() {
let result = gcd(48, 18);
println!("{}", result);
}Solution
Step 1: Trace Euclidean algorithm steps for gcd(48, 18)
Start with a=48, b=18. Compute remainder: 48 % 18 = 12. Update a=18, b=12. Next, 18 % 12 = 6. Update a=12, b=6. Then, 12 % 6 = 0. Update a=6, b=0.Step 2: Identify when loop ends and return value
Loop ends when b=0. Return a=6 as GCD.Final Answer:
6 -> Option BQuick Check:
GCD(48,18) = 6 [OK]
- Returning the last remainder instead of last non-zero divisor
- Mixing up a and b updates
- Stopping loop too early
fn gcd(mut a: u32, mut b: u32) -> u32 {
while b != 0 {
let temp = b;
b = a % b;
a = temp;
}
a
}
fn lcm(a: u32, b: u32) -> u32 {
a * b / gcd(a, b)
}
fn main() {
println!("{}", lcm(0, 0));
}Solution
Step 1: Analyze the LCM call with zero inputs
The LCM function computes (a * b) / gcd(a, b). The main calls lcm(0, 0), where gcd(0, 0) returns 0, leading to division by zero.Step 2: Verify gcd(0, 0) behavior
In gcd, when a=0, b=0, the while loop doesn't execute (b == 0), so it returns a = 0.Final Answer:
Division by zero error when both inputs are zero -> Option DQuick Check:
LCM divides by gcd; gcd(0,0)=0 causes error [OK]
- Ignoring zero inputs causing division by zero
- Assuming gcd never returns zero
- Not validating inputs before LCM calculation
[12, 15, 20], which Rust code snippet correctly computes their LCM using the Euclidean algorithm for GCD?Solution
Step 1: Understand LCM folding logic
LCM of multiple numbers can be found by folding with formula: lcm(a,b) = a * b / gcd(a,b). Starting with 1u32, fold applies this stepwise.Step 2: Analyze each option
let lcm = nums.iter().fold(1u32, |acc, &x| acc * x / gcd(acc, x)); correctly folds with acc * x / gcd(acc, x). let lcm = nums.iter().fold(0, |acc, &x| acc + x / gcd(acc, x)); uses addition, incorrect. let lcm = nums.iter().fold(1, |acc, &x| gcd(acc, x)); returns gcd only, not LCM. let lcm = nums.iter().fold(1, |acc, &x| acc * x * gcd(acc, x)); multiplies by gcd, incorrect.Final Answer:
let lcm = nums.iter().fold(1u32, |acc, &x| acc * x / gcd(acc, x)); -> Option CQuick Check:
Fold with acc * x / gcd(acc, x) computes LCM [OK]
- Using addition instead of multiplication
- Returning gcd instead of LCM
- Multiplying by gcd instead of dividing
Ready to prove it?
Pass the test and this pattern moves unknown
