最近突发奇想,不知为何对短码上瘾了,虽说短码不利于工程,因为很难看懂,但是能写出牛逼短码的程序员都是高手这一事实是不会改变的,那我今天刚好无聊缩了个小程序,发出来交流交流。
题目很简单:
对任何一个自然数n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把(3n+1)砍掉一半。这样一直反复砍下去,最后一定在某一步得到n=1。求给一个数字,要多步不才能使得n=1
首先是没缩短之前的源代码: 一共是15行
#include
int main(){
int n,sum = 0;
scanf("%d",&n);
while( n!=1 ){
if( n % 2 ){
n = (3*n+1)/2;
}else{
n /= 2;
}
++sum;
}
printf("%d\n",sum);
return 0;
}
代码很清晰,乍一看没什么可以缩减的,我们来一步一步缩。
首先改造while循环里面的if语句,用三目运算替换,得到:
n%2 ? n = (3*n+1)/2: n /= 2;
这样一下子就缩短了4行,从5行变为了1行,其实这种if语句的缩短是很基础的,而且三目运算在短码里面有着举足轻重的地位,非常普遍,掌握三目运算是很重要的。
然后我们发现sum这个变量除了累加没有别的操作,我们可以继续并入刚才的那一行。
n%2 ? n = (3*n+1)/2: n /= 2,++sum;
于是我们就又缩短了一行,由于while循环里面只一条语句,于是将大括号去掉,然后我们来看看整个代码:共9行
#include
int main(){
int n,sum = 0;
scanf("%d",&n);
while( n!=1 )
n%2 ? n = (3*n+1)/2: n /= 2,++sum;
printf("%d\n",sum);
return 0;
}
这么一看好像差不多了,但是这还远远不够,刚才提到了if语句的一个缩短方法,其实一般的短码中不会出现while循环,因为while循环功能太少,而for循环的两个分号隔开的三段既可以有初始,又有判断语句,然后又有一般作为累加段的常用语句段,于是我们接下来做第二次的大缩减。
于是while循环就变成了这个样子
for( ; n != 1 ; n%2 ? n = (3*n+1)/2: n /= 2,++sum);
这样一来又缩减了一行,然后我们继续研究一下整个代码:
#include
int main(){
int n,sum = 0;
scanf("%d",&n);
for( ; n != 1 ; n%2 ? n = (3*n+1)/2: n /= 2,++sum);
printf("%d\n",sum);
return 0;
}
首先很快的我们可以把scanf放入for循环中这个很容易想到,但是printf呢?
这时候我们应该可以想到printf会输出的条件是循环的跳出,于是可以将printf放入for的第二个语段里面,于是稍加改造成了这个样子
for( scanf("%d",&n); n != 1 ? 1:(printf("%d\n",sum),0) ; n%2 ? n = (3*n+1)/2: n /= 2,++sum);
这里面有几个注意点,也主要是如何把printf加入到for循环中的注意点:
1. 第二条语句肯定要有返回值,不是0就是非0来给出for循环是否结束
2. (x,y)这个表达式的返回值是y
这两个注意点注意了之后就很好办了,代码就可以很简单的缩减成这个样子了。
我们继续看看现在整个代码是什么样子:一共是6行
#include
int main(){
int n,sum = 0;
for( scanf("%d",&n); n != 1 ? 1:(printf("%d\n",sum),0) ; n%2 ? n = (3*n+1)/2: n /= 2,++sum);
return 0;
}
纵观整个代码前两行和后两行应该是没什么好缩短的了,况且也没啥意思,for循环一行搞定,不可能再从1行缩减到0行,因此只有变量的定义这部分了,那么这个能不能归并到别的行呢,答案是肯定的,我们要注意到一点,main是一个函数,因此可以有参数,最终的代码为:
#include
int main(int n,int sum){
for(sum = 0,scanf("%d",&n) ; n!=1?1:(printf("%d\n",sum),0); n%2?n = (3*n+1)/2:n /= 2,++sum);
return 0;
}
一共只有5行,作为这个程序的主体部分也就是for循环,只一行,本次短码实例到此结束。
PS: 貌似VS编译不通过的样子,我同学说的,我不清楚,因为我一直都是用的Code::Blocks是GNU的标准,我自己编译没啥问题。
------------------------------分割线------------------------------
有同学继续对我的进行了修改,减少了几个字节,我觉得有必要贴出来,能少多少就少多少,追求最好嘛。
#include
int main(int n,int sum){
for( sum = 0,scanf("%d",&n) ; n!=1?1:!printf("%d\n",sum) ; n =( n%2?(3*n+1):n) >> 1,++sum);
return 0;
}
最后再次声明,短码不利于工程,Just for fun,大家玩玩就行,在实际的工作学习中不要养成这个习惯,我也只是最近突然对这个来了兴趣,觉得有更好的方法,或者可以再缩短的欢迎交流。谢谢! 最后附上壁纸一张,前两篇都是风景,这次来个动漫的,我的第三篇博客到此结束。