Zaimplementowałem Miller-Rabin Strong Pseudoprime Test w Rust przy użyciu BigUint
do obsługi dowolnych dużych liczb pierwszych. Aby przejść przez liczby od 5 do 10^6, zajęło to około 40 sekund z cargo run --release
.Czy duża liczba całkowita w powolnej skrzynce num?
Zaimplementowałam ten sam algorytm z Javą BigInteger
i ten sam test trwał 10 sekund. Rdza wydaje się 4 razy wolniejsza. Zakładam, że jest to spowodowane wdrożeniem num::bigint
.
Czy to tylko obecny stan num::bigint
, czy ktoś może zauważyć jakąkolwiek oczywistą poprawę w moim kodzie? (Głównie o tym, jak użyłem tego języka, bez względu na to, czy moja implementacja algorytmu jest dobra czy zła, jest prawie zaimplementowana dokładnie tak samo w obu językach - tak więc nie powoduje to różnicy w wydajności.)
Zauważyłem tam jest wiele wymaganych clone()
, ze względu na model własności Rusta, który może mieć wpływ na prędkość do pewnego poziomu. Ale myślę, że nie da się tego obejść, mam rację?
Oto kod:
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);
}
Uwaga: 'core :: ops' jest ponownie eksportowany jako' std :: ops', więc nie musisz go importować. –