[Project Euler] 来做欧拉项目练习题吧: 题目012

                                   [Project Euler] 来做欧拉项目练习题吧: 题目012

                                                       周银辉 

 

 

问题描述: 

The sequence of triangle numbers is generated by adding the natural numbers. So the 7thtriangle 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?

 

 

问题分析:

 

题目要求找出第一个拥有超过500个约数的三角数。

 

首先,求第i个三角数很容易,其是1~i之和

long get_trangle_number(int i)
{
return ((long)(1+i)*i)/2;
}

难在如何求一个数n的约数,观察 28: 1,2,4,7,14,28 除了1和28(n本身)外,其它约数中,关键约数是2和7,剩下的则是关键约数的倍数

所以,先求出关键的这些约数,然后再求约数的倍数,而关键的约数也就是质因数(prime factor),求一个数的质因数参考003题

get_divisors_count这个函数是上述算法的粗糙版本,

int get_divisors_count(int number)
{
if(number<1)
{
return 1;
}

int count     = 0;
int factor    = 2;
int n         = number;
int buffer_sz = number+1;
int i;

int array[buffer_sz];
for(i=0; i<buffer_sz; i++)
{
array[i]=0;
}
array[1]      = 1;
array[number] = 1;

while(n>1)
{
if(n%factor==0)
{
//printf("prime factor found: %d\n", factor);

array[factor]=1;
i=2;
while(number%(i*factor)==0)
{
array[i*factor]=1;
i++;
}
while(n%factor==0)
{
n/=factor;
}
}

factor++;
}

        //printf("divisors of %d: ", number);
for(i=0; i<buffer_sz; i++)
{
if(array[i]==1)
{
//printf("%d ", i);
count++;
}
}
return count;

} 

其中有个问题是如何记录已经找到的约数,由于有可能存在重复查找(比如14是2的倍数也是7的倍数,所以重复查找了)则不能简单地累计,

要记录这些数,最简单的方法是用hashtable之类的,但标准C里面没有内置,所以我用了一个数组,数组下标表示约数,值为1则表示找到了。

数组对于比较小的数没问题,但数太大的话,一是遍历是效率低下,二是如果在栈上分配的容易导致内存错误。 

 

 

上述方法是可以改进的,然后得到了get_divisors_count2这个函数,理论基础来自于这里:http://mathforum.org/library/drmath/view/55843.html 

于是我们得到更高效的函数:

int get_divisors_count2(long number)
{
if(number<1)
{
return 1;
}
int count     = 1;
int factor    = 2;
long n        = number;
int i, t;
while(n>1)
{
if(n%factor==0)
{
//printf("prime factor found: %d\n", factor);
t = 1;
while(n%factor==0)
{
t++;
n/=factor;
}
count *= t;
}
factor++;
}
return count;
}
 

 

注:当完成题目后,对于某些题,官方网站会给出参考答案,在我的博客里不会将官方答案贴出来,仅仅会写下我自己当时的思路,除非两者不谋而合。另外,如果你有更好的思路,请留言告诉我,我非常乐意参与到讨论中来。 

 

posted @ 2011-02-16 07:21 周银辉 阅读(1620) 评论(9) 编辑 收藏

 回复 引用 查看   
#1楼 2011-02-16 10:35 mirguest      
参照这个方法,我也写了一个,不过还是python的。
# -*- coding:utf-8 -*-
import psyco 
psyco.full() 
def gentriangle():
    i=1
    while True:
        yield i*(i+1)/2
        i+=1

def isprime(n):
    for t in xrange(2,int(n**0.5)+1):
        if n%t==0:
            return False
    return True

def genprime():
    x=2
    while True:
        if isprime(x):
            yield x
        x+=1

def length(n,x):
    """
    >>> l(45,3)
    2
    """
    i=0
    while True:
        if n%x**(i+1)==0:
            i+=1
        else:
            return i

def divisor(n,primes=[2],f=genprime(),p=False):
    if p==True:print len(primes)
    mydict={}
    while (int(n**0.5)+1)>primes[-1]:
        p=f.next()
        if p not in primes: 
            primes.append(p)
    # every primes' length
    for i in primes:
        if length(n,i)!=0:
            mydict[i]=length(n,i)
    return mydict

def factorlength(n):
    if n==1:return 1
    mydict=divisor(n)
    r=1
    for i in mydict.values():
        r*=(i+1)
    return r

if __name__=='__main__':
    #速度最快
    from time import clock
    t1=clock()
    for i in gentriangle():
        if factorlength(i)>=500:
            print i
            break
    t2=clock()
    print t2-t1
    print divisor(i,p=True)


的确用了这个方法后,在要求的数字越大时,其速度反而更快。
不过写的有点烦琐。我看有个人只用了50ms。
我算法真的不行啊。

 回复 引用 查看   
#2楼[楼主] 2011-02-16 10:47 周银辉      
@mirguest
呵呵呵...

 回复 引用 查看   
#3楼 2011-02-16 14:56 mirguest      
后面的题真够难的。做不下去啊。
 回复 引用 查看   
#4楼[楼主] 2011-02-16 15:13 周银辉      
@mirguest
做到多少题了?

 回复 引用 查看   
#5楼 2011-02-17 11:02 mirguest      
@周银辉
今天刚到31题,还没开做。明天就要回学校,估计以后有空再玩玩。

 回复 引用 查看   
#6楼 2011-02-21 15:22 fluxay      
求了质因数之后很容易求约数个数.
比如28=2^2*7
则28的约数个数为(2+1)*(1+1)
72=3^2*2^3
则72的约数个数为(2+1)*(3+1)

用乘法原理想想就知道这是为什么了.
好像这个东西还有个专门的名字,叫算数基本定理还是什么的.

 回复 引用 查看   
#7楼 2011-03-01 16:16 玫瑰季节      
为什么要把i*(i+1)/2 给直接算出来呢? 有些费解
 回复 引用 查看   
#8楼[楼主] 2011-03-04 14:25 周银辉      
@玫瑰季节
你的意思是没必要求?

 回复 引用 查看   
#9楼 2011-03-04 15:46 玫瑰季节      
完全没有必要, 用a*b来代替i * (i+1) / 2

如果, i & 1 = 0 则a = i / 2, b = i + 1;

否则 a = i, b = (i + 1) / 2

假设(I) a = a1^(n1) * a2^(n2) * a3^(n3) * a4^(n4) .... (ai 为质数, ni >= 1)

同样 (II) b = b1^(m1) * b2^(m2) * b3^(m3) * b4^(m4) .... (bi为质数, mi>=1)

则 a*b一定可以直接利用(I),(II)的结果进行类似的描述, 剩下的, 就不需要过多解释了吧

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1955691 8ks1KYXbDKk=