Project Euler Problem 70 (C++和Python代码实现和解析)

100 篇文章 3 订阅
87 篇文章 1 订阅

Problem 70 : Totient permutation

Euler’s Totient function, φ(n) [sometimes called the phi function], is used to determine the number of positive numbers less than or equal to n which are relatively prime to n. For example, as 1, 2, 4, 5, 7, and 8, are all less than nine and relatively prime to nine, φ(9)=6.

The number 1 is considered to be relatively prime to every positive number, so φ(1)=1.

Interestingly, φ(87109)=79180, and it can be seen that 87109 is a permutation of 79180.

Find the value of n, 1 < n < 107, for which φ(n) is a permutation of n and the ratio n/φ(n) produces a minimum.

1. 欧拉项目的第70道题:欧拉函数的排列

欧拉函数φ(n) (有时称为Phi函数), 用于确定小于n且与n互质的数的个数。例如:小于9 且与9互质的数有1, 2, 4, 5, 7, 和 8, 所以 φ(9)=6。

数1 被认为对于每一个正整数来说都是互质的数, 所以 φ(1)=1.

有趣地是,φ(87109)=79180, 可以看出数87109 是数79180的一个排列。

对于1 < n < 107, 找到n, 确保φ(n) 是 n 的一个排列,且n/φ(n) 的值最小。

2. 求解分析

这道题跟Project Euler Problem 69 相关,都是有关欧拉函数的题。不同的是这道题要找到n, 让φ(n) 是 n 的一个排列, 且n/φ(n)最小。为了避免盲目的枚举所有的质数,我们用纸和笔分析后知道,当一个数仅有两个质因数的时候 n/φ(n) 会比较小,我们就缩小范围从质数列表中来找到n两个质因数p1, p2, 这样n = p1*p2, φ(n) = (p1-1) * (p2-1),判断φ(n) 是 n 的一个排列后,计算出n/φ(n)的值, 最后找到n, 保证φ(n) 是 n 的一个排列,且n/φ(n)的值最小。

3. C++ 代码实现

我们首先通过质数筛子找到所有的小于一千万的质数,保存到一个数组里(没有使用STL vector是为了节约时间),然后在函数里getTotientPermutation()缩小了范围,搜索p1 = m_primesArray[i] 和p2= m_primesArray[j], n = p1*p2, φ(n) = (p1-1) * (p2-1),然后通过函数getDigitsString()来判断:φ(n)是不是n的一个排列,找到更小的n/φ(n),最后找到最小的n/φ(n)的n。

值得说明的是,函数getDigitsString()使用了C++11的函数to_string(),再使用sort()排序,比用vector 保存一个数的各位上的数字,再排序,要快速很多。

C++11 的代码

#include <iostream>
#include <string>     // to_string(), C++11
#include <cmath>      // sqrt()
#include <ctime>      // clock()
#include <algorithm>  // sort()

using namespace std;

// #define UNIT_TEST

class PE0070
{
private:
    static const int m_max_n = 10000000; // ten million
    bool  *m_primeSieve;                 // prime Sieve
    int   *m_primesArray;
    int    m_numOfPrimes = 0;

    void getAllPrimes();
    string getDigitsString(int number);

public:
    PE0070() 
    { 
        m_primeSieve  = new bool[m_max_n + 1]; 
        m_primesArray = new int[m_max_n / 10]; 
        getAllPrimes();
    }

    ~PE0070() { delete [] m_primeSieve; delete [] m_primesArray; }

    int getTotientPermutation();
};

// use prime Sieve of Eratosthenes to get all primes
void PE0070::getAllPrimes()
{
    memset(m_primeSieve, true, (m_max_n + 1) * sizeof(bool));

    m_primeSieve[0] = m_primeSieve[1] = false;

    for (int i = 2; i <= (int)sqrt((double)m_max_n); i++)
    {
        if (true == m_primeSieve[i])
        {
            for (int j = i * i; j < m_max_n; j += i)
            {
                m_primeSieve[j] = false;
            }
        }
    }

    for (int i = 2; i < m_max_n; i++)
    {
        if (true == m_primeSieve[i])
        {
            m_primesArray[m_numOfPrimes++] = i;
        }
    }
}

string PE0070::getDigitsString(int number)
{
    string digits_string = to_string(number);

    sort(digits_string.begin(), digits_string.end());

    return digits_string;
}

int PE0070::getTotientPermutation()
{    
#ifdef UNIT_TEST
    clock_t start = clock();
#endif
    double min_n_div_phi = 10.0;
    double n_div_phi;
    int n, n0 = 0;
    int phi, phi0 = 0;

    int root = (int)sqrt((double)m_max_n);

    int k = 0;
    while (m_primesArray[k] <= root)
    {
        k++;
    }

    for (int i=k+100; m_primesArray[i] > root / 2; i--)
    {
        for (int j = i-1; m_primesArray[j] > root / 2; j--)
        {
            n = m_primesArray[i] * m_primesArray[j];

            if (n > m_max_n) continue;
            
            // phi = p1 * p2 * (1-/p1) * (1-1/p2)
            // phi = (p1-1) * (p2-1)
            phi = (m_primesArray[i] - 1) * (m_primesArray[j] - 1);

            // check whether φ(n) is a permutation of n 
            if (getDigitsString(phi) == getDigitsString(n))
            {
                n_div_phi = (n*1.0) / phi;

                if (n_div_phi < min_n_div_phi)
                {
                    min_n_div_phi = n_div_phi;
                    n0            = n;
                    phi0          = phi;
                }
            }
        }
    }
    
    cout << "For 1 < n < 10^7, when n = " << n0 << ", φ(n) = ";
    cout << phi0 << " is a " << endl;
    cout << "permutation of n and the ratio n / φ(n) produces a minimum." << endl;
 
#ifdef UNIT_TEST
    cout << "And the minimum ratio n / φ(n) is " << min_n_div_phi << "." << endl;

    clock_t finish = clock();
    double duration = (double)(finish - start) / CLOCKS_PER_SEC;
    cout << "C++ running time: " << duration << " seconds" << endl;
#endif

    return 0;
}

int main()
{
    PE0070 pe0070;

    pe0070.getTotientPermutation();

    return 0;
}

4. Python 代码实现

Python跟C++采用了一样的方法,用质数筛子得到质数列表,然后缩小范围搜索合适的p1和p2,最后判断找到n,确保φ(n)是n的一个排列,且n / φ(n)的值最小。

Python 代码

def createPrimeSieve(n):
    """ create prime sieve and return primes list """
    sieve = [True] * n
    sieve[0] = sieve[1] = False

    for i in range(2, int(n**0.5)+1):
        if True == sieve[i]:
            for j in range(i*i, n, i):
                sieve[j] = False

    return [ i for i in range(1, n) if True==sieve[i] ]
    
def getTotientPermutation():
    max_n, min_n_div_phi = 10**7, 10.0

    root, k = int(max_n**0.5), 0

    primes_list = createPrimeSieve(max_n)

    while primes_list[k] <= root:
        k += 1

    i = k + 100
    while primes_list[i] > root // 2:
        j = i - 1
        while primes_list[j] > root // 2:
            n = primes_list[i] * primes_list[j]

            if n > max_n :
                j -= 1
                continue

            # phi = p1 * p2 * (1-/p1) * (1-1/p2)
            # phi = (p1-1) * (p2-1)
            phi = (primes_list[i] - 1) * (primes_list[j] - 1)
    
            # check whether φ(n) is a permutation of n 
            if sorted(str(phi)) == sorted(str(n)):
                n_div_phi = n / phi

                if n_div_phi < min_n_div_phi:
                    min_n_div_phi, n0, phi0 = n_div_phi, n, phi
            j -= 1
        i -= 1
        
    print("For 1 < n < 10^7, when n = %d, φ(n) = %d is a" %(n0, phi0))
    print("permutation of n and the ratio n / φ(n) produces a minimum.")
    print("And the minimum ratio n / φ(n) is %f." % min_n_div_phi)

def main():
    getTotientPermutation()
    
if  __name__ == '__main__':
    main()    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值