[03_are_you_still_doing_this_by_hand] improve performance by precomputing giant steps

This commit is contained in:
2025-09-12 17:01:56 +02:00
parent cb876e7427
commit 3556025849
3 changed files with 83 additions and 2 deletions

BIN
res/03_baby_steps.bin Normal file

Binary file not shown.

BIN
res/03_giant_steps.bin Normal file

Binary file not shown.

View File

@@ -1,23 +1,104 @@
use rug::Integer; use rug::Integer;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::sync::LazyLock;
use tqdm::tqdm; use tqdm::tqdm;
/// the base of the discrete logarithm
const G: u64 = 42;
/// the stride value of the Baby-Step-Giant-Step algorithm, controlling the space-time-tradeoff
const S: u64 = 5341666;
/// the modul of the discrete logarithm /// the modul of the discrete logarithm
const P: u64 = 12345679943u64; const P: u64 = 12345679943u64;
/// the precomputed baby steps
static BABY_STEPS: LazyLock<Vec<u64>> = LazyLock::new(|| {
let path = "res/03_baby_steps.bin";
if !std::fs::exists(path).unwrap() {
precompute_baby_steps(G, P, S, &mut File::create(path).unwrap());
}
std::fs::read(path).unwrap()
.chunks_exact(8)
.map(|chunk| u64::from_be_bytes(chunk.try_into().unwrap()))
.collect()
});
/// the precomputed giant steps
static GIANT_STEPS: LazyLock<HashMap<u64, usize>> = LazyLock::new(|| {
let path = "res/03_giant_steps.bin";
if !std::fs::exists(path).unwrap() {
precompute_giant_steps(G, P, S, &mut File::create(path).unwrap());
}
std::fs::read(path).unwrap()
.chunks_exact(8)
.map(|chunk| u64::from_be_bytes(chunk.try_into().unwrap()))
.enumerate()
.map(|(idx, step)| (step, idx))
.collect()
});
pub fn solve(n: u64, x: Integer) -> (u64, Integer) { pub fn solve(n: u64, x: Integer) -> (u64, Integer) {
let x = x.modulo(&Integer::from(P - 1)).to_u64().unwrap(); let x = x.modulo(&Integer::from(P - 1)).to_u64().unwrap();
let mut n = n; let mut n = n;
let mut x = x.clone(); let mut x = x.clone();
for _ in tqdm(0..10_000) { for _ in 0..10_000 {
n += 1; n += 1;
x = log(42, 1 + (x + n) % (P - 1), P).unwrap(); x = log42_mod_p(1 + (x + n) % (P - 1)).unwrap();
} }
(n, Integer::from(x)) (n, Integer::from(x))
} }
/// Quickly computes the discrete logarithm of `b` to the base [`G`] modulo [`M`] using the Baby-Step-Giant-Step
/// algorithm with precomputed steps
fn log42_mod_p(b: u64) -> Option<u64> {
if b % P == 1 {
return Some(0);
}
let inv = pow(b, P - 2, P) as u128;
let s = GIANT_STEPS.len();
for j in 1..=BABY_STEPS.len() {
let baby_step = BABY_STEPS[j - 1] as u128;
let search = ((baby_step * inv) % P as u128) as u64;
if let Some(i) = GIANT_STEPS.get(&search) {
return Some((j * s - i) as u64);
}
}
None
}
/// Precomputes the giant steps for use in the Baby-Step-Giant-Step algorithm for a fixed base `g` and modul `m` using a
/// stride of `s`.
fn precompute_giant_steps(g: u64, m: u64, s: u64, out: &mut impl Write) {
let g = g as u128;
let m = m as u128;
let mut a = 1u128;
for _ in tqdm(0..s) {
out.write_all(&(a as u64).to_be_bytes()).unwrap();
a *= g;
a %= m;
}
}
/// Precomputes the baby steps for use in the Baby-Step-Giant-Step algorithm for a fixed base `g` and modul `m` using a
/// stride of `s`.
fn precompute_baby_steps(g: u64, m: u64, s: u64, out: &mut impl Write) {
let gs = pow(g, s, m) as u128;
let m= m as u128;
let mut a = gs;
for _ in tqdm(1..=(m as u64 / s + 1)) {
out.write_all(&(a as u64).to_be_bytes()).unwrap();
a *= gs;
a %= m;
}
}
/// Computes the discrete logarithm of `b` to the base `g` modulo `m` using the Baby-Step-Giant-Step algorithm. /// Computes the discrete logarithm of `b` to the base `g` modulo `m` using the Baby-Step-Giant-Step algorithm.
fn log(g: u64, b: u64, m: u64) -> Option<u64> { fn log(g: u64, b: u64, m: u64) -> Option<u64> {
let g: u128 = g as u128; let g: u128 = g as u128;