diff --git a/src/math/binary_exponentiation.rs b/src/math/binary_exponentiation.rs new file mode 100644 index 00000000000..79ed03b5b46 --- /dev/null +++ b/src/math/binary_exponentiation.rs @@ -0,0 +1,50 @@ +// Binary exponentiation is an algorithm to compute a power in O(logN) where N is the power. +// +// For example, to naively compute n^100, we multiply n 99 times for a O(N) algorithm. +// +// With binary exponentiation we can reduce the number of muliplications by only finding the binary +// exponents. n^100 = n^64 * n^32 * n^4. We can compute n^64 by ((((n^2)^2)^2)...), which is +// logN multiplications. +// +// We know which binary exponents to add by looking at the set bits in the power. For 100, we know +// the bits for 64, 32, and 4 are set. + +// Computes n^p +pub fn binary_exponentiation(mut n: u64, mut p: u32) -> u64 { + let mut result_pow: u64 = 1; + while p > 0 { + if p & 1 == 1 { + result_pow *= n; + } + p >>= 1; + n *= n; + } + result_pow +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + // Need to be careful about large exponents. It is easy to hit overflows. + assert_eq!(binary_exponentiation(2, 3), 8); + assert_eq!(binary_exponentiation(4, 12), 16777216); + assert_eq!(binary_exponentiation(6, 12), 2176782336); + assert_eq!(binary_exponentiation(10, 4), 10000); + assert_eq!(binary_exponentiation(20, 3), 8000); + assert_eq!(binary_exponentiation(3, 21), 10460353203); + } + + #[test] + fn up_to_ten() { + // Compute all powers from up to ten, using the standard library as the source of truth. + for i in 0..10 { + for j in 0..10 { + println!("{}, {}", i, j); + assert_eq!(binary_exponentiation(i, j), u64::pow(i, j)) + } + } + } +} diff --git a/src/math/mod.rs b/src/math/mod.rs index 953545a8774..0ecde541e18 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -4,6 +4,7 @@ mod amicable_numbers; mod armstrong_number; mod baby_step_giant_step; mod bell_numbers; +mod binary_exponentiation; mod ceil; mod chinese_remainder_theorem; mod collatz_sequence; @@ -49,6 +50,7 @@ pub use self::amicable_numbers::amicable_pairs_under_n; pub use self::armstrong_number::is_armstrong_number; pub use self::baby_step_giant_step::baby_step_giant_step; pub use self::bell_numbers::bell_number; +pub use self::binary_exponentiation::binary_exponentiation; pub use self::ceil::ceil; pub use self::chinese_remainder_theorem::chinese_remainder_theorem; pub use self::collatz_sequence::sequence;