Recursion 有没有办法优化这段代码,使其不会';你不会溢出堆栈吗?
我正在处理第三个项目Euler问题:Recursion 有没有办法优化这段代码,使其不会';你不会溢出堆栈吗?,recursion,optimization,rust,stack-overflow,Recursion,Optimization,Rust,Stack Overflow,我正在处理第三个项目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
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)
}
}
}
fn main(){
println!(“{}”,p3());
}
fn p3()->u64{
设divs=除数(16008511475143,vec![]);
设mut max=0;
对于divs中的x{
如果素数(x,0,false)&&x>max{
最大值=x
}
}
最大值
}
fn除数(i:u64,n:u64,div:Vec)->Vec{
让mut temp=div;
如果i*i>n{
临时雇员
}否则{
如果n%i==0{
温度推力(i);
温度推力(n/i);
}
除数(i+2,n,temp)
}
}
fn素数(n:u64,i:u64,skip:bool)->bool{
如果!跳过{
如果n==2 | | n==3{
真的
}如果n%3==0 | | n%2==0,则为else{
假的
}否则{
素数(n,5,真)
}
}否则{
如果i*i>n{
真的
}如果n%i==0 | | n%(i+2)==0{
假的
}否则{
素数(n,i+6,真)
}
}
}
值600851475143
是在某个点导致其溢出的值。如果我将其替换为1010数量级或更小的任何值,它将返回一个答案。在将其保持为递归解决方案的同时,是否有任何方法可以:
致命运行时:堆栈溢出
错误我知道这可以迭代完成,但我不希望这样做。如果您确实不想要迭代版本: 首先,确保编译时进行了优化(
rustc-O
或cargo--release
)。如果没有它,就不可能实现生锈的TCO。您的除数
函数是尾部递归函数,但似乎在递归堆栈中上下移动此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)
}
}
fn除数(i:u64,n:u64,mut div:Vec)->Vec{
除数(i、n和mut div);
div
}
fn除数(i:u64,n:u64,div:&mut-Vec){
如果i*i>n{
}否则{
如果n%i==0{
第二组(i);
分区推进(n/i);
}
除数(i+2,n,div)
}
}
在我的机器上,这些更改使代码不再出现故障
如果仍要增加堆栈大小,则应在增加堆栈大小的单独线程中运行函数(使用)
Rust为保证尾部递归保留了
been
关键字,
因此,也许将来您只需要在代码中添加一个关键字就可以了。一个包含600*109
u64
s的向量意味着您将需要4.8TB的RAM或交换空间
我相信你不需要这个问题,你在这里缺少一些数学知识:扫描到600851475143的平方根就足够了。您还可以通过使用来加速程序
ProjectEuler很好地提高了你的数学技能,但它对你的任何编程语言都没有特别的帮助。为了学习Rust,我从开始进行了。执行了一些优化,例如在检查其因子和是否为素数时,只取数字的平方根,我得到:
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);
}
}
在这里,坚持递归是毫无意义的。调用是尾部递归的,因此使函数迭代是很简单的,这样就可以保证它们使用恒定的堆栈空间,而不是依赖于TCO(这是不保证的,在这种情况下似乎不会发生)。用递归编写任何东西都不会与生锈很好地结合,坚持递归而不是迭代只会让你头疼;它与C程序的堆栈相同,因此您可以使用
ulimit
或适合您的操作系统的任何东西。这是一组很好的优化,这正是Project Euler的重点。然而,OP不愿意改变编程风格意味着这最多只能将问题延迟到一个更大的数字。我可能会写它。你确定没有大的复合数(n作为f64)。sqrt()作为i64
小于n
的实际平方根吗?(我找不到,只是好奇是否可以证明)