我正在研究第三个项目Euler问题:
fn main() {
println!("{}", p3());
}
fn p3() -> u64 {
let divs = divisors(1, 600851475143, vec![]);
let mut max = 0;
for x in divs {
if prime(x, 0, false) && x > max {
max = x
}
}
max
}
fn divisors(i: u64, n: u64, div: Vec<u64>) -> Vec<u64> {
let mut temp = div;
if i * i > n {
temp
} else {
if n % i == 0 {
temp.push(i);
temp.push(n / i);
}
divisors(i + 2, n, temp)
}
}
fn prime(n: u64, i: u64, skip: bool) -> bool {
if !skip {
if n == 2 || n == 3 {
true
} else if n % 3 == 0 || n % 2 == 0 {
false
} else {
prime(n, 5, true)
}
} else {
if i * i > n {
true
} else if n % i == 0 || n % (i + 2) == 0 {
false
} else {
prime(n, i + 6, true)
}
}
}
值600851475143
是某个时刻导致其溢出的值。如果我将其替换为10 10 数量级或更小的任何值,它将返回一个答案。在将其作为递归解决方案的同时,有没有办法:
fatal runtime: stack overflow
错误?我知道这可以迭代完成,但我宁愿不这样做。
答案 0 :(得分:2)
包含600 * 10 9 u64
s的向量意味着您需要4.8 TB的RAM或交换空间。
我确定你不需要这个问题,你在这里缺少一些数学知识:扫描直到600851475143的平方根就足够了。您也可以使用Sieve of Eratosthenes。
来加速该计划Project Euler很高兴提高你的数学技能,但它对你的任何编程语言都没有帮助。为了学习Rust,我从Exercism开始。
答案 1 :(得分:2)
执行一些优化,例如在检查其因素时是否达到数字的平方根,以及是否为素数,我得到了:
fn is_prime(n: i64) -> bool {
let float_input = n as f64;
let upper_bound = float_input.sqrt() as i64;
for x in 2..upper_bound + 1 {
if n % x == 0 {
return false;
}
}
return true;
}
fn get_factors(n: i64) -> Vec<i64> {
let mut factors: Vec<i64> = Vec::new();
let float_input = n as f64;
let upper_bound = float_input.sqrt() as i64;
for x in 1..upper_bound + 1 {
if n % x == 0 {
factors.push(x);
factors.push(n / x);
}
}
factors
}
fn get_prime_factors(n: i64) -> Vec<i64> {
get_factors(n)
.into_iter()
.filter(|&x| is_prime(x))
.collect::<Vec<i64>>()
}
fn main() {
if let Some(max) = get_prime_factors(600851475143).iter().max() {
println!("{:?}", max);
}
}
在我的机器上,此代码运行速度非常快,没有溢出。
./problem003 0.03s user 0.00s system 90% cpu 0.037 total
答案 2 :(得分:1)
如果你真的不想要迭代版本:
首先,确保使用优化(rustc -O
或cargo --release
)进行编译。没有它,Rust的TCO就没有机会了。你的divisors
函数是尾递归的,但似乎在递归堆栈中上下移动Vec
令人困惑,LLVM就错过了这个事实。我们可以在这里使用一个引用来帮助编译器:
fn divisors(i: u64, n: u64, mut div: Vec<u64>) -> Vec<u64> {
divisors_(i, n, &mut div);
div
}
fn divisors_(i: u64, n: u64, div: &mut Vec<u64>) {
if i * i > n {
} else {
if n % i == 0 {
div.push(i);
div.push(n / i);
}
divisors_(i + 2, n, div)
}
}
在我的机器上,更改使代码不再是段错误。
如果你想增加堆栈大小,你应该在一个单独的线程中运行你的函数,堆栈大小增加(使用std::thread::Builder::stack_size
)
Rust保留了become
关键字以保证尾递归,
因此,将来您可能只需要在代码中添加一个关键字即可使其正常运行。