Ich implementierte den Miller-Rabin Strong Pseudoprimientest in Rust unter Verwendung von BigUint
, um beliebig große Primzahlen zu unterstützen. Um die Zahlen zwischen 5 und 10^6 zu durchlaufen, dauerte es ungefähr 40s mit cargo run --release
.Ist die große Integer-Implementierung in der Num-Box langsam?
Ich implementierte den gleichen Algorithmus mit Java BigInteger
und der gleiche Test dauerte 10s, um zu beenden. Rust scheint 4 mal langsamer zu sein. Ich nehme an, dass dies durch die Implementierung von num::bigint
verursacht wird.
Ist dies nur der aktuelle Stand von num::bigint
, oder kann jemand irgendeine offensichtliche Verbesserung in meinem Code feststellen? (Hauptsächlich über, wie ich die Sprache verwendete. Unabhängig davon, ob meine Implementierung des Algorithmus gut oder schlecht ist, ist es in beiden Sprachen fast identisch implementiert - verursacht also keinen Unterschied in der Leistung.)
Ich habe es dort bemerkt sind viele clone()
erforderlich, aufgrund der Eigentümer-Modell von Rust, die die Geschwindigkeit auf ein gewisses Maß beeinflussen könnte. Aber ich denke, dass es keinen Weg gibt, habe ich recht? Hier
ist der Code:
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);
}
Hinweis: 'core :: ops' wird als' std :: ops' re-exportiert, Sie müssen es also nicht importieren. –