[problem12]欧拉

The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten terms would be:

1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...

Let us list the factors of the first seven triangle numbers:

 1: 1
 3: 1,3
 6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28

We can see that 28 is the first triangle number to have over five divisors.

What is the value of the first triangle number to have over five hundred divisors?

求解一个数的因子的个数,用暴力求解是最明显的做法,也是不可能算得出来的解法。这一题的作用也在于进一步揭示:算法的效率是要看场合的。在问题规模小的时候,再烂的算法都可以很快的执行并给出正确结果;但问题规模变大后,算法的优劣将体现的非常明显。

这一题,用暴力求解(用1-n的数字逐个被n除,统计被整除的次数,即因子的个数),根本算不出来。所以,这一题的关键就是:如何更快的求出一个数的因子的个数。解法:

a. 这里,关键是要认识并用到数学上关于因子分解的知识:一个数总是能唯一的表示为一组质数的乘积。任何因子都可以分解为质因子的乘积。一个数可以被任何一个质因子整除,必然可以被质因子的任意组合整除,即质因子的一个组合就是一个因子。可见因子的个数即质因子组合的个数。 (因子数的求解公式:将整数n分解为质因子相乘的形式,然后将每个质因子的幂分别加1之后连乘所得的结果就是n的因子的个数)详见代码注释。

View Code
#include <iostream>
#include 
<ctime>
#include 
<map>
using namespace std;

// get next triangle number in the sequence... 
// count its factor number. if > 500, get the number printed.

//int getDivisorNumber(int val) // 暴力求解。。无终而果。。。。根本算不出来的。
//{
//    int cnt = 2;
//    for(int i = 2; i <= val/2; i++)
//    {
//        if(val % i == 0)
//            cnt++;
//    }
//
//    return cnt;
//}

int getDivisorNumber(int val)
{    
    
// 因为45= 3^2×5^1,45可以被3^0 ×5^0,3^0×5^1,3^1×5^0,3^1×5^1,3^2×5^0,和3^2×5^1,或者 1,5,3,9,15,和 45整除。
    
// 所以:算出所有质因子及对应的指数次方。所有指数次方的乘积就是所有因子的个数(所有组合的个数)
    map<intint> factorMap;

    
int i = 2;
    
while(val > 1)
    {        
        
if(val % i == 0)
        {
            map
<intint>::iterator itr = factorMap.find(i);
            
if(factorMap.end() == itr)
            {
                factorMap[i] 
= 2;
            }
            
else
            {
                itr
->second++;
            }
            
            val 
/= i;
            i 
= 2;
        }
        
else
            i
++;
    }    

    map
<intint>::iterator itr = factorMap.begin();
    map
<intint>::iterator itrEnd = factorMap.end();
    
int cnt = 1;
    
while(itr != itrEnd)
    {
        cnt 
*= itr->second;
        itr
++;
    }

    
return cnt;
}

int _tmain(int argc, _TCHAR* argv[])
{
    clock_t startTime 
= clock();
    
int index = 10// start from the 10th number..
    int formerSum = 0;
    
bool bIsReady = false;
    
while(true)
    {
        
int sum = 0;
        
if(!bIsReady)
        {
            
int i = 1;
            
while(i<=index)
            {
                sum 
+= i;
                i
++;
            }
            formerSum 
= sum;
            bIsReady 
= true;
        }
        
else
        {
            sum 
= formerSum + index;
            formerSum 
= sum;
        }
        
        
// get its divisor number.
        int cnt = 0;
        cnt 
= getDivisorNumber(sum);
        
if(cnt > 500)
        {
            cout 
<< sum << endl;
            
break;
        }

        index
++;
    }

    clock_t endTime 
= clock();
    
double spentTime = difftime(endTime, startTime);
    cout 
<< "spentTime: " << spentTime << endl;

    
return 0;
}

b. 直接计算因子个数。不过只算到sqrt(n),然后把因子数*2作为总因子数。thread里很多人都是用这个方法。更快。。不过依据呢?没想出。

 

学到的:

0. 数学的知识对于思考算法,提升算法效率,非常有帮助。计算机可以帮你运算,但随着问题规模的变大,可能需要算很久。这时候,就必须人为的提升算法来提高速度了。

1. 复习数学:
三角数: 第n个数是1至n的自然数的和。Sn = n(n+1)/2。两个三角形的和的1/2。 一般我们用Sn来表示1+2+3+…+n的值。现在要知道Sn的数目,我们可以设想有另外一个Sn(这里用白圆球来表示),把它倒放,并和原来的Sn靠拢拼合起来;我们就得到一个菱形(图二,这里n是等于4的情形),总共有n行,每一行有n+1个圆球,所以全部有n(n+1)个圆球。这是两个Sn,因此一个Sn应该是n(n+1)÷2。
参见百度百科: http://baike.baidu.com/view/467293.htm
级数(等差数列,等比数列)
--等差数列:公差是d。第n项公式是 An = A1 + (n-1)d; 前n项和公式是: A1 + A1 + d + A1 + 2d + ... + A1 + (n-1)d = nA1 + d* (n-1)n/2
--等比数列:公比是q。第n项公式是 An = A1* q^(n-1); 前n项和公式是: A1 + A1*q + A1*q^2 +.. + A1*q^(n-1) = ()(1-q)/(1-q) = A1*(1-q^n)/(1-q)
2. 判断质数和求解质因子,因子

判断质数:首先,除了2,其他都是奇数;limit是被判断数的平方根。

求解质因子:任何数都可以唯一的表示为一组质数的乘积。任何因子肯定可以分解为质因子的乘积。

求因子:一般情况下,一般求解法足够。特殊情况下,考虑到质因子和因子之间的关系。


 

posted @ 2011-03-09 18:08  能巴  阅读(225)  评论(0)    收藏  举报