因为研究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电子书。