最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,因此先补一下Rust的基础知识。学习了一段时间,发现Rust的学习曲线很是陡峭,不过仍有快速入门的办法。git
学习任何一项技能最怕没有反馈,尤为是学英语、学编程的时候,必定要“用”,学习编程时有一个很是有用的网站,它就是“欧拉计划”,网址: https://projecteuler.net编程
这个网站提供了几百道由易到难的数学问题,你能够用任何办法去解决它,固然主要还得靠编程,编程语言不限,论坛里已经有Java、C#、Python、Lisp、Haskell等各类解法,固然若是你直接用google搜索答案就没任何乐趣了。数组
学习Rust最好先把基本的语法和特性看过一遍,而后就能够动手解题了,解题的过程就是学习、试错、再学习、掌握和巩固的过程,学习进度会大大加快。性能优化
问题描述:编程语言
1到1000用英文单词写下来,求总字符个数(空格和连字符不算),例如:342,英文单词是:three hundred and forty-two。函数式编程
问题分解:函数
1到19的拼写比较特殊,须要分别对待,而超过20的数,能够利用递归调用。这里能够学到String的语法知识点。Rust中的字符串有点烦人,list[n].to_string()、"one thousand".to_string()的这种写法让人很是不适应。除了String以外,还有字符串切片(slice)、字符常量,须要看基础教程慢慢理解。性能
fn english_number(n: usize) -> String { let list0_9 = vec![ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", ]; if n <= 9 { return list0_9[n].to_string(); } if n <= 19 { let list = vec![ "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", ]; return list[n - 10].to_string(); } if n <= 99 { let a: usize = n / 10; // 十位 let b: usize = n % 10; let list = vec![ "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" ]; let str = list[a].to_string(); if b > 0 { return str + "-" + &english_number(b); } return str; } if n <= 999 { let a: usize = n / 100; // 百位 let b: usize = n % 100; let str = list0_9[a].to_string() + " hundred"; if b > 0 { return str + " and " + &english_number(b); } return str; } if n == 1000 { return "one thousand".to_string(); } return "unknown".to_string(); }
从字符串里移除特定的字符,要利用函数式编程,还有filter()和collect()函数,一鼓作气。filter()函数中的*c又是让人写错的地方。学习
fn remove_space(s: &str) -> String { s.chars().filter(|c| *c != ' ' && *c != '-').collect() }
主程序就比较容易了,求和便可。优化
let mut sum = 0; for n in 1..=1000 { let s = remove_space(&english_number(n)); sum += s.len(); // println!("{}: {} {}", n, english_number(n), s.len()); } println!("{}", sum);
问题描述:
从堆成三角的数字中,找到一条路径,使其和最大,求和。一个节点的下一个点只能是下一层的左、右节点。
为了节省内存空间,用一维数组表示这些数,须要准确地计算出各个索引位置的行号,我为了方便,最上一层的行号为1。
let w = [ 75, // row 1 95, 64, // row 2 17, 47, 82, // row 3 18, 35, 87, 10, 20, 04, 82, 47, 65, 19, 01, 23, 75, 03, 34, 88, 02, 77, 73, 07, 63, 67, 99, 65, 04, 28, 06, 16, 70, 92, 41, 41, 26, 56, 83, 40, 80, 70, 33, 41, 48, 72, 33, 47, 32, 37, 16, 94, 29, 53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14, 70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57, 91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48, 63, 66, 04, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31, 04, 62, 98, 27, 23, 09, 70, 98, 73, 93, 38, 53, 60, 04, 23, ];
在数组中的第n个数,行号是多少?利用了第r行必定有r个数的性质。
fn row(n: usize) -> usize { let mut s = 0; for r in 1.. { s += r; if s > n {return r;} } return 0; }
根据上面行号的性质,能够得出节点n的下一层的左节点的编号是 n + r,右节点是 n + r + 1。
求路径的和的时候能够利用递归,终止条件是遇到最底一层的时候,因为原题只让求路径长度,这里没有记下来所走路径的编号。
fn max_path_weight(w: &[u32], n: usize) -> u32 { if n >= w.len() {return 0;} //越界判断 let r = row(n); let bottom_row = row(w.len() - 1); if r == bottom_row { // 递归的退出条件 return w[n]; } let left = max_path_weight(w, n + r); let right = max_path_weight(w, n + r + 1); let max = if left > right {left} else {right}; return w[n] + max; }
主程序调用只须要一行,数组的总层数很少,复杂度不高,没再作进一步的性能优化。
println!("{}", max_path_weight(&w, 0));
问题描述:
求20世纪(1901年1月1日到2000年12月31日)有多少个月的一号是星期日?
本题固然能够利用闰年的性质,只用数学公式就能算出来,这里用编程办法,熟悉一下日期时间的函数库。
关于日期的库用chrono,网上有些资料比较老,建议直接参考官网上的帮助,写得很是详细,少走一些弯路。
在https://docs.rs 网站上搜索chrono便可。
use chrono::prelude::*; use time::Duration;
代码简单粗暴,每次加一天,用到Duration;判断星期几用到weekday(),判断几号用了day(),逻辑很简单。
let mut count = 0; let mut d = Utc.ymd(1901, 1, 1); while d <= Utc.ymd(2000, 12, 31) { if d.weekday() == Weekday::Sun && d.day() == 1 { println!("{}", d); count += 1; } d = d + Duration::days(1); } println!("{}", count);
问题描述:
求100的阶乘中全部数字之和。
本题与第16题很是类似,稍微修改就出来,不解释。
let mut prod = BigUint::from(1 as u64); for i in 1..=100 { prod *= BigUint::from(i as u64); } let full_str = prod.to_str_radix(10); let s = full_str .chars() .map(|c| c.to_digit(10).unwrap()) .sum::<u32>(); println!("{}", s);
问题描述:
10000以内的全部亲和数之和。所谓亲和数,是指两个正整数中,彼此的所有约数之和(自己除外)与另外一方相等。好比,220的因子有1, 2, 4, 5, 10, 11, 20, 22, 44, 55 和 110,因子之和是 284,而284的全部因子是 1, 2, 4, 71 和 142,因子之和是220。
问题分解:
在第12题里已经求出了一半的因子,函数是:
fn half_factors(num: u32) -> Vec<u32> { let s = (num as f32).sqrt() as u32; (1..=s).filter(|x| num % x == 0).collect::<Vec<u32>>() }
很容易补上后面的一半因子。
fn proper_divisors(num: u32) -> Vec<u32> { let mut v = half_factors(num); for i in (1..v.len()).rev() { //不要num自身,因此从1开始 v.push(num / v[i]); } v }
全部因子求和:
fn proper_divisors_sum(num: u32) -> u32 { let divs = proper_divisors(num); divs.iter().sum::<u32>() }
主程序暴力循环便可,10000以内只有5对亲和数:
let mut sum = 0; for a in 1u32..10000 { let b = proper_divisors_sum(a); if a != b && proper_divisors_sum(b) == a { sum += a; println!("{} {}", a, b); } } println!("{}", sum);
由于亲和数是成对出现的,还能够优化性能,引入一个数组,这里再也不展开了。
在projecteuler中注册一个帐号,能够添加好友,一块儿讨论学习,个人Key是:
1539870_KBNiIXymh4SnmDEDZmUTg7tu1MTBVlLj
近期文章: