Generics 使用泛型在编译时生成斐波那契序列 在C++中,用模板元编程可以很容易地在编译时计算Fibonacci序列。 模板 constexpr int fibonacci(){返回fibonacci()+fibonacci();} 模板 constexpr int fibonacci(){return 1;} 模板 constexpr int fibonacci(){return 0;}
但据我所知,在rust中,不能只通过泛型传递常量,而且我知道有时rust会在assmebly代码中将某些函数优化为常量。例如: 但该问题的传统递归方法并没有优化到一个常数Generics 使用泛型在编译时生成斐波那契序列 在C++中,用模板元编程可以很容易地在编译时计算Fibonacci序列。 模板 constexpr int fibonacci(){返回fibonacci()+fibonacci();} 模板 constexpr int fibonacci(){return 1;} 模板 constexpr int fibonacci(){return 0;},generics,rust,compile-time,Generics,Rust,Compile Time,但据我所知,在rust中,不能只通过泛型传递常量,而且我知道有时rust会在assmebly代码中将某些函数优化为常量。例如: 但该问题的传统递归方法并没有优化到一个常数 fn fibo(n:i32)->i32{ 匹配{ 0 => 0, 1 => 1, n=>fibo(n-1)+fibo(n-2), } } //称之为 fibo(45);//运行时计算大约需要5秒 好的,到目前为止,我不能理解只有编译器不知道如何优化它,但是我找到了一种方法,可以在编译时使用迭代器计算它 structfibo(
fn fibo(n:i32)->i32{
匹配{
0 => 0,
1 => 1,
n=>fibo(n-1)+fibo(n-2),
}
}
//称之为
fibo(45);//运行时计算大约需要5秒
好的,到目前为止,我不能理解只有编译器不知道如何优化它,但是我找到了一种方法,可以在编译时使用迭代器计算它
structfibo(u32,u32);
Fibo的impl迭代器{
项目类型=u32;
fn下一步(&mut self)->选项{
*self=Fibo(self.1,self.1+self.0);
一些(self.0)
}
}
fn fibo()->fibo{
Fibo(0,1)
}
//称之为
收集::()[44];//这将立即获得编译时计算的第45个元素
此时,我只想知道为什么会发生这种情况。我查看了第二个代码示例的汇编输出,编译器似乎没有将其优化为常数。很可能,正在发生一些非常不同的事情 您称之为“经典”递归算法的方法是计算斐波那契数的最糟糕的方法,因为函数调用的数量随着
n
呈指数增长。迭代方法要好得多,因为它只需要随着n
线性增长的迭代次数。对于n=44
,递归方法大约需要10万亿次函数调用,而迭代方法需要44次循环迭代。当然,后者在运行时看起来是“即时的”,但这并不意味着这里发生了任何特殊的编译器魔法
(对于真正大的n
您需要任意精度的算法,最好的方法是二进制矩阵加电。)
现在是第二个问题,如何让Rust在编译时对此进行评估。C++中的模板元编程实际上是编译时计算的一个拐杖,RIST有一个更容易的方法:常数函数。const-fns的某些方面仍在发展中,但在当前的beta版(将在大约两周后稳定发布)中,您可以用一种非常简单的方式编写斐波那契函数:
pub const fn fibo(mut n: u64) -> u64 {
let mut a = 1;
let mut b = 0;
while n > 0 {
let tmp = b;
b += a;
a = tmp;
n -= 1;
}
b
}
pub const K: u64 = fibo(93);
()
Rust中也有const泛型,但它们是不稳定的(而且仍然相当不稳定)。你可以做类似于C++模板元编程的东西,但是我没有研究它。 <代码> const fn FiBO(n:i32)-> i32 {
匹配{
0 => 0,
1 => 1,
n=>fibo(n-1)+fibo(n-2),
}
}
常数A:i32=fibo(45);
此代码将在编译时计算。但是要花很长时间来编译它,而且它不能在操场上编译。 因此,生锈可能不会优化它。
您还可以查看和ir算法复杂性 计算斐波那契序列的简单方法具有指数复杂性
fn fibo(n:i32)->i32{
匹配{
0 => 0,
1 => 1,
n=>fibo(n-1)+fibo(n-2),
}
}
您可以将其可视化为:
:1次呼叫fibo(0)
:1次呼叫fibo(1)
:3个调用--fibo(2)
,fibo(2)
,fibo(1)
fibo(0)
:5次调用--fibo(3)
,fibo(3)
(值3),fibo(2)
fibo(1)
:9次调用--fibo(4)
,fibo(4)
(值5)和fibo(3)
(值3)fibo(2)
fn fibo(n:i32)->i32{
fn rec(i:i32,当前:i32,下一个:i32)->i32{
如果i==0{current}则{rec(i-1,next,current+next)}
}
记录(n,0,1)
}
它以精确的n+1
步骤执行。。。提供n>=0
但是C++中它是有效的!
C++编译器倾向于对模板实例化和constexpr求值使用memonization。他们不必这样做,这严格来说是一个实施细节,但他们这样做是出于效率考虑
在本例中,fibo
的记忆版本将指数复杂度转化为线性复杂度,这更易于计算
在铁锈里做!
可以在编译时使用当前的beta版本计算Rust中的斐波那契,这可以稳定const
函数中的分支
见:
常量fn fibo(n:i32)->i32{
常数fn rec(i:i32,当前:i32,下一个:i32)->i32{
如果i==0{current}则{rec(i-1,next,current+next)}
}
记录(n,0,1)
}
fn main(){
常数结果:usize=fibo(9)为usize;
let数组:[i32;结果]=[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1
];
println!(“{}”,数组[0]);
}
可能有一个技巧可以在编译时不使用分支来表示计算,允许在stable上的编译时计算
fibo
,但是我不确定rustc是否会执行递归调用。这里有两个问题,它们彼此非常不同。找到一种处理泛型的方法与Rust编译器如何优化此计算无关。。它的运行速度与Fibo一样快