本文是为回顾之前内容所做的一个小练习。使用
Rust
实现斐波那契数列。
1. 题目
1、输出几个斐波那契数列,让用户了解斐波那契数列的基本规律;(0、1、1、2、3、5、8、13、21、34
)
2、要求用户输入需要查看的斐波那契数的索引号,并输出对应索引的斐波那契数。(如:输入 0
,输出 0
;输入 1
,输出 1
;输入 2
,输出 1
;输入 6
,输出 8
)
3、同时校验用户输入是否正确。
2. 分析
分析1:
这个很简单,我们只需要将基本样例简单的打印出来就好。
分析2:
相信斐波那契数列大家也已经熟悉的不能再熟悉了,但仍需提醒的一点是,Fibonacci
数列的第 0
项为 0
(第一个数,是第零项,而非第一项,即索引号为 0
)。
获取从终端上的标准输入,并将其转换为整型数字(这一点之前已经可以做到了)。再根据斐波那契数列的规律将对应索引的值打印即可。
分析3:
确保用户输入的是正确的索引号,而非负值、零、小数和其它各种符号。
3. 实现
在此之前请各位根据题目先自己完成,然后用各位写的代码与笔者写的代码对比,若觉得笔者代码需要改进的地方,还请不吝赐教,欢迎指出,我会非常感谢的!!!
和往常一样,首先创建新的项目:
imaginemiracle:rust_projects$ cargo new fibonacci
imaginemiracle:rust_projects$ cd fibonacci/
imaginemiracle:fibonacci$ ls
Cargo.lock Cargo.toml src target
3.1. 代码展示
如下提供两种实现方法,一种是以常规的方式实现,另一种使用递归的方法实现。
(1)常规方法实现:
笔者实现的代码如下:
use std::io;
fn fibonacci_demo() {
println!("The Fibonacci sequence demo: 0、1、1、2、3、5、8、13、21、34、...");
}
fn get_input_number() -> usize {
loop {
let mut number = String::new();
io::stdin()
.read_line(&mut number)
.expect("Failed to read line.");
let number: usize = match number.trim().parse::<usize>() {
Ok(num) => num,
Err(_) => {
println!("Please enter the correct index(like 1,2,3,...)!");
continue;
}
};
return number;
}
}
fn overflow_check(num1: usize, num2: usize) -> bool {
if num1 > (usize::MAX / 2) && num2 > (usize::MAX / 2) {
return false;
} else if num1 > (usize::MAX / 2) || num2 > (usize::MAX / 2) {
let checker: isize = num1 as isize - (usize::MAX / 2) as isize ;
let checker1: usize = num2 - (usize::MAX / 2);
let checker: isize = checker + checker1 as isize;
if checker > 0 { return false } else { return true }
} else {
return true;
}
}
fn fibonacci(index: usize) -> usize {
let mut f0: usize = 0;
let mut f1: usize = 1;
let mut result: usize = f0;
for _i in 1..index {
if overflow_check(f0, f1) {
result = f0 + f1;
f0 = f1;
f1 = result;
} else {
println!("It's too big!!!");
return 0;
}
}
match index {
0 => 0,
1 => 1,
_ => result
}
}
fn main() {
fibonacci_demo();
println!("Please enter the index of the fibonacci sequence:");
let index: usize = get_input_number();
println!("The {index}th Fibonacci number: {}", fibonacci(index));
}
(2)递归方法实现:
使用递归方式实现,与上面代码不同之处在于 fibonacci
函数的实现,因此下面只展示 fibonacci
函数,其余代码与上文相同。
fn fibonacci(index: usize) -> usize {
if index < 2 {
return 1 * (1 & index);
}
// 无数据溢出检查的返回,输出过大会报 “栈溢出” 错误
return fibonacci(index - 1) + fibonacci(index - 2);
// 有数据溢出检查
/*
if overflow_check( fibonacci(index - 1), fibonacci(index - 2) ) {
return fibonacci(index - 1) + fibonacci(index - 2);
} else {
println!("It's too big!!!");
return 0;
}
*/
}
3.2. 功能验证
下面验证结果仅使用常规方式实现的代码。
运行结果展示:
# 查看第 0 项
imaginemiracle:fibonacci$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/fibonacci`
The Fibonacci sequence demo: 0、1、1、2、3、5、8、13、21、34、...
Please enter the index of the fibonacci sequence:
0
The 0th Fibonacci number: 0
# 查看第 1 项
imaginemiracle:fibonacci$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/fibonacci`
The Fibonacci sequence demo: 0、1、1、2、3、5、8、13、21、34、...
Please enter the index of the fibonacci sequence:
1
The 1th Fibonacci number: 1
# 查看第 2 项
imaginemiracle:fibonacci$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/fibonacci`
The Fibonacci sequence demo: 0、1、1、2、3、5、8、13、21、34、...
Please enter the index of the fibonacci sequence:
2
The 2th Fibonacci number: 1
# 查看第 16 项
imaginemiracle:fibonacci$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/fibonacci`
The Fibonacci sequence demo: 0、1、1、2、3、5、8、13、21、34、...
Please enter the index of the fibonacci sequence:
16
The 16th Fibonacci number: 987
为了验证其计算功能,这里运行了多次。
再来看看它的错误输入检查功能如何:
imaginemiracle:fibonacci$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/fibonacci`
The Fibonacci sequence demo: 0、1、1、2、3、5、8、13、21、34、...
Please enter the index of the fibonacci sequence:
asljd
Please enter the correct index(like 1,2,3,...)!
-123
Please enter the correct index(like 1,2,3,...)!
qq
Please enter the correct index(like 1,2,3,...)!
🤗
Please enter the correct index(like 1,2,3,...)!
12
The 12th Fibonacci number: 144
笔者在这里还添加了上限检测函数,用于防止数值过大而导致的溢出错误。
imaginemiracle:fibonacci$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/fibonacci`
The Fibonacci sequence demo: 0、1、1、2、3、5、8、13、21、34、...
Please enter the index of the fibonacci sequence:
12312
It's too big!!!
The 12312th Fibonacci number: 0
目前姑且看起来是没问题的,那么这个题目暂时就这样了。
补充:
这里大家可能会问,为什么不验证递归实现的代码,是有问题吗?
回答:不是的,不,不完全是的。
按照代码的思路,是完全没有问题的,各位同学也可以使用上面递归的代码运行起来,输出小一点的索引(< 50
)进行测试,结果一定是可以计算出来,且正确。为什么不建议输出大于 50
的值测试,原因就是,代码使用了递归,要知道,使用递归的好处是:代码简洁,实现简单; 同样的使用递归实现带来的弊端是:代码逻辑复杂,代码重入次数多,时间复杂度高。 通俗来讲就是 “递归实现是用复杂的处理逻辑和大量的代码重入,来换取简单的实现。可以用简单的几行代码实现复杂的功能。”
如果是按照上文中无溢出校验的递归实现的话,那么计算 index = n
结果, fibonacci()
函数的重入次数为 Fibonacci[n + 1]
(n > 2
时)。Fibonacci
数列为 0、1、1、2、3、5、8、13、21...
,当 n > 2
时,用递归计算出第 n
个斐波那契数,则需要执行第 n + 1
个斐波那契数次的 fibonacci()
函数。
举个栗子🌰:
想要计算第 3
个斐波那契数,就需要执行 3
次 fibonacci()
得出结果为 2
;
想要计算第 4
个斐波那契数,就需要执行 5
次 fibonacci()
得出结果为 3
;
想要计算第 5
个斐波那契数,就需要执行 8
次 fibonacci()
得出结果为 5
;
……
想要计算第 43
个斐波那契数,就需要执行 701408733
次 fibonacci()
得出结果为 433494437
;
……
想要计算第 100
个斐波那契数,就需要执行 573147844013817200000
次 fibonacci()
得出结果为 354224848179262000000
。
执行这么多次你的电脑得算很久很久(具体时间笔者没有统计),总之这种方法在数值较小的时候还是可以用用的,主要学习一下递归的思路。如果聪明的大家有更优秀的递归方法,欢迎在下面留言或直接私信笔者,非常愿意与大家一同讨论问题。
4. 所用到的知识点
在之前的文章中可能对几个函数用法以及原理没有解释清楚,这里重新整理一下。
4.1. str::trim()
str::trim() 该函数将会为调用它的字符串去除前后的空格或回车和换行,然后返回处理后的字符串。
返回值:去除前后空格或回车或换行后的字符串。
其函数原型为:
pub fn trim(&self) -> &str {
self.trim_matches(|c: char| c.is_whitespace())
}
示例:
fn main() {
let mut str = " @@ImagineMiracle## ** \n\n\n";
println!("{}", str);
str = str.trim();
println!("{}", str);
}
运行结果:
imaginemiracle:trim$ cargo run
Compiling trim v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/trim)
Finished dev [unoptimized + debuginfo] target(s) in 0.23s
Running `target/debug/trim`
@@ImagineMiracle## **
@@ImagineMiracle## **
4.2. str::parse()
str::parse(),该函数将字符串解析为另一种类型。
返回值:Result
类型
因此需要对其返回的 Result
处理。
函数原型:
pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> {
FromStr::from_str(self)
}
示例:
fn main() {
let str = "27149";
// 使用 match 处理 Result
let num: u32 = match str.parse::<u32>() {
Ok(n1) => n1,
Err(_) => {
println!("Error.");
0
}
};
println!("str to num: {str} -> {num}");
// 使用 expect 处理 Result
let num: u32 = str.parse::<u32>().expect("Failed to u32");
println!("str to num: {str} -> {num}");
// 使用 unwrap 处理 Result,出错后将会程序将会报 panic 错误同时崩溃并退出
let num: u32 = str.parse().unwrap();
println!("str to num: {str} -> {num}");
}
运行程序:
imaginemiracle:parse$ cargo run
Compiling parse v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/parse)
Finished dev [unoptimized + debuginfo] target(s) in 0.55s
Running `target/debug/parse`
str to num: 27149 -> 27149
str to num: 27149 -> 27149
str to num: 27149 -> 27149
4.3. match 表达式
match
表达式可用于匹配 arm
中的各个条件,而选择性的执行其中一个符合条件的 arm
。
示例01:
fn main() {
let num = 3;
match num {
0 => println!("zero"),
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("something else"),
}
}
运行结果:
imaginemiracle:match_test$ cargo run
Compiling match_test v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/match_test)
Finished dev [unoptimized + debuginfo] target(s) in 1.02s
Running `target/debug/match_test`
three
示例02:
可以使用简单的条件作为判断。
fn main() {
let nums = [1, 2, 3, 6];
for num_i in nums {
let message = match num_i {
0 | 1 => "0 or 1",
2 ..= 3 => "[2, 3]",
4.. => "[4, ...)",
_ => "something else"
};
println!("{}", message);
}
}
其中 ..=
和 ..
表示范围,2 ..= 3
表示 2 <= x <= 3
,4..
表示 x >= 4
。
运行结果:
imaginemiracle:match_test$ cargo run
Compiling match_test v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/match_test)
Finished dev [unoptimized + debuginfo] target(s) in 0.38s
Running `target/debug/match_test`
0 or 1
[2, 3]
[2, 3]
[4, ...)
示例03:
也可在其中使用 if
表达式做条件判断。
fn main() {
let str1 = "Imagine";
let str2 = "Miracle";
match str1 {
str if (str == "Imagine") => println!("Imagine"),
str if (str == "Miracle") => println!("Miracle"),
_ => println!("Others")
}
match str2 {
str if (str == "Imagine") => println!("Imagine"),
str if (str == "Miracle") => println!("Miracle"),
_ => println!("Others")
}
}
运行结果:
imaginemiracle:match_test$ cargo run
Compiling match_test v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/match_test)
Finished dev [unoptimized + debuginfo] target(s) in 0.23s
Running `target/debug/match_test`
Imagine
Miracle
Boys and Girls!!!
准备好了吗?下一节我们要还有一个小练习要做哦!
不!我还没准备好,让我先回顾一下之前的。
上一篇《Rust 中的条件、循环结构——Rust语言基础08》
我准备好了,掛かって来い(放马过来)!
下一篇《Rust 实现摄氏度与华氏度相互转换——Rust语言基础10》
觉得这篇文章对你有帮助的话,就留下一个赞吧v*
请尊重作者,转载还请注明出处!感谢配合~
[作者]: Imagine Miracle
[版权]: 本作品采用知识共享署名-非商业性-相同方式共享 4.0 国际许可协议进行许可。
[本文链接]: https://blog.csdn.net/qq_36393978/article/details/125741139