私は、BigUint
を使ってRustのMiller-Rabin Strong Pseudoprime Testを実装して、任意の大きな素数をサポートしました。 5と10^6の間の数字を実行するには、cargo run --release
で約40秒かかりました。numクレートの大整数実装は遅いですか?
私はJavaのBigInteger
で同じアルゴリズムを実装し、同じテストでは10秒で完了しました。錆は4倍遅く見えます。私はこれがnum::bigint
の実装によって引き起こされたと仮定します。
これはちょうどnum::bigint
の現在の状態ですか、それとも誰かが自分のコードで明らかな改善を見出すことができますか? (主に私が言語をどのように使っていたかにかかわらず、私のアルゴリズムの実装が良いか悪いかに関わらず、両方の言語で全く同じように実装されているため、性能の違いはありません)。
私はそこに気付きましたRustの所有モデルのために必要とされる多くのものがclone()
であり、ある程度スピードに影響を与える可能性があります。しかし、私はそれを回避する方法はないと思います、そうですか?ここで
はコードです:
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'として再エクスポートされるので、インポートする必要はありません。 –