几点:
1. 在问题规模大的时候,一定要检查选择的数据类型是否够用,会不会overflow。这里的case就是一个很好的例子。我的算法几分钟就写好了,也蛮正确的,除了对于start num i及chain中的number n的数据类型选择了int,这就导致在n被3n+1反复搞大后,超出了int的范围,变成了负数,也就进入了导致算不出结果来的死循环里(因为永远也算不到1了)。所以,切记:问题规模大的时候,一定仔细考虑清楚数据类型的选择。
2. unsigned int的数据参与大规模运算要比long long快3倍以上。这里指的是仅仅替换i和n的数据类型。
3. 在看到更多规律的基础上,我加了个map来试图减少一些运算,结果:在题目要求的1million的问题规模下,配合unsigned int的正确类型,算不出;在调整到1000以验证算法正确性的实验中,还是发现:使用map后,由于要多做很多排序和查找的工作,虽然节省了一些直接的chain中number的计算,但最终时间统计显示还是比不用map要慢。所以,在你确定要使用map时,务必考虑清楚你增加的运算和减少的运算之间的关系,不见得能改善多少算法性能,而且还容易产生副作用。算法时间复杂度的本质在于:其需要执行的基本运算的次数的多少。
4. oh,my!我终于意识到:位操作要比乘除运算高效的多!在算法中,用n&1替换n%2来判断奇数偶数,时间花费从3秒多减少到了1秒左右!这个例子,我印象很深刻,非常有意义的实验。感谢problem 14 answer thread里的兄弟的启发。
下面附上代码:
#include < map >
#include < ctime >
using namespace std;
int _tmain( int argc, _TCHAR * argv[])
{
clock_t startT = clock();
int maxNum = 10 ; // initialized with starting number 13's chain length.
unsigned int theStartNum = 13 ;
unsigned int n = 0 ; // Be exact to choose its data type.
for (unsigned int i = 14 ; i < 1000000 ; i ++ )
{
n = i;
int tempNum = 0 ;
while ( true ) // to calculate tempNum.
{
// if(n%2) // odd. Using this, whole algorithm cost >3s.
if (n & 1 ) // using this, cost 1s or so.
{
n = 3 * n + 1 ;
}
else // even.
{
if (n == 16 ) // minor optimization.
{
tempNum += 4 ;
break ;
}
else
n /= 2 ;
}
tempNum ++ ;
if (n == 1 )
break ;
}
if (maxNum < tempNum)
{
maxNum = tempNum;
theStartNum = i;
}
}
clock_t endT = clock();
double spentT = difftime(endT, startT);
cout << " theNum is: " << theStartNum << endl;
cout << " theChain: " << maxNum << endl;
cout << " spentT: " << spentT << endl;
return 0 ;
}