最近想學習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
近期文章: