經過歐拉計劃學Rust編程(第500題)

因爲研究Libra等數字貨幣編程技術的須要,學習了一段時間的Rust編程,一不當心刷題上癮。git

「歐拉計劃」的網址: https://projecteuler.netgithub

英文若是不過關,能夠到中文翻譯的網站: http://pe-cn.github.io/編程

這個網站提供了幾百道由易到難的數學問題,你能夠用任何辦法去解決它,固然主要還得靠編程,編程語言不限,論壇裏已經有Java、C#、Python、Lisp、Haskell等各類解法,固然若是你直接用google搜索答案就沒任何樂趣了。數組

此次解答的是第500題:微信

https://projecteuler.net/problem=500編程語言

題目描述:學習

120的因子個數爲16,事實上120是最小的有16個因子的數。網站

找出最小的有2^500500個因子的數,給出這個數除以500500507的餘數。google

.net

解題過程:

直接看最終的問題,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爲素數因子。

image

那麼,它的因子個數爲:

image

最終的因子個數能夠表示爲2 ^ 500500形式,令:

image

則有:

image

最終的結果要讓[b0, b1, b2...bi]的和爲500500。如今來看一下這個數組是如何變化的,找出遞推的規律。

image

因子個數 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。

image

若是其中的某一項增1,則數值增長:

image

若是尾部增長一項,數值增長:

image

上面的數值中,哪一項更小,則表示或者在尾部增長一個,或者原數組中的數值增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電子書。

相關文章
相關標籤/搜索