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