算法 之 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=;
scanf("%d", &n);
while(n > )
{
if(n% != )
{
n = floor(1.5*n + );
count += ;
}
else
{
n = n / ;
count++;
}
}
printf("%d\n", count);
return ;
}
使用 long long版本的亦可以算出正确结果
#include<stdio.h>
int main()
{
int n2;
int count=; scanf("%d", &n2);
long long n = n2; while(n > )
{
if(n% != )
n = (n * + 1)/2;
else
n = n / ;
count++;
}
printf("%d\n", count);
return ;
}
参考资料:《算法竞赛入门经典》——刘汝佳
经验:
2、while(scanf("%d%d",&begin,&end)!=EOF);
最后一些疑惑(望看到的高手能解答一二,吾将不胜感激):
1、这里第二个程序是我依照作者的提示写的,自认应该正确吧:-),但是还是有一些疑惑,比如我们考虑了第一次n的输入值,但是循环
中的第二次,第三次...呢?我们如何说明以后循环的值不会发生溢出呢?
比如开始时n的值为7,那么一次循环后其值为11,而11>7。也就是说二次循环的值大于7。
2、再者,我们如何知道经过若干次的变换后一定会得到1呢,也就是说我们如何知道函数收敛呢?嗯...这貌似是一个数学问题啊。
关于这个问题的答案我是在数学科普神犇顾森的博客中找到的,其博客地址在这里Matrix67(顺便推荐,很好的博客呢),原文见这里千万别学数学:最折磨人的数学未解之谜(一),下面把与该问题相关的文字摘录在这里:
数学之美不但体现在漂亮的结论和精妙的证明上,那些尚未解决的数学问题也有让人神魂颠倒的魅力。和 Goldbach 猜想、 Riemann 假设不同,有些悬而未解的问题趣味性很强,“数学性”非常弱,乍看上去并没有触及深刻的数学理论
,似乎是一道可以被瞬间秒杀的数学趣题,让数学爱好者们“不找到一个巧解就不爽”;但令人称奇的是,它们的困难程度却不亚于那些著名的数学猜想,这或许比各个领域中艰深的数学难题更折磨人吧。
作为一本数学趣题集, Mathematical Puzzles 一书中竟把仍未解决的数学趣题单独列为一章,可见这些问题有多么令人着迷。我从这一章里挑选了一些问题,在这里和大家分享一下。这本书是 年出版的,
书里提到的一些“最新进展”其实已经不是最新的了;不过我也没有仔细考察每个问题当前的进展,因此本文的信息并不保证是 % 准确的,在此向读者们表示歉意。
3x + 问题
从任意一个正整数开始,重复对其进行下面的操作:如果这个数是偶数,把它除以 ;如果这个数是奇数,则把它扩大到原来的 倍后再加 。序列是否最终总会变成 , , , , , , … 的循环?这个问题可以说是一个
“坑”——乍看之下,问题非常简单,突破口很多,于是数学家们纷纷往里面跳;殊不知进去容易出去难,不少数学家到死都没把这个问题搞出来。已经中招的数学家不计其数,这可以从 3x + 问题的各种别名看出来: 3x + 问题又叫
Collatz 猜想、 Syracuse 问题、 Kakutani 问题、 Hasse 算法、 Ulam 问题等等。后来,由于命名争议太大,干脆让谁都不沾光,直接叫做 3x + 问题算了。
3x + 问题不是一般的困难。这里举一个例子来说明数列收敛有多么没规律。从 开始算起, 步就掉入了“ 陷阱”:, , , , , , , , , , , , , , …
但是,从 开始算起,数字会一路飙升到几千多,你很可能会一度认为它脱离了“ 陷阱”;但是,经过上百步运算后,它还是跌了回来:
, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
, , , , , , , , , , , …
额外,再说一点:如上所述,既然数列的收敛如此没有规律,那么上面我们的程序可能是错误的(包括优化过后的那个)。举一个例子,我们用 long long int 做一个测试:
#include<math.h>
int main(void)
{
long long int n;
int count=; // 统计计算次数 scanf("%I64d", &n); // long long int在windows下一定要用%I64d读入数据,否则会出问题(比如数据截断什么的)
long long max = ; // 记录计算过程中出现的最大值 while(n > )
{
if(n% != )
{
//n = floor(1.5*n + 1);
n = (n * + 1)/2;
}
else
{
n = n / ;
}
count++;
printf("%I64d\n", n); // 输出也使用%I64d if(n > max)
max = n;
} printf("%d\n%I64d\n", count, max); return ;
}
我们输入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问题的更多相关文章
- 1129: 零起点学算法36——3n+1问题
1129: 零起点学算法36--3n+1问题 Time Limit: 1 Sec Memory Limit: 64 MB 64bit IO Format: %lldSubmitted: 4541 ...
- 武汉科技大学ACM:1004: 零起点学算法36——3n+1问题
Problem Description 任给一个正整数n,如果n为偶数,就将它变为n/2,如果为奇数,则将它乘3加1(即3n+1).不断重复这样的运算,经过有限步后,一定可以得到1 . Input 输 ...
- Problem E: 零起点学算法34——3n+1问题
#include<stdio.h> #include<math.h> int main() { int n; n<=pow(,); ; scanf("%d&qu ...
- Problem J: 零起点学算法34——3n+1问题
#include<stdio.h> int main() { ; int n; scanf("%d",&n); ) { ==) n=n*+; else n/=; ...
- grep之字符串搜索算法Boyer-Moore由浅入深(比KMP快3-5倍)
这篇长文历时近两天终于完成了,前两天帮网站翻译一篇文章“为什么GNU grep如此之快?”,里面提及到grep速度快的一个重要原因是使用了Boyer-Moore算法作为字符串搜索算法,兴趣之下就想了解 ...
- 字符串搜索算法Boyer-Moore
整理日: 2015年2月16日 1. 主要特征 假设文本串text长度为n,模式串pattern长度为m,BM算法的主要特征为: 从右往左进行比较匹配(一般的字符串搜索算法如KMP都是从从左往右进行匹 ...
- grep之字符串搜索算法Boyer-Moore由浅入深(比KMP快3-5倍)(转)
这篇长文历时近两天终于完成了,前两天帮网站翻译一篇文章“为什么GNU grep如此之快?”,里面提及到grep速度快的一个重要原因是使用了Boyer-Moore算法作为字符串搜索算法,兴趣之下就想了解 ...
- 从入门到精通之Boyer-Moore字符串搜索算法详解
本文讲述的是Boyer-Moore算法,Boyer-Moore算法作为字符串搜索算法,兴趣之下就想了解这个算法,发现这个算法一开始还挺难理解的,也许是我理解能力不是很好吧,花了小半天才看懂,看懂了过后 ...
- c语言进阶11-算法设计思想
一. 算法设计的要求: 为什么要学算法? /* 输出Hello word! */ #include "stdio.h" void main() { printf("He ...
随机推荐
- php多进程防止出现僵尸进程
对于用PHP进行多进程并发编程,不可避免要遇到僵尸进程的问题. 僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程(zombie)进程.任何进程在退出前(使用exit退出) ...
- scrapy爬虫--10分钟入门
# -*- coding: utf-8 -*- # @Time : 2019/4/18 9:10 # @Author : wujf # @Email : 1028540310@qq.com # @Fi ...
- swiper 滑动获取当前第几页下标
- 基于分布式框架 Jepsen 的 X-Cluster 正确性测试
转自:https://mp.weixin.qq.com/s/iOe1VjG1CrHalr_I1PKdKw 原创 2017-08-27 严祥光(祥光) 阿里巴巴数据库技术 1 概述 AliSQL X-C ...
- PHP第一节课
基础语法 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...
- [vuejs短文]使用vue-transition制作小小轮播图
提示 本文是个人的一点小笔记,用来记录开发中遇到的轮播图问题和vue-transition问题. 会不断学习各种轮播图添加到本文当中 也有可能会上线,方便看效果 开始制作 超简易呼吸轮播 简单粗暴的使 ...
- 【hihocoder 1511】树的方差
[题目链接]:http://hihocoder.com/problemset/problem/1511 [题意] [题解] 有个方差的公式 V(X)=E(X2)−E(X)2 这里E(X)指的是X的期望 ...
- mongodb drop不释放磁盘空间
点击(此处)折叠或打开 use demodb //使用demodb,以下假设操作的collection是foo db.foo.remove({"id":"123456&q ...
- 转载 - KMP算法
出处:http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html KMP算法 在介绍KMP算法之前,先介绍一下BF算法. 一. ...
- error at ::0 can't find referenced pointcut...解决方法
error at ::0 can't find referenced pointcut...解决方法 学习了:http://dyldragon.iteye.com/blog/512612 升级aspe ...