最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,因此先补一下Rust的基础知识。学习了一段时间,发现Rust的学习曲线很是陡峭,不过仍有快速入门的办法。算法
学习任何一项技能最怕没有反馈,尤为是学英语、学编程的时候,必定要“用”,学习编程时有一个很是有用的网站,它就是“欧拉计划”,网址: https://projecteuler.net编程
这个网站提供了几百道由易到难的数学问题,你能够用任何办法去解决它,固然主要还得靠编程,编程语言不限,论坛里已经有Java、C#、Python、Lisp、Haskell等各类解法,固然若是你直接用google搜索答案就没任何乐趣了。数组
学习Rust最好先把基本的语法和特性看过一遍,而后就能够动手解题了,解题的过程就是学习、试错、再学习、掌握和巩固的过程,学习进度会大大加快。缓存
问题描述:编程语言
从文件中读取一堆名字,按字母顺序排序,求名字分总和。名字分 = 顺序号 * 名字中几个字母的序号和。ide
例如:COLIN,全部字符在字母表中的序号之和,3 + 15 + 12 + 9 + 14 = 53,COLIN名字排在第938个,该名字的得分为938 × 53 = 49714。函数式编程
问题分解:函数
1)读文件,移除引号性能
2)把名字存储在Vec向量中学习
3)排序
4)求字符在字母表中的序号
5)求单词的分数
6)求总分
正式开始:
1)首先把文件读到一个字符串中。
use std::fs; fn main() { let data = fs::read_to_string("names.txt") .expect("读文件失败"); println!("{}", data); }
名字中都带着引号,须要移除,能够利用函数式编程,还有filter()和collect()函数,一鼓作气。filter()函数中的*c又是让人容易出错的地方。
fn remove_quote(s: &str) -> String { s.chars().filter(|c| *c !='"').collect() }
2)每一个名字是用逗号分开的,因此能够用split()函数,分解成向量。
let data2 = remove_quote(&data); let names: Vec<&str> = data2.split(",").collect(); println!("{:?}", names);
3)向量有专门的排序函数,须要将变量定义为可修改的。
let mut names: Vec<&str> = data2.split(",").collect(); names.sort();
4)字符在字母表中的顺序号,能够求find(),也能够用position()函数。
fn letter_number(ch: char) -> usize { let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; letters.chars().position(|c| c == ch).unwrap() + 1 }
5)求一个单词的分数
fn word_score(word: &str) -> usize { let mut score = 0; for ch in word.chars() { score += letter_number(ch); } score }
6)如今能够求总分了,有一个很是有用的for循环的用法,能够既获得元素,还能够获得元素的索引号,利用enumerate()函数。
let mut score = 0; for (i, name) in names.iter().enumerate() { let ws = word_score(name); println!("{} {} {}", (i+1), name, ws); score += ws * (i + 1); } println!("{}", score);
完整的main()代码:
let data = std::fs::read_to_string("names.txt").expect("读文件失败"); let data2 = remove_quote(&data); let mut names: Vec<&str> = data2.split(",").collect(); names.sort(); let mut score = 0; for (i, name) in names.iter().enumerate() { let ws = word_score(name); println!("{} {} {}", (i + 1), name, ws); score += ws * (i + 1); } println!("{}", score);
语法点:
1)std::fs读文件
2)字符串的split()函数
3)排序函数sort()
4)字符串中查找一个字符的位置
5)enumerate()迭代器,能够产生序号和元素
问题描述:
富裕数是指因子之和大于自身的数,例如12的全部因子和,1 + 2 + 3 + 4 + 6 = 16, 由于16 > 12,因此12是富裕数。
数学上已经证实,超过28123的数均可以分解为2个富裕数之和。
求全部不能分解为两个富裕数之和的正整数的总和。
求解过程:
1)求全部因子(不包含自身)
2)判断是否为富裕数
3)判断是否能够分解为2个富裕数之和
4)求解最后的问题
第一步求因子,在第21题中已经求过,但这里发现它的一个BUG,对于4, 9, 16, 25这样的彻底平方数,因子会多出来一个。
修改以后是这样:
fn proper_divisors(num: u32) -> Vec<u32> { let mut v = { // 求一半的因子 let s = (num as f32).sqrt() as u32; (1..=s).filter(|x| num % x == 0).collect::<Vec<u32>>() }; let last = v.last().unwrap(); if last * last == num { // 16的一半因子为1,2,4,另外只差一个8,即16 / 2 for i in (1..v.len()-1).rev() { v.push(num / v[i]); } } else { // 12的一半因子为1,2,3,另一半因子:4,6,分别对应于12/3,12/2 for i in (1..v.len()).rev() {//不要num自身,因此从1开始 v.push(num / v[i]); } } v }
第二步判断是否为富裕数,逻辑简单。
fn is_abundant_number(num: u32) -> bool { let proper_divisors_sum = proper_divisors(num).iter().sum::<u32>(); proper_divisors_sum > num }
第三步,进行分解判断时,出于性能考虑,须要将富裕数的结果缓存在一个数组中。
let mut abundant_numbers = vec![false; 28124]; for i in 2usize..abundant_numbers.len() { if is_abundant_number(i as u32) { abundant_numbers[i] = true; } }
判断是否能够分解为2个富裕数,只需暴力循环。
fn can_divide(abundant_numbers: &[bool], num: u32) -> bool { for x in 1..=28123 { let y = num - x; if y <= 0 {break;} if abundant_numbers[x as usize] && abundant_numbers[y as usize] { // println!("{} = {} + {}", num, x, y); return true; } } return false; }
第四步,把不可分解的数字求和。
let mut sum = 0; for i in 1..=28123 { if !can_divide(&abundant_numbers, i) { sum += i; } } println!("sum: {}", sum);
固然这求和的几行语句,也能够用函数式编程把它浓缩在一行:
println!("sum: {}", (1..=28123).filter(|&x| !can_divide(&abundant_numbers, x)) .sum::<u32>() );
语法知识点:
问题描述:
0,1,2,3,4,5,6,7,8和9,每一个数字用且只用一次,称为全排列,按数值大小排序,求第一百万个数是多少?
例如,0,1和2按从小到大只有6种排列:012,021,102,120,201,210。
解法:
这是一道排列组合类的数学题,在百度文库中有一个PPT介绍得不错,连接:https://wk.baidu.com/view/5f4bacf79e31433239689339?pcf=2&fromShare=1&fr=copy©fr=copylinkpop
这道题能够利用其中的字典序的算法:
实现这个算法不太麻烦,只是须要细心一些。
fn next_perm(v: &mut Vec<u32>) { let mut i = v.len() - 2; while v[i] > v[i + 1] { i -= 1; } let mut j = v.len() - 1; while i < j && v[i] > v[j] { j -= 1; } swap(v, i, j); i += 1; j = v.len() - 1; while i < j { swap(v, i, j); i += 1; j -= 1; } } fn swap(v: &mut Vec<u32>, i: usize, j: usize) { let temp = v[i]; v[i] = v[j]; v[j] = temp; }
主程序只须要循环就好了,向量的初始值就是第一个排列,从2开始找到第100万个排列数。
fn main() { let mut v: Vec<u32> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; for i in 2..=1_000_000 { next_perm(&mut v); //println!("{} {:?}", i, v); } println!("{:?}", v); }
这里的v是向量表示,要转换成一个整数,能够这样:
let v_str = v.iter() .map(|x| x.to_string()) .collect::<String>(); println!("{}", v_str.parse::<u64>().unwrap());
这里的组合数一直是10个数字,也能够换成定长数组的写法,
fn main() { let mut v = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; for i in 2..=1_000_000 { next_perm(&mut v); } println!("{:?}", v); } fn next_perm(v: &mut [u32]) { // 保持不变 } fn swap(v: &mut [u32], i: usize, j: usize) { let temp = v[i]; v[i] = v[j]; v[j] = temp; }
语法知识点:
问题描述:
在斐波那契数列中,第一个有1000位数字的是第几项?
本题与第16题很是类似,稍微修改就出来,不解释。
extern crate num_bigint; use num_bigint::BigUint; fn main() { let mut prev = BigUint::from(1 as u64); let mut cur = BigUint::from(1 as u64); for i in 3.. { let next = prev + &cur; let str = next.to_string(); if str.len() >= 1000 { println!("{} {} {}", i, str, str.len()); break; } prev = cur; cur = next; } }
在projecteuler中注册一个帐号,能够添加好友,一块儿讨论学习,个人Key是:
1539870_KBNiIXymh4SnmDEDZmUTg7tu1MTBVlLj
近期文章: