算法 之 3n+1问题
Posted 叶碎夜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法 之 3n+1问题相关的知识,希望对你有一定的参考价值。
卡拉兹(Callatz)猜想:
对任何一个自然数n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把(3n+1)砍掉一半。这样一直反复砍下去,最后一定在某一步得到n=1。卡拉兹在1950年的世界数学家大会上公布了这个猜想,传说当时耶鲁大学师生齐动员,拼命想证明这个貌似很傻很天真的命题,结果闹得学生们无心学业,一心只证(3n+1),以至于有人说这是一个阴谋,卡拉兹是在蓄意延缓美国数学界教学与科研的进展……
猜想内容: 对于任意大于1的自然数n,若n为奇数,则将n变为3n+1,否则变为n的一半。经过若干次这样的变换,一定会使n变为1。例如3->10->5->16->8->2->1。
输入n,输出变换的次数。n≤10^9。
样例输入:3
样例输出:7 (有的博客输出为5是考虑的变化次数 这里是把每一次出现的都算作一次)
#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)/2;
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; }
使用 long long版本的亦可以算出正确结果
#include<stdio.h> int main() { int n2; int count=0; scanf("%d", &n2); long long n = n2; while(n > 1) { if(n%2 != 0) n = (n * 3 + 1)/2; else n = n / 2; count++; } printf("%d\\n", count); return 0; }
参考资料:《算法竞赛入门经典》——刘汝佳
经验:
2、while(scanf("%d%d",&begin,&end)!=EOF);
最后一些疑惑(望看到的高手能解答一二,吾将不胜感激):
1、这里第二个程序是我依照作者的提示写的,自认应该正确吧:-),但是还是有一些疑惑,比如我们考虑了第一次n的输入值,但是循环
中的第二次,第三次...呢?我们如何说明以后循环的值不会发生溢出呢?
比如开始时n的值为7,那么一次循环后其值为11,而11>7。也就是说二次循环的值大于7。
2、再者,我们如何知道经过若干次的变换后一定会得到1呢,也就是说我们如何知道函数收敛呢?嗯...这貌似是一个数学问题啊。
关于这个问题的答案我是在数学科普神犇顾森的博客中找到的,其博客地址在这里Matrix67(顺便推荐,很好的博客呢),原文见这里千万别学数学:最折磨人的数学未解之谜(一),下面把与该问题相关的文字摘录在这里:
数学之美不但体现在漂亮的结论和精妙的证明上,那些尚未解决的数学问题也有让人神魂颠倒的魅力。和 Goldbach 猜想、 Riemann 假设不同,有些悬而未解的问题趣味性很强,“数学性”非常弱,乍看上去并没有触及深刻的数学理论
,似乎是一道可以被瞬间秒杀的数学趣题,让数学爱好者们“不找到一个巧解就不爽”;但令人称奇的是,它们的困难程度却不亚于那些著名的数学猜想,这或许比各个领域中艰深的数学难题更折磨人吧。 作为一本数学趣题集, Mathematical Puzzles 一书中竟把仍未解决的数学趣题单独列为一章,可见这些问题有多么令人着迷。我从这一章里挑选了一些问题,在这里和大家分享一下。这本书是 04 年出版的,
书里提到的一些“最新进展”其实已经不是最新的了;不过我也没有仔细考察每个问题当前的进展,因此本文的信息并不保证是 100% 准确的,在此向读者们表示歉意。 3x + 1 问题
从任意一个正整数开始,重复对其进行下面的操作:如果这个数是偶数,把它除以 2 ;如果这个数是奇数,则把它扩大到原来的 3 倍后再加 1 。序列是否最终总会变成 4, 2, 1, 4, 2, 1, … 的循环?这个问题可以说是一个
“坑”——乍看之下,问题非常简单,突破口很多,于是数学家们纷纷往里面跳;殊不知进去容易出去难,不少数学家到死都没把这个问题搞出来。已经中招的数学家不计其数,这可以从 3x + 1 问题的各种别名看出来: 3x + 1 问题又叫
Collatz 猜想、 Syracuse 问题、 Kakutani 问题、 Hasse 算法、 Ulam 问题等等。后来,由于命名争议太大,干脆让谁都不沾光,直接叫做 3x + 1 问题算了。 3x + 1 问题不是一般的困难。这里举一个例子来说明数列收敛有多么没规律。从 26 开始算起, 10 步就掉入了“421 陷阱”:26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1, … 但是,从 27 开始算起,数字会一路飙升到几千多,你很可能会一度认为它脱离了“421 陷阱”;但是,经过上百步运算后,它还是跌了回来: 27, 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186,
593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822,
911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40,
20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1, …
额外,再说一点:如上所述,既然数列的收敛如此没有规律,那么上面我们的程序可能是错误的(包括优化过后的那个)。举一个例子,我们用 long long int 做一个测试:
#include<math.h> int main(void) { long long int n; int count=0; // 统计计算次数 scanf("%I64d", &n); // long long int在windows下一定要用%I64d读入数据,否则会出问题(比如数据截断什么的) long long max = 0; // 记录计算过程中出现的最大值 while(n > 1) { if(n%2 != 0) { //n = floor(1.5*n + 1); n = (n * 3 + 1)/2; } else { n = n / 2; } count++; printf("%I64d\\n", n); // 输出也使用%I64d if(n > max) max = n; } printf("%d\\n%I64d\\n", count, max); return 0; }
我们输入704511,最后那个printf函数的输出结果:242,56991483520。可见其中间值有5百亿这么大,而用 n = floor(1.5*n + 1) 语句代替 n = n * 3 + 1;输出的中间值为28495741760,也有2百亿那么大,int类型存不下这么大的数据,所以之前的两个程序在测试强度不够的情况下表面上看是正确,实则是错误的。
最后,写的这里,应该可以告一段落了。
转自 :http://www.cnblogs.com/xpjiang/p/4129340.html
以上是关于算法 之 3n+1问题的主要内容,如果未能解决你的问题,请参考以下文章
3038 3n+1问题——http://codevs.cn/problem/3038/