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

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

                                                                  周银辉 

 

问题描述

The following iterative sequence is defined for the set of positive integers:

n --> n/2 (n is even)
n --> 3n + 1 (n is odd)

Using the rule above and starting with 13, we generate the following sequence:

13 --> 40 --> 20 --> 10 --> 5 --> 16 --> 8 --> 4 --> 2 --> 1

It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.

Which starting number, under one million, produces the longest chain?

NOTE: Once the chain starts the terms are allowed to go above one million. 

 

问题分析:

在小于1000000的数中,哪个数作为首项并按如下公式能得到最长的数列:

n --> n/2 (n is even)

n --> 3n + 1 (n is odd)

根据n判断其构成的数列长度,如果n比较小的话,则简单地写个递归函数就可以了。

但对于一百万这样的大数据,递归一百万次效率很低下,并且可能堆栈溢出。

所以,为了减少递归次数,用一个缓冲区来保存中间结果,比如我们已经计算过13的话,在计算26时,其除以2等于13,

然后我们可以直接从缓冲区中取13上次的计算结果。

另外在缓冲区中取值时有一个技巧:我们应该在递归前判断缓冲区中是否有值,如果有则不递归,而不应该在递归后才判断缓冲区是否有值(如果有则立即返回)。前者递归函数要调用132434424次,时间上约30秒,后者调用3168610次,时间上1秒多。

设置的缓冲区在递归算法中经常用到,就如许多动态规划题目一样 

 

 

std::hash_map<long, long> buffer;
long  get_chain_length(long n)
{
long length=0, next;
std::hash_map<long, long>::iterator found;
if(n==1)
{
length = 1;
}
else 
{
next = n%2==0? n>>1 : n*3+1;
found = buffer.find(n);
if(found != buffer.end())
{
length = found->second; 
}
else
{
length = 1+ get_chain_length(next);
buffer[n] = length;
}
}
return length;
}

 

  

posted @ 2011-02-20 21:02  周银辉  阅读(2065)  评论(6编辑  收藏