前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通过欧拉计划学Rust编程(第69题)

通过欧拉计划学Rust编程(第69题)

作者头像
申龙斌
发布2020-02-25 15:34:30
6740
发布2020-02-25 15:34:30
举报

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

刷完欧拉计划中的63道基础题,能学会Rust编程吗?

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

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

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

这次解答的是第69题:

https://projecteuler.net/problem=69

题目描述:

欧拉总计函数与最大值

在小于n的数中,与n互质的数的数目记为欧拉总计函数φ(n)(有时也称为φ函数)。例如,因为1、2、4、5、7和8均小于9且与9互质,故φ(9)=6。

n

互质的数

φ(n)

n/φ(n)

2

1

1

2

3

1, 2

2

1.5

4

1, 3

2

2

5

1, 2, 3, 4

4

1.25

6

1, 5

2

3

7

1, 2, 3, 4, 5, 6

6

1.1666…

8

1, 3, 5, 7

4

2

9

1, 2, 4, 5, 7, 8

6

1.5

10

1, 3, 7, 9

4

2.5

可以看出,对于n ≤ 10,当n=6时n/φ(n)取得最大值。

当n ≤ 1,000,000时,求使得n/φ(n)取得最大值的n。

解题过程:

遇到一个复杂的问题,可以先从简单的情况入手,寻找一些规律,再慢慢逼近最终的问题。

第一步: 暴力求解

先根据题意暴力求解。

代码语言:javascript
复制
fn main() {
    let mut max_ratio = 0_f64;
    for n in 2..=1_000_000 {
        // 最大公约数为1,表示互质
        let phi = (1..n).filter(|&x| gcd(x, n) == 1).count();
        let ratio = (n as f64) / (phi as f64);
        if ratio > max_ratio {
            println!("n= {:6}  phi={:6}  n/phi= {:.4}", n, phi, ratio);
            max_ratio = ratio;
        }
    }
}

// 最大公约数
fn gcd(a: u64, b: u64) -> u64 {
    if b == 0 {
        a
    } else {
        gcd(b, a % b)
    }
}

可以得到部分结果,计算到2310非常快,计算到30030则要1分钟,计算到1百万估计得几天。

代码语言:javascript
复制
n=      2  phi=     1  n/phi= 2.0000
n=      6  phi=     2  n/phi= 3.0000
n=     30  phi=     8  n/phi= 3.7500
n=    210  phi=    48  n/phi= 4.3750
n=   2310  phi=   480  n/phi= 4.8125
n=  30030  phi=  5760  n/phi= 5.2135

第二步: 找规律

通过前面几个结果,可以发现是连续几个素数的乘积,所以问题可以变为求哪些连续素数的乘积小于1百万。

代码语言:javascript
复制
// 2 * 3 * 5 * 7 * 11 * ... 找到最多的素数,乘积在1百万以内
let mut pset = PrimeSet::new();
let mut prod = 1;
for p in pset.iter() {
    if prod * p > 1_000_000 {
        break;
    }
    prod *= p;
    println!("prime={} {}", p, prod);
}
println!("{}", prod);

这里可以练习一下函数式编程,好像可读性很差。

代码语言:javascript
复制
let mut some_primes = (2..).filter(|&x| primes::is_prime(x));
let mut prod = 1;
let some_products = std::iter::repeat_with(|| {
    let tmp = prod;
    prod *= some_primes.next().unwrap();
    tmp}).take_while(|&x| x <= 1_000_000)
         .collect::<Vec<_>>();
println!("{:?}", some_products);
println!("{:?}", some_products.last().unwrap());

用itertools写一下,这样好一些。

代码语言:javascript
复制
let mut some_primes = (2..).filter(|&x| primes::is_prime(x));
let result = itertools::iterate(1, |&prod| prod * some_primes.next().unwrap())
    .take_while(|&x| x <= 1_000_000)
    .last()
    .unwrap();
println!("{:?}", result);

第三步: 补一下数学知识

这个欧拉总计函数,可以推导出数学公式,网上很容易搜到一大堆相关内容。

代码语言:javascript
复制
let mut max_ratio = 0_f64;
for n in 2..=1_000_000 {
    let all_factors = primes::factors(n);
    let uniq_factors = primes::factors_uniq(n);

    let mut phi = 1;
    for p in uniq_factors {
        let k = all_factors.iter().filter(|&x| *x == p).count();
        phi *= p.pow(k as u32 - 1) * (p-1);
    }
    let ratio = (n as f64) / (phi as f64);
    if ratio > max_ratio {
        println!("n= {:6}  phi={:6}  n/phi= {:.4}", n, phi, ratio);
        max_ratio = ratio;
    }
}

看看后一种写法的例子:

用这个公式,程序写起来更简单。

代码语言:javascript
复制
let mut max_ratio = 0_f64;
for n in 2..=1_000_000 {
    let uniq_factors = primes::factors_uniq(n);
    let mut phi = n;
    for p in uniq_factors {
        phi = phi * (p-1) / p;
    }
    let ratio = (n as f64) / (phi as f64);
    if ratio > max_ratio {
        println!("n= {:6}  phi={:6}  n/phi= {:.4}", n, phi, ratio);
        max_ratio = ratio;
    }
}

--- END ---

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

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

提取码:qfha

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 申龙斌的程序人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档