Я реализовал прочный тест псевдотермического эффекта Миллера-Рабина в ржавчине, используя BigUint
для поддержки произвольных больших простых чисел. Чтобы пробежать числа от 5 до 10^6, это заняло около 40 с cargo run --release
.Является ли реализация большого целого числа в ящике num медленным?
Я реализовал тот же алгоритм с Java BigInteger
и тот же тест занял 10 секунд. Ржавчина в 4 раза медленнее. Я предполагаю, что это вызвано реализацией num::bigint
.
Это только текущее состояние num::bigint
, или может ли кто-нибудь заметить какие-либо очевидные улучшения в моем коде? (В основном, о том, как я использовал этот язык. Независимо от того, хорошо ли я реализована в алгоритме, он практически реализован на обоих языках точно так же, поэтому не вызывает различий в производительности.)
Я заметил там требуется много clone()
, из-за модели владения Rust, которая может сильно повлиять на скорость до некоторого уровня. Но я думаю, что это не так, я прав?
Вот код:
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);
}
Примечание: 'core :: ops' реэкспортируется как' std :: ops', поэтому вам не нужно его импортировать. –