由于研究Libra等数字货币编程技术的需要,学习了一段时间的Rust编程,一不小心刷题上瘾。

“欧拉计划”的网址: https://projecteuler.net

英文如果不过关,可以到中文翻译的网站: http://pe-cn.github.io/

这个网站提供了几百道由易到难的数学问题,你可以用任何办法去解决它,当然主要还得靠编程,编程语言不限,论坛里已经有Java、C#、Python、Lisp、Haskell等各种解法,当然如果你直接用google搜索答案就没任何乐趣了。

这次解答的是第500题:

https://projecteuler.net/problem=500

题目描述:

120的因子个数为16,事实上120是最小的有16个因子的数。

找出最小的有2^500500个因子的数,给出这个数除以500500507的余数。

解题过程:

直接看最终的问题,2^500500是个天文数字,肯定不能用蛮力。遇到一个复杂的问题,可以先尝试解决简单的情况,然后慢慢逼近最终的问题。

第一步: 从简单的情况入手找规律:

第650题里解决过因子个数的公式,还可以计算出所有因子之和。

fn min_number_has_factors(x: u64) -> u64 {
for n in 2.. {
let groups = factors_group(n);
let factors_num = groups.iter().map(|(_, x)| x + 1).product::<u64>();
if factors_num == x {
println!("{}, divisors num: {}", n, factors_num);
print_factors_group(groups);
return n;
}
}
0
} // 如果一个数有这些因子:[2, 2, 3, 3, 3, 3, 5, 7]
// 则得到:[(2,2), (3,4), (5,1), (7,1)]
fn factors_group(n: u64) -> Vec<(u64, u64)> {
let factors = primes::factors(n);
let groups = factors
.iter()
.group_by(|e| **e)
.into_iter()
.map(|(k, group)| (k, group.count() as u64))
.collect::<Vec<(u64, u64)>>();
groups
} fn print_factors_group(groups: Vec<(u64, u64)>) {
println!(
"{}",
&groups
.iter()
.map(|(k, v)| k.to_string() + &"^" + &v.to_string())
.join(" * ")
);
println!(
"divisors num: {}",
&groups
.iter()
.map(|(_, v)| "(".to_string() + &v.to_string() + &"+1)")
.join(" * ")
);
}

现在先尝试计算几个,慢慢寻找规律。

min_number_has_factors(4); // 2^2
min_number_has_factors(8); // 2^3
min_number_has_factors(16); // 2^4
min_number_has_factors(32); // 2^5
min_number_has_factors(64); // 2^6
min_number_has_factors(128); // 2^7
min_number_has_factors(256); // 2^8

结果有:

6 = 2^1 * 3^1
因子个数 4= (1+1) * (1+1) 24 = 2^3 * 3^1
因子个数 8 = (3+1) * (1+1) 120 = 2^3 * 3^1 * 5^1
因子个数 16 = (3+1) * (1+1) * (1+1) 840 = 2^3 * 3^1 * 5^1 * 7^1
因子个数 32 = (3+1) * (1+1) * (1+1) * (1+1) 7560 = 2^3 * 3^3 * 5^1 * 7^1
因子个数 64 = (3+1) * (3+1) * (1+1) * (1+1) 83160 = 2^3 * 3^3 * 5^1 * 7^1 * 11^1
因子个数 128 = (3+1) * (3+1) * (1+1) * (1+1) * (1+1) 1081080 = 2^3 * 3^3 * 5^1 * 7^1 * 11^1 * 13^1
因子个数 256 = (3+1) * (3+1) * (1+1) * (1+1) * (1+1) * (1+1)

第二步: 努力寻找规律

通过分析几个简单的特例,将一般性的公式推导出来,需要运用基础的数学知识。

一个数n可以分解成如下形式,其中pi为素数因子。

那么,它的因子个数为:

最终的因子个数可以表示为2 ^ 500500形式,令:

则有:

最终的结果要让[b0, b1, b2...bi]的和为500500。现在来看一下这个数组是如何变化的,找出递推的规律。

因子个数 2 =  (2^1)
[b0] = [1] 因子个数 4 = (2^1) * (2^1)
[b0,b1] = [1,1] 因子个数 8 = (2^2) * (2^1)
[b0,b1] = [2,1] 因子个数 16 = (2^2) * (2^1) * (2^1)
[b0,b1,b2] = [2,1,1] 因子个数 32 = (2^2) * (2^1) * (2^1) * (2^1)
[b0,b1,b2] = [2,2,1] 因子个数 64 = (2^2) * (2^2) * (2^1) * (2^1)
[b0,b1,b2,b3] = [2,2,1,1] 因子个数 128 = (2^2) * (2^2) * (2^1) * (2^1) * (2^1)
[b0,b1,b2,b3,b4] = [2,2,1,1,1] 因子个数 256 = (2^2) * (2^2) * (2^1) * (2^1) * (2^1) * (2^1)
[b0,b1,b2,b3,b4,b5] = [2,2,1,1,1,1]

这里需要足够的耐心,这个bi数组或者在末尾增加一个元素1,或者在前面的某个位置上数值增1。

如果其中的某一项增1,则数值增加:

如果尾部增加一项,数值增加:

上面的数值中,哪一项更小,则表示或者在尾部增加一个,或者原数组中的数值增1。

最后的代码:

fn p500(n: usize) -> u64 {
let mut pset = PrimeSet::new();
let primes: Vec<_> = pset.iter().take(n).collect();
let primes_log: Vec<_> = primes.iter().map(|x| (*x as f64).log10()).collect(); let mut b = vec![1];
for _i in 2..=n {
let mut min = primes_log[b.len()];
let mut pos = b.len(); // 默认尾部增加一个
for j in 0..b.len() {
let temp = 2_f64.powf(b[j] as f64) * primes_log[j];
if temp < min {
pos = j;
min = temp;
}
if b[j] == 1 {
break; // 后面的都不用判断了
}
}
if pos == b.len() {
b.push(1);
} else {
b[pos] += 1;
}
} let mut result = 1_u64;
for i in 0..b.len() {
let exp = 2_u32.pow(b[i]) - 1;
for _j in 0..exp {
result = result * primes[i] % 500500507;
}
}
result
}

--- END ---

我把解决这些问题的过程记录了下来,写成了一本《用欧拉计划学 Rust 编程》PDF电子书,请随意下载。

链接:https://pan.baidu.com/s/1NRfTwAcUFH-QS8jMwo6pqw

提取码:qfha

由于欧拉计划不让发布100题之外的解题步骤,否则封号,所以最新PDF不再公开,请加我微信(SLOFSLB)索要最新的PDF电子书。

通过欧拉计划学Rust编程(第500题)的更多相关文章

  1. 通过欧拉计划学Rust编程(第54题)

    由于研究Libra等数字货币编程技术的需要,学习了一段时间的Rust编程,一不小心刷题上瘾. 刷完欧拉计划中的63道基础题,能学会Rust编程吗? "欧拉计划"的网址: https ...

  2. 用欧拉计划学Rust编程(第26题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,所以先补一下Rust的基础知识.学习了一段时间,发现Rust的学习曲线非常陡峭,不过仍有快速入门的办法. 学习任何一项技能最怕没有 ...

  3. 通过欧拉计划学习Rust编程(第22~25题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,所以先补一下Rust的基础知识.学习了一段时间,发现Rust的学习曲线非常陡峭,不过仍有快速入门的办法. 学习任何一项技能最怕没有 ...

  4. 用欧拉计划学Rust语言(第17~21题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,所以先补一下Rust的基础知识.学习了一段时间,发现Rust的学习曲线非常陡峭,不过仍有快速入门的办法. 学习任何一项技能最怕没有 ...

  5. 用欧拉计划学习Rust编程(第13~16题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,所以先补一下Rust的基础知识.学习了一段时间,发现Rust的学习曲线非常陡峭,不过仍有快速入门的办法. 学习任何一项技能最怕没有 ...

  6. 用欧拉计划学Rust语言(第7~12题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,所以先补一下Rust的基础知识.学习了一段时间,发现Rust的学习曲线非常陡峭,不过仍有快速入门的办法. 学习任何一项技能最怕没有 ...

  7. 通过欧拉计划学Rust(第1~6题)

    最近想学习Libra数字货币的MOVE语言,发现它是用Rust编写的,看来想准确理解MOVE的机制,还需要对Rust有深刻的理解,所以开始了Rust的快速入门学习. 看了一下网上有关Rust的介绍,都 ...

  8. 刷完欧拉计划中难度系数为5%的所有63道题,我学会了Rust中的哪些知识点?

    我为什么学Rust? 2019年6月18日,Facebook发布了数字货币Libra的技术白皮书,我也第一时间体验了一下它的智能合约编程语言MOVE,发现这个MOVE是用Rust编写的,看来想准确理解 ...

  9. 【欧拉计划4】Largest palindrome product

    欢迎访问我的新博客:http://www.milkcu.com/blog/ 原文地址:http://www.milkcu.com/blog/archives/1371281760.html 原创:[欧 ...

随机推荐

  1. 次小生成树题(k) poj1679The Unique MST

    http://poj.org/problem?id=1679 #include<iostream> #include<vector> #include<cstdio> ...

  2. Raspberrypi 装配笔记

    1 镜像烧制 2 基础配置 2.1 SSH 连接 2.2 修改管理员密码 2.3 Samba 3 功能配置 3.1 Homebridge 1 镜像烧制 从树莓派官网下载最新的 Raspbian 系统镜 ...

  3. 91)PHP,cookie代码展示

    cookie练习的代码: (1)先设置:setcookie('key值‘,’value值’): (2)然后我执行那个文件, (3)获取我的cookie值,用$_cookie['key值’]  cook ...

  4. Qt QString类及常用函数功能详解

    QString 是 Qt 编程中常用的类,除了用作数字量的输入输出之外,QString 还有很多其他功能,熟悉这些常见的功能,有助于灵活地实现字符串处理功能. QString 存储字符串釆用的是 Un ...

  5. openssl内存分配,查看内存泄露

    openssl内存分配 用户在使用内存时,容易犯的错误就是内存泄露.当用户调用内存分配和释放函数时,查找内存泄露比较麻烦.openssl提供了内置的内存分配/释放函数.如果用户完全调用openssl的 ...

  6. HDU-1040-As Easy As A+B(各种排序)

    希尔排序 Accepted 1040 0MS 1224K 564 B G++ #include "cstdio" using namespace std; ]; int main( ...

  7. 控制webbrowser滚动到指定位置

    在构造函数中添加事件: webBrowser.DocumentCompleted+=new WebBrowserDocumentCompletedEventHandler(webBrowser_Doc ...

  8. 如何有效地报告Bug

    英文原文:Simon Tatham,编译:Dasn 引言 为公众写过软件的人,大概都收到过很拙劣的bug报告,例如: 在报告中说“不好用”: 所报告内容毫无意义: 在报告中用户没有提供足够的信息: 在 ...

  9. 前端自动化构建工具gulp

    1.gulp的安装 首先确保你已经正确安装了nodejs环境.然后以全局方式安装gulp: npm install -g gulp 全局安装gulp后,还需要在每个要使用gulp的项目中都单独安装一次 ...

  10. python基础函数、方法

    python的函数和方法,通过def 定义: 函数的特性: 减少重复代码 使程序变的可扩展 使程序变得易维护 函数和方法的区别:函数有返回值.方法没有 语法定义: def sayhi():#函数名 p ...