码迷,mamicode.com
首页 > 其他好文 > 详细

3n+1问题

时间:2014-11-28 22:38:26      阅读:217      评论:0      收藏:0      [点我收藏+]

标签:style   blog   io   使用   sp   on   div   问题   log   

猜想: 对于任意大于1的自然数n,若n为奇数,则将n变为3n+1,否则变为n的一半。
    经过若干次这样的变换,一定会使n变为1。例如3->10->5->16->8->2->1。
    输入n,输出变换的次数。n≤10^9。
     样例输入:3
     样例输出:7

不假思索的写出下面的代码:

#include<stdio.h>
int main(void)
{
	int n;
	int count=0;
	
	scanf("%d", &n);
	
	while(n > 1)
	{	
		if(n%2 != 0)
			n = 3*n + 1;
		else
			n /= 2;
		count++;
	}
	
	printf("%d\n", count);

    return 0;
}

然而,程序正确吗?很不幸,如果输入987654321,答案为1,这显然是错误的。通过调试或者在循环体中用printf语句打印出n的值,看到n的值为负数,导致一次循环后程序退出。从这里可以获悉n的值溢出了,因为整型最大值为2^31-1 = 2147483647,大约为21亿,而由题意n的最大值为10亿(10^9),所以在n的值颇大且为奇数时乘以3是危险的,会导致溢出。

解决方案如下:

因为奇数*奇数=奇数,所以经过n=3*n+1的计算后,n的值必然是偶数,并且下次循环必然做运算n/=2,所以这里可以合并这两步,也就是n为奇数的情况下做运算n=floor(1.5*n+0.5),由于double值的误差问题,我们可以用n=floor(1.5*n+1)(floor函数接收double类型的参数,返回不大于给定参数的最大整形数,返回值类型为double),将取整后的double值赋给整形从而丢弃小数点。

修改后的程序如下:

#include<stdio.h>
#include<math.h>
int main(void)
{
	int n;
	int count=0;
	
	scanf("%d", &n);

	while(n > 1)
	{
		if(n%2 != 0)
        {
            n = floor(1.5*n + 1);
            count += 2;
        }
		else
		{
		    n = n / 2;
		    count++;
		}
	}

	printf("%d\n", count);

    return 0;
}

再次用987654321测试,得到结果180次。再者,如果对n的限制为整形int的范围,该程序就不能完成要求了,那么我们可以使用long long int。

 

参考资料:《算法竞赛入门经典》——刘汝佳

 

最后一些疑惑(望看到的高手能解答一二,吾将不胜感激):

1、这里第二个程序是我依照作者的提示写的,自认应该正确吧:-),但是还是有一些疑惑,比如我们考虑了第一次n的输入值,但是循环

  中的第二次,第三次...呢?我们如何说明以后循环的值不会发生溢出呢?

  比如开始时n的值为7,那么一次循环后其值为11,而11>7。也就是说二次循环的值大于7。

2、再者,我们如何知道经过若干次的变换后一定会得到1呢,也就是说我们如何知道函数收敛呢?嗯...这貌似是一个数学问题啊。

3n+1问题

标签:style   blog   io   使用   sp   on   div   问题   log   

原文地址:http://www.cnblogs.com/xpjiang/p/4129340.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!