因爲研究Libra等數字貨幣編程技術的須要,學習了一段時間的Rust編程,一不當心刷題上癮。我把解決63道問題的過程記錄了下來,寫成了一本《用歐拉計劃學 Rust 編程》PDF電子書,請隨意下載。git
連接:https://pan.baidu.com/s/1NRfTwAcUFH-QS8jMwo6pqwgithub
提取碼:qfha算法
「歐拉計劃」的網址:
https://projecteuler.net編程
英文若是不過關,能夠到中文翻譯的網站:
http://pe-cn.github.io/數組
這個網站提供了幾百道由易到難的數學問題,你能夠用任何辦法去解決它,固然主要還得靠編程,編程語言不限,論壇裏已經有Java、C#、Python、Lisp、Haskell等各類解法,固然若是你直接用google搜索答案就沒任何樂趣了。微信
此次解答的是第61題:閉包
https://projecteuler.net/problem=61編程語言
題目描述:函數
循環的多邊形數學習
三角形數、正方形數、五邊形數、六邊形數、七邊形數和八邊形數統稱爲多邊形數。它們分別由以下的公式給出:
三角形數 | P3,n=n(n+1)/2 | 1, 3, 6, 10, 15, … |
正方形數 | P4,n=n2 | 1, 4, 9, 16, 25, … |
五邊形數 | P5,n=n(3n−1)/2 | 1, 5, 12, 22, 35, … |
六邊形數 | P6,n=n(2n−1) | 1, 6, 15, 28, 45, … |
七邊形數 | P7,n=n(5n−3)/2 | 1, 7, 18, 34, 55, … |
八邊形數 | P8,n=n(3n−2) | 1, 8, 21, 40, 65, … |
由三個4位數812八、288二、8281構成的有序集有以下三個有趣的性質。
存在惟一一個包含六個4位數的有序循環集,每種多邊形數——三角形數、正方形數、五邊形數、六邊形數、七邊形數和八邊形數——在其中各有一個表明。求這個集合的元素和。
〓
〓
〓
〓
請
先
不
要
直
接
看
答
案
,
最
好
自
己
先
嘗
試
一
下
。
解題過程:
把複雜的問題分解爲一個一個的簡單問題。
第一步: 先把全部四位數求出來
利用map()、filter()和take_while()等函數,能夠用一行語句將數組生成。
let p3: Vec<u32> = (1..) .map(|n| n * (n + 1) / 2) .filter(|&n| n > 1000) .take_while(|&n| n <= 9999) .collect(); let p4: Vec<u32> = (1..) .map(|n| n * n) .filter(|&n| n > 1000) .take_while(|&n| n <= 9999) .collect();
若是這樣生成六種多邊形數,會有大量的重複代碼,能夠閉包做爲函數的參數,統一寫一個函數,這樣代碼很是簡練。
let p3 = gen_poly_numbers(1000, 9999, |n| n * (n + 1) / 2); let p4 = gen_poly_numbers(1000, 9999, |n| n * n); let p5 = gen_poly_numbers(1000, 9999, |n| n * (3 * n - 1) / 2); let p6 = gen_poly_numbers(1000, 9999, |n| n * (2 * n - 1)); let p7 = gen_poly_numbers(1000, 9999, |n| n * (5 * n - 3) / 2); let p8 = gen_poly_numbers(1000, 9999, |n| n * (3 * n - 2)); fn gen_poly_numbers(start: u32, end: u32, f: fn(u32) -> u32) -> Vec<u32> { (1..).map(f) .filter(|&n| n >= start) .take_while(|&n| n <= end) .collect() }
這裏的一個語法知識點是f: fn(u32) -> u32的寫法,把一個函數做爲參數傳遞給另外一個函數。
第二步: 遞歸算法
如今仍不要直接奔向最後的問題,先從簡單的狀況入手,用題目中給出的三個多邊形數[8128, 8281, 2882],實現一個遞歸算法,驗證算法的正確性。
算法描述:
1)從p3中的取一個數 for n in p3
2)假設這個數是8128,記錄到found數組中,咱們下一步的任務是從[p4, p5]數組中尋找28開頭,81結尾的兩個數,僞代碼:find([p4, p5], 28, 81, [8128])
3)先在p4中找,再在p5中找 for cur_list in lists
4)在數組中尋找以28開頭的數 for n in cur_list
5)假設是在p5中找到了2882,此時found數組變爲[8128, 2882],遞歸調用,在[p4]中查找以82開頭,81結尾的一個數 find([p4], 82, 81, [8128, 2882])
5.1)在p4裏會找到一個以82開頭的數,即8281,更新found,再調用 find([], 81, 81 [8128, 2882, 8281]
5.2)從這裏能夠發現遞歸函數的退出機制,待查的數組已經爲空,要找的開頭與結尾正好相等。
遞歸函數的源代碼:
fn find(lists: &[&[u32]], start: u32, end: u32, found: &[u32]) { if lists.is_empty() && start == end { let result = found.iter().sum::<u32>(); println!("{:?} {}", found, result); } for (i, &cur_list) in lists.iter().enumerate() { for &n in cur_list { if head(n) == start { let mut lists_copy = lists.to_vec(); lists_copy.remove(i); let mut found_copy = found.to_vec(); found_copy.push(n); find(&lists_copy, tail(n), end, &found_copy); } } } }
lists.copy.remove(i)的含義,假設當前搜索的數組爲[p4, p5, p6, p7, p8],若是在p5中找到了知足條件的數,後續的搜索範圍將是[p4, p6, p7, p8],這裏的i記住數組中的位置,remove(i)將刪除p5。
主程序中的代碼:
for n in p3 { find(&[&p4, &p5], n % 100, n / 100, &[n]); }
第三步:解決最終的問題
搜索3個數的代碼測試經過後,再搜索6個數。
for n in p3 { find(&[&p4, &p5, &p6, &p7, &p8], n % 100, n / 100, &[n]); }
因爲p8中的數據元素較少,先搜索p8可能會稍快一點。
--- END ---
我把解決這些問題的過程記錄了下來,寫成了一本《用歐拉計劃學 Rust 編程》PDF電子書,請隨意下載。
連接:https://pan.baidu.com/s/1NRfTwAcUFH-QS8jMwo6pqw
提取碼:qfha
因爲歐拉計劃不讓發佈100題以外的解題步驟,不然封號,因此最新PDF再也不公開,請加我微信(SLOFSLB)索要最新的PDF電子書。