[03_are_you_still_doing_this_by_hand] improve performance by precomputing giant steps
This commit is contained in:
BIN
res/03_baby_steps.bin
Normal file
BIN
res/03_baby_steps.bin
Normal file
Binary file not shown.
BIN
res/03_giant_steps.bin
Normal file
BIN
res/03_giant_steps.bin
Normal file
Binary file not shown.
@@ -1,23 +1,104 @@
|
||||
use rug::Integer;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::sync::LazyLock;
|
||||
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
|
||||
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) {
|
||||
let x = x.modulo(&Integer::from(P - 1)).to_u64().unwrap();
|
||||
|
||||
let mut n = n;
|
||||
let mut x = x.clone();
|
||||
for _ in tqdm(0..10_000) {
|
||||
for _ in 0..10_000 {
|
||||
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))
|
||||
}
|
||||
|
||||
/// 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.
|
||||
fn log(g: u64, b: u64, m: u64) -> Option<u64> {
|
||||
let g: u128 = g as u128;
|
||||
|
||||
Reference in New Issue
Block a user