Day 13
This commit is contained in:
5
chinese_remainder/Cargo.lock
generated
Normal file
5
chinese_remainder/Cargo.lock
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "chinese_remainder"
|
||||
version = "0.1.0"
|
||||
9
chinese_remainder/Cargo.toml
Normal file
9
chinese_remainder/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "chinese_remainder"
|
||||
version = "0.1.0"
|
||||
authors = ["Guilhem MARION <gmarion@netc.fr>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
64
chinese_remainder/src/lib.rs
Normal file
64
chinese_remainder/src/lib.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
/// Represents the GCD computed as part of the Extended Euclidean Algorithm.
|
||||
/// This invariant should always be valid : ax + by = gcd
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct EGCD {
|
||||
pub a: i64,
|
||||
pub b: i64,
|
||||
pub x: i64,
|
||||
pub y: i64,
|
||||
pub gcd: i64,
|
||||
}
|
||||
|
||||
/// Computes the Extended Euclidean Algorithm's solution to finding a & b's GCD
|
||||
/// Additionally to the standard Euclidean Algorithm, it provides x and y so
|
||||
/// that ax + by = GCD
|
||||
pub fn egcd(a: i64, b: i64) -> EGCD {
|
||||
// If b == 0, then a == gcd(a,b)
|
||||
// Then gcd = 1 * a + 0 * b follows
|
||||
if b == 0 {
|
||||
EGCD {
|
||||
a,
|
||||
b,
|
||||
x: 1,
|
||||
// Anything other than 0 gives a valid solution but
|
||||
// With different end x and y
|
||||
y: 0,
|
||||
gcd: a,
|
||||
}
|
||||
} else {
|
||||
let rec = egcd(b, a%b);
|
||||
EGCD {
|
||||
a,
|
||||
b,
|
||||
x: rec.y,
|
||||
y: rec.x - rec.y * (a / b),
|
||||
gcd: rec.gcd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Solves the chinese remainder problem, stated as follows:
|
||||
/// ```noexec
|
||||
/// for i in residues.len(),
|
||||
/// (chinese_remainder(residues, moduli) - residues[i]) / moduli[i] == 0
|
||||
/// ```
|
||||
pub fn chinese_remainder(residues: &[i64], moduli: &[i64]) -> Option<i64> {
|
||||
// TODO: Filter out 0s in moduli (meaning big_n is 0 -> div by 0)
|
||||
let big_n: i64 = moduli.iter().product();
|
||||
Some(moduli.iter()
|
||||
.map(|&ni| {
|
||||
egcd(ni, big_n / ni)
|
||||
})
|
||||
.zip(residues)
|
||||
.map(|(egcd,ai)| {
|
||||
if egcd.gcd != 1 {
|
||||
// Fail in case moduli are not co-prime
|
||||
None
|
||||
} else {
|
||||
Some(ai * egcd.y * egcd.b)
|
||||
}
|
||||
})
|
||||
.sum::<Option<i64>>()?
|
||||
.rem_euclid(big_n)) // Sum on Option<_> returns None if the Iter
|
||||
// contains a None
|
||||
}
|
||||
36
chinese_remainder/tests/tests.rs
Normal file
36
chinese_remainder/tests/tests.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chinese_remainder::{EGCD, egcd, chinese_remainder};
|
||||
|
||||
#[test]
|
||||
fn test_egcd() {
|
||||
assert_eq!(
|
||||
egcd(25, 7),
|
||||
EGCD {
|
||||
a: 25,
|
||||
b: 7,
|
||||
x: 2,
|
||||
y: -7,
|
||||
gcd: 1,
|
||||
}
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn test_cr1() {
|
||||
assert_eq!(
|
||||
chinese_remainder(&[2, 3, 2], &[3, 5, 7]), Some(23));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cr2() {
|
||||
assert_eq!(
|
||||
chinese_remainder(&[11,22,19], &[10,4,9]), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cr3() {
|
||||
assert_eq!(
|
||||
chinese_remainder(&[10,4,12], &[11,12,13]), Some(1000));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user