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

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

Problem 80 : Square root digital expansion

It is well known that if the square root of a natural number is not an integer, then it is irrational. The decimal expansion of such square roots is infinite without any repeating pattern at all.

The square root of two is 1.41421356237309504880…, and the digital sum of the first one hundred decimal digits is 475.

For the first one hundred natural numbers, find the total of the digital sums of the first one hundred decimal digits for all the irrational square roots.

1. 欧拉项目第80道题 : 平方根的数字展开

众所周知,如果一个自然数的平方根不是整数的话,那么它就是无理数。这种平方根的十进制展开是无限的,根本没有重复的模式。

2的平方根是1.41421356237309504880…,前一百位小数的数字和是475。

对于前100个自然数,求出所有平方根是无理数的前100位小数的数字和的总和。

2. 求解分析

这道题首先要知道如何计算有很多位小数的平方根,当然sqrt()不支持100位,在网上找到了求解自然数n的平方根一个方法Frazer Jarvis: Square roots by subtraction,算法如下:

Algorithm to compute the square root of an integer n

Initial step
Let a = 5n (this multiplication by 5 is the only time when an operation other than addition and subtraction is involved!), and put b = 5.

Repeated steps
(R1) If a ≥ b, replace a with a − b, and add 10 to b.
(R2) If a < b, add two zeroes to the end of a, and add a zero to b just before the final digit (which will always be ‘5’).

Conclusion
Then the digits of b approach more and more closely the digits of the square root of n

另外,无理数的平方根前一百位小数的数字和是包含整数位和小数位的所有数字之和,这个可以通过计算2的平方根验证。

第三,求1到100的自然数的总和,只需要把各个数的和累加就可以。

3. C++ 代码实现

C++不直接支持大整数,所以用C++求解,必须考虑数据结构,一种方法就是使用vector保存平方根的每一位数字,另一种方法就是使用int 数组来保存平方根的每一位数字。

C++代码 I (使用STL vector)

#include <iostream>
#include <vector>
#include <iterator>
#include <cmath>
#include <ctime>
#include <cassert>

using namespace std;

// #define UNIT_TEST

class PE0080
{
private:
    static const int max_N = 100;

    vector<int> adjust_vector_value(vector<int>& v);
    bool compare_ab_values(vector<int>& a, vector<int>& b);
    vector<int> calculate_b_value_R1(vector<int>& b);
    vector<int> calculate_b_value_R2(vector<int>& b);
    vector<int> calculate_a_value_R2(vector<int>& a);
    vector<int> calculate_a_value_R1(vector<int>& a, vector<int>& b);

    void print_ab_values(vector<int>& a, vector<int>& b);

public:
    int getSquareRootsBySubtraction(int n);

    int getSumOfIrrationalSquareRootsDecimalDigits(int n);
    int getTotalOfSumsOfDecimalDigits();
};

vector<int> PE0080::adjust_vector_value(vector<int>& v)
{
    for (unsigned int i = 0; i < v.size(); i++)
    {
        if (v[i] >= 10 && i + 1 < v.size())
        {
            v[i + 1] += v[i] / 10;
            v[i] %= 10;
        }
        else if (v[i] >= 10 && i + 1 == v.size())
        {
            v.push_back(v[i] / 10);
            v[i] %= 10;
        }
        else if (v[i] < 0 && i + 1 < v.size())
        {
            v[i] += 10;
            v[i + 1] -= 1;
        }
    }

    vector<int>::reverse_iterator iter = v.rbegin();
    while (*iter == 0)
    {
        v.pop_back();  // !!! remove unused element 0 in vector
        iter = v.rbegin();
    }
    return v;
}

bool PE0080::compare_ab_values(vector<int>& a, vector<int>& b)
{
    int a_size = a.size();
    int b_size = b.size();

    if (a_size < b_size)
    {
        return false;
    }
    else if (a_size == b_size)
    {
        for (int i = a_size - 1; i >= 0; i--)
        {
            if (a[i] < b[i])
            {
                return false;
            }
        }
    }

    return true;
}

vector<int> PE0080::calculate_b_value_R1(vector<int>& b)
{
    b[0] += 10;
    b = adjust_vector_value(b);
    return b;
}

vector<int> PE0080::calculate_b_value_R2(vector<int>& b)
{
    vector<int> tmp;
    tmp.push_back(5);
    tmp.push_back(0);
    for (unsigned int i = 1; i < b.size(); i++)
    {
        tmp.push_back(b[i]);
    }
    return tmp;
}

vector<int> PE0080::calculate_a_value_R2(vector<int>& a)
{
    vector<int> tmp;
    tmp.push_back(0);
    tmp.push_back(0);
    for (unsigned int i = 0; i < a.size(); i++)
    {
        tmp.push_back(a[i]);
    }
    return tmp;
}

vector<int> PE0080::calculate_a_value_R1(vector<int>& a, vector<int>& b)
{
    for (unsigned int i = 0; i < b.size(); i++)
    {
        a[i] -= b[i];
    }
    a = adjust_vector_value(a);
    return a;
}

void PE0080::print_ab_values(vector<int>& a, vector<int>& b)
{
    cout << "a[] = ";
    copy(a.rbegin(), a.rbegin() + 100, ostream_iterator<int>(cout));
    cout << endl << "b[] = ";
    copy(b.rbegin(), b.rbegin() + 100, ostream_iterator<int>(cout));
    cout << endl;
}

// Frazer Jarvis: Square roots by subtraction
int PE0080::getSumOfIrrationalSquareRootsDecimalDigits(int n)
{
    vector<int> a;
    vector<int> b;

    a.push_back(5 * n);
    a = adjust_vector_value(a);

    b.push_back(5);

    while (b.size() < max_N + 5)
    {
        if (true == compare_ab_values(a, b))
        {
            a = calculate_a_value_R1(a, b); // a -= b;  R1
            b = calculate_b_value_R1(b);    // b += 10; R1
        }
        else  // (a value < b value )
        {
            a = calculate_a_value_R2(a);    // a = a*10*10;       R2
            b = calculate_b_value_R2(b);    // b = (b-b%10)*10+5; R2
        }
    }

    int sum = 0;
    vector<int>::reverse_iterator iter = b.rbegin();
    for (int i = 0; i < 100; i++)
    {
        sum += *iter;
        iter++;
    }

#ifdef UNIT_TEST
    cout << "the sums of first one hundred decimal digits of square root of ";
    cout << n << " : " << sum << endl;
    print_ab_values(a, b);
#endif

    return sum;
}

// Frazer Jarvis: Square roots by subtraction
int PE0080::getSquareRootsBySubtraction(int n)
{
    int a = 5 * n;
    int b = 5;
    int repeatedSteps = 0;

    while (repeatedSteps < 20)
    {
        if (a >= b)
        {
            a -= b;
            b += 10;
        }
        else // (a < b)
        {
            a = a * 10 * 10;
            b = (b - b % 10) * 10 + 5;
        }
        cout << "(" << a << "," << b << ")" << endl;
        repeatedSteps++;
    }
    return b;
}

int PE0080::getTotalOfSumsOfDecimalDigits()
{
    int total = 0;

    for (int n = 2; n <= max_N; n++)
    {
        int root = (int)sqrt(double(n));;
        if (root*root == n)
        {
            continue;
        }
        else
        {
            total += getSumOfIrrationalSquareRootsDecimalDigits(n);
        }
    }
    return total;
}

int main()
{
    clock_t start = clock();

    PE0080 pe0080;

#ifdef UNIT_TEST
    pe0080.getSquareRootsBySubtraction(3);
#endif

    assert(475 == pe0080.getSumOfIrrationalSquareRootsDecimalDigits(2));
    
    int total = pe0080.getTotalOfSumsOfDecimalDigits();

    cout << "The total of the sums of the first one hundred decimal" << endl;
    cout << "digits for all the irrational square roots is " << total << "." << endl;

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

    return 0;
}

C++代码II (直接使用整型数组)

#include <iostream>
#include <cstring>
#include <ctime>
#include <cassert>

using namespace std;

// #define UNIT_TEST

class PE0080
{
private:

    static const int max_n = 100;
    static const int m_max_size = max_n + 5;
    int m_a[m_max_size];
    int m_b[m_max_size];
    int m_a_actualSize;
    int m_b_actualSize;

    void initialize_ab();
    void adjust_array_value(int v[], int& size);
    bool compare_ab_values();
    void calculate_b_value_R1();
    void calculate_b_value_R2();
    void calculate_a_value_R2();
    void calculate_a_value_R1();

    void print_ab_values();

public:
    int getSquareRootsBySubtraction(int n);

    int getSumOfIrrationalSquareRootsDecimalDigits(int n);
    int getTotalOfSumsOfDecimalDigits();
};

void PE0080::initialize_ab()
{
    memset(m_a, 0, sizeof(m_a));
    memset(m_b, 0, sizeof(m_b));
    m_a_actualSize = 0;
    m_b_actualSize = 0;
}

void PE0080::adjust_array_value(int v[], int& size)
{
    for (int i = 0; i < size; i++)
    {
        if (v[i] >= 10)
        {
            v[i + 1] += v[i] / 10;
            v[i] %= 10;
        }
        else if (v[i] < 0)
        {
            v[i] += 10;
            v[i + 1] -= 1;
        }
    }

    for (int i = m_max_size - 1; v[i] == 0; i--)
    {
        size = i;
    }
}

bool PE0080::compare_ab_values()
{
    if (m_a_actualSize < m_b_actualSize)
    {
        return false;
    }
    else if (m_a_actualSize == m_b_actualSize)
    {
        for (int i = m_a_actualSize - 1; i >= 0; i--)
        {
            if (m_a[i] < m_b[i])
            {
                return false;
            }
        }
    }

    return true;
}

void PE0080::calculate_a_value_R1()  // a -= b;  R1
{
    for (int i = 0; i < m_b_actualSize; i++)
    {
        m_a[i] -= m_b[i];
    }
    adjust_array_value(m_a, m_a_actualSize);
}

void PE0080::calculate_b_value_R1()   // b += 10; R1
{
    m_b[0] += 10;
    adjust_array_value(m_b, m_b_actualSize);
}

void PE0080::calculate_b_value_R2()  // b = (b-b%10)*10+5; R2
{
    for (int i = m_b_actualSize - 1; i >= 0; i--)
    {
        m_b[i + 1] = m_b[i];
    }
    m_b[0] = 5;
    m_b[1] = 0;
    m_b_actualSize = m_b_actualSize + 2 - 1;
}

void PE0080::calculate_a_value_R2()  // a = a*100;         R2
{
    for (int i = m_a_actualSize - 1; i >= 0; i--)
    {
        m_a[i + 2] = m_a[i];
    }
    m_a[0] = 0;
    m_a[1] = 0;
    m_a_actualSize += 2;
}

int PE0080::getSumOfIrrationalSquareRootsDecimalDigits(int n)
{
    initialize_ab();

    m_a[m_a_actualSize++] = 5 * n;
    adjust_array_value(m_a, m_a_actualSize);

    m_b[m_b_actualSize++] = 5;

    while (m_b_actualSize < m_max_size)
    {
        if (true == compare_ab_values())
        {
            calculate_a_value_R1();    // a -= b;  R1
            calculate_b_value_R1();    // b += 10; R1
        }
        else // (a value < b value)
        {
            calculate_a_value_R2();    // a = a*100;         R2
            calculate_b_value_R2();    // b = (b-b%10)*10+5; R2
        }
    }

    int sum = 0;
    for (int i = m_b_actualSize - 1; i >= m_b_actualSize - max_n; i--) //100:from 104 to 5
    {
        sum += m_b[i];
    }

#ifdef UNIT_TEST
    cout << "the sums of first one hundred decimal digits of square root of ";
    cout << n << " : " << sum << endl;
    print_ab_values();
#endif

    return sum;
}

void PE0080::print_ab_values()
{
    cout << "m_a[] = ";
    for (int i = m_a_actualSize - 1; i >= 0; i--)
    {
        cout << m_a[i];
    }
    cout << "(" << m_a_actualSize << ")" << endl;

    cout << "m_b[] = ";
    for (int i = m_b_actualSize - 1; i >= 0; i--)
    {
        cout << m_b[i];
    }
    cout << "(" << m_b_actualSize << ")" << endl;
}

int PE0080::getSquareRootsBySubtraction(int n)
{
    int a = 5 * n;
    int b = 5;
    int repeatedSteps = 0;

    while (repeatedSteps < 20)
    {
        if (a >= b)
        {
            a -= b;
            b += 10;
        }
        else // (a < b)
        {
            a = a * 10 * 10;
            b = (b - b % 10) * 10 + 5;
        }
        cout << "(" << a << "," << b << ")" << endl;
        repeatedSteps++;
    }
    return b;
}

int PE0080::getTotalOfSumsOfDecimalDigits()
{
    int total = 0;

    for (int n = 2; n <= max_n; n++)
    {
        int root = (int)sqrt(double(n));;
        if (root*root == n)
        {
            continue;
        }
        else
        {
            total += getSumOfIrrationalSquareRootsDecimalDigits(n);
        }
    }
    return total;
}
    
int main()
{
    clock_t start = clock();

    PE0080 pe0080;

#ifdef UNIT_TEST
    pe0080.getSquareRootsBySubtraction(2);
#endif

    assert(475 == pe0080.getSumOfIrrationalSquareRootsDecimalDigits(2));

    int total = pe0080.getTotalOfSumsOfDecimalDigits();

    cout << "The total of the sums of the first one hundred decimal" << endl;
    cout << "digits for all the irrational square roots is " << total << "." << endl;

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

    return 0;
}

4. Python 代码实现

Python直接支持大整数,所以简单很多,但是Python和C++采用的方法是一样的。

Python代码

def getNumOfDigits(n):
    return len(str(n))

def getSumOfFirst100Digits(n):
    return sum(list(map(int, str(n)))[:100])

def getSumOfIrrationalSquareRootsDecimalDigits(n):
    """ Frazer Jarvis: Square roots by subtraction """
    a, b = 5*n, 5
    while getNumOfDigits(b) < 105:
        if a >= b:
            a, b = a-b, b+10
        else: # (a < b)
            a, b = a*100, (b-b%10)*10+5
    return getSumOfFirst100Digits(b)

def getTotalOfSumsOfDecimalDigits():
    total_list = [ getSumOfIrrationalSquareRootsDecimalDigits(n) \
        for n in range(2, 101) if (n**0.5) % 1 != 0.0 ]
    return sum(total_list)
    
def main():
    assert 475 == getSumOfIrrationalSquareRootsDecimalDigits(2)
    
    print("The total of the sums of the first one hundred decimal")
    print("digits for all the irrational square roots is %d." \
        % getTotalOfSumsOfDecimalDigits())

if  __name__ == '__main__':
    main()

Python代码II (更紧凑的代码)

def getSumOfIrrationalSquareRootsDecimalDigits(n):
    """ Frazer Jarvis: Square roots by subtraction """
    max_size, a, b = 105, 5*n, 5
    while len(str(b)) < max_size:
        if a >= b:
            a, b = a-b, b+10              # R1
        else: # (a < b)
            a, b = a*100, (b-b%10)*10+5   # R2
    return sum(list(map(int, str(b)))[:100])

def getTotalOfSumsOfDecimalDigits():
    total_list = [ getSumOfIrrationalSquareRootsDecimalDigits(n) \
        for n in range(2, 101) if (n**0.5) % 1 != 0.0 ]
    return sum(total_list)
    
def main():
    assert 475 == getSumOfIrrationalSquareRootsDecimalDigits(2)   
    print("The total of the sums of the first one hundred decimal")
    print("digits for all the irrational square roots is %d." \
        % getTotalOfSumsOfDecimalDigits())

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值