When working with num_bigint::BigInt I have run into some performance limitations, for context I am working on https://www.geeksforgeeks.org/how-to-generate-large-prime-numbers-for-rsa-algorithm/ [full code at bottom of page].
In the nBitRand function we essentially shuffle a vector of BigInt's determined by the range:
(2.pow(n - 1) 1)..(2.pow(n) - 1)
I have made a function to calculate pow for BigInts:
fn big_pow(base: BigInt, exp: BigInt) -> BigInt
{
if exp == BigInt::from_u32(0).unwrap()
{
return BigInt::from_u32(1).unwrap();
}
let mut answer = base.clone();
let mut i = BigInt::from_u32(1).unwrap();
while i < exp
{
i = i BigInt::from_u32(1).unwrap();
answer = answer * base.clone();
}
return answer;
}
Now here is the function for nBitRand which uses the big_pow function:
fn nBitRand(n: BigInt) -> BigInt
{
let big2 = BigInt::from_u64(2).unwrap();
let big1 = BigInt::from_u64(1).unwrap();
let mut range: Vec<BigInt> =
num_iter::range_inclusive
(
(big_pow(big2.clone(), n.clone() - big1.clone()) big1.clone()),
(big_pow(big2.clone(), n.clone()) - big1.clone())
)
.collect();
range.shuffle(&mut rand::thread_rng());
return range[0].clone();
}
When I try this function with a number n that is larger than 32 I get this panic:
memory allocation of 137438953440 bytes failed
Meanwhile the python implementation can do 1024 bits of n seamlessly?
Not to mention that if I do actually run this function for 32 bits of n ( in release mode or not ) it runs terribly slow and nearly crashes my PC which is running several other processes as most peoples PCs are.
Is this the same for every current BigInt implementation in rust? I would much rather use Rust than Python in general, is it expected that I should just use python for any number this big? I don't have a serious issue with doing this, however if there is an alternative crate I could use to avoid it I would be very happy. There is not a terrible amount of discussion around this topic that I have found, so any help would be greatly appreciated. Thanks.
Edit: SOLUTION FOUND
If anyone wants to use this, add use num_bigint::RandBigInt;
to your use directives.
Also in Cargo.toml enable rand feature in bigint as such:
num-bigint = {version = "0.4.3", features = ["rand"] }
if you are working with num_bigint as I am. Use the function like this:
gen_bigint_range(&mut rand::thread_rng(), &lowerbound.clone(), &higherbound.clone());
Docs: https://docs.rs/num-bigint/0.4.3/num_bigint/trait.RandBigInt.html#tymethod.gen_bigint_range
CodePudding user response:
The reason you are having issues is that there are a lot of numbers between 2.pow(n - 1)
and 2^pow(n)
, and your rust code is trying to hold all of them in memory at once. Just trying to hold the numbers between 2^31
and 2^32
in memory all at once will likely require a few tens of gigabytes of ram, which is evidently more than your computer can handle.
The correct way to handle this is to use something like the gen_bigint
which handles this elegantly:
use rand::prelude::*;
use num::bigint::RandBigInt;
fn main(){
println!("{}", thread_rng().gen_bigint(1024));
}
(no playground link because it requires activating the rand
feature)
sample output:
-120678186469168050141127714278465650797509809181209645055716269782732917216510450905974158776294027244037250792198062938087103374539558073937169391547408613028913574776101294537137332842677479099864132223045958106561504255177421361770704902635830935432132237493695901107602818879400859307921127547090428474495