Ho implementato il test Pseudoprime Strong di Miller-Rabin in Rust utilizzando BigUint
per supportare primi grandi arbitrari. Per eseguire i numeri compresi tra 5 e 10^6, sono stati necessari circa 40 secondi con cargo run --release
.L'implementazione del numero intero nella cella num è lenta?
Ho implementato lo stesso algoritmo con Java BigInteger
e lo stesso test ha richiesto 10 secondi. La ruggine sembra essere 4 volte più lenta. Presumo che ciò sia causato dall'implementazione di num::bigint
.
Questo è solo lo stato corrente di num::bigint
oppure qualcuno può notare miglioramenti evidenti nel mio codice? (Principalmente su come ho usato il linguaggio. Indipendentemente dal fatto che la mia implementazione dell'algoritmo sia buona o cattiva, è quasi implementata esattamente nello stesso modo in entrambe le lingue - quindi non fa la differenza nelle prestazioni.)
Ho notato lì sono necessari molti clone()
, a causa del modello proprietario di Rust, che potrebbe influire sulla velocità a un certo livello. Ma immagino che non ci sia modo di aggirare questo, ho ragione?
Ecco il codice:
extern crate rand;
extern crate num;
extern crate core;
extern crate time;
use std::time::{Duration};
use time::{now, Tm};
use rand::Rng;
use num::{Zero, One};
use num::bigint::{RandBigInt, BigUint, ToBigUint};
use num::traits::{ToPrimitive};
use num::integer::Integer;
use core::ops::{Add, Sub, Mul, Div, Rem, Shr};
fn find_r_and_d(i: BigUint) -> (u64, BigUint) {
let mut d = i;
let mut r = 0;
loop {
if d.clone().rem(&2u64.to_biguint().unwrap()) == Zero::zero() {
d = d.shr(1usize);
r = r + 1;
} else {
break;
}
}
return (r, d);
}
fn might_be_prime(n: &BigUint) -> bool {
let nsub1 = n.sub(1u64.to_biguint().unwrap());
let two = 2u64.to_biguint().unwrap();
let (r, d) = find_r_and_d(nsub1.clone());
'WitnessLoop: for kk in 0..6u64 {
let a = rand::thread_rng().gen_biguint_range(&two, &nsub1);
let mut x = mod_exp(&a, &d, &n);
if x == 1u64.to_biguint().unwrap() || x == nsub1 {
continue;
}
for rr in 1..r {
x = x.clone().mul(x.clone()).rem(n);
if x == 1u64.to_biguint().unwrap() {
return false;
} else if x == nsub1 {
continue 'WitnessLoop;
}
}
return false;
}
return true;
}
fn mod_exp(base: &BigUint, exponent: &BigUint, modulus: &BigUint) -> BigUint {
let one = 1u64.to_biguint().unwrap();
let mut result = one.clone();
let mut base_clone = base.clone();
let mut exponent_clone = exponent.clone();
while exponent_clone > 0u64.to_biguint().unwrap() {
if exponent_clone.clone() & one.clone() == one {
result = result.mul(&base_clone).rem(modulus);
}
base_clone = base_clone.clone().mul(base_clone).rem(modulus);
exponent_clone = exponent_clone.shr(1usize);
}
return result;
}
fn main() {
let now1 = now();
for n in 5u64..1_000_000u64 {
let b = n.to_biguint().unwrap();
if might_be_prime(&b) {
println!("{}", n);
}
}
let now2 = now();
println!("{}", now2.to_timespec().sec - now1.to_timespec().sec);
}
Nota: 'core :: ops' viene riesportato come' std :: ops', quindi non è necessario importarlo. –