编程杂谈|十余年后再做课堂练习题
近日偶然在一论坛网站上看到一道问答题目 “使用三种不同的实现,完成 1+2+..+100 的编程”。 让人回忆起,好似这是初学编程时课堂留下的练习题目。算算如今离开课堂已是十余年了,一时兴趣不妨再来做一做这道题。
三种基础循环语法
没记错的话,这道题在学习完基础循环语法后所布置的练习,最先想到的是使用 for(;;)
循环语句来实现。
int recursionCompute(int n){
int sum = 0;
for (int i = 1; i <= n ; i++) {
sum += i;
}
return sum;
}
熟悉 for(;;)
语句语法的三要素话,也可以写的更简短一些,利用从大到小倒过来进行累加 ,不一定需要 变量 i
int recursionForCompute(int n) {
int sum;
for(sum = 0; n > 0; sum += n--);
return sum;
}
学习循环后布置的练习,出题者的初衷或许是用于练习三种循环写法。所以除开 for(;;)
循环结构,同样 也可以利用 while(){}
的语法来进行实现。
int recursionWhileCompute(int n){
int sum = 0;
while ( n > 0 ){
sum += n--;
}
return sum;
}
当然,还有 do{ }while()
的循环结构来写同样也必然可以的,在此就不赘述了。
应用基础算法思想
以上的几种实现虽然不尽相同,但都是用基础循环语法进行的编码。换个角度除开基础语法的直接实现,也可以结合一些基础算法的思想来解题。例如:如果 将其拆解成同类子问题,对于 求 sum(n)
,可以拆解为 n + sum(n-1)
;最简单的情况 sum(1) 的值为 1,从而可以用递归算法来实现。
int recurrenceCompute(int n){
if( 1 == n ) return 1;
return n + recurrenceCompute(n-1);
}
二分算法的思想或许也是个不错的选择,求 1+2+...+n
的和,其实也就是 求 1+2+...+ mid
+ (mide+1) + ... + n
的和。
int divideCompute(int start,int end){
if (start == end) return start;
int mid = (start + end) / 2;
return divideCompute(start,mid) + divideCompute(mid+1,end);
}
另外,也可以观察下规律 1+100 = 2+99 = 3+98 ...,不妨也可以运用双指针算法技巧来进行实现。
int doublePointerCompute(int n){
int sum = 0,left = 1,right = n++;
do{
sum += n;
}while (++left < --right);
return left == right ? sum + right : sum;
}
数学与二进制
编程的基础是数学,如果说最简单的实现,当然还是应用数学求和公式。
int mathCompute(int n){
return ((1+n)*n)/2;
}
就计算机的组成设计而言,计算机中的位运算通常可以通过基本的逻辑门操作来实现,而除法则需要更复杂的电路和算法。位运算可以在硬件级别上更直接地执行,而不需要像除法一样的迭代和复杂的步骤。所以对于求1+2...+100的和而言 除2
可以应用二进制位移来替代。
int mathBitCompute(int n){
return ((1+n)*n)>>1;
}
如果熟悉编程中各种运算优先级,或许可以写的更艺术
一点,不过这样可读性就没那么高了。
int mathIncrementCompute(int n){
return n++*n>>1;
}
特定高级语言功能
除了语法、算法、数学外,可以考虑结合特定高级语言功能库来进行实现。以java为例 ,在1.8版本应用流(Stream)来进行实现。
int streamCompute(int n){
return IntStream.rangeClosed(1,n).sum();
}
同样应用 Stream 提供的功能,改成 并发执行。
int streamParallelCompute(int n){
return IntStream.rangeClosed(1,n).parallel().sum();
}
除了 IntStream, 应用 Stream 迭代器来实现也是个不错的选择。
int streamIterateCompute(int n){
return Stream.iterate(1, i -> i + 1)
.limit(n)
.reduce(0, Integer::sum);
}
如果升级到 java 9版本,也可以利用 takeWhile 来实现。
int streamTakeWhileCompute(int n){
return Stream.iterate(1, i -> i + 1)
.takeWhile(x -> x <= n)
.reduce(0, Integer::sum);
}
当然还可以有更多的写法了,比如Java 19 中的虚拟线程等等,在此就不一一列举了。
解题思路从最开始的基础语法开始,然后是数据结构与算法,再到结合计算机基础原理(电路、二进制等),再到特定语言的类库、特性等,也暗合初学编程从入门到熟练使用的过程。
回顾看对于简单的问题,也有许多不同的编码方式和实现方法。不同的开发者可能会选择不同的路径来解决相同的问题。通过编写具有不同实现方式的代码,来表达自己的观点、风格和创造性,这些不同的实现让人感受到_编程多样之美_。
在底层,所有的编程语言最终都会被翻译成机器语言-计算机硬件可以直接执行的指令。这些指令本质上是逻辑门电路的操作,都基于二进制数学。底层的一致性使得无论我们使用什么样的高级抽象,最终都是在共同的计算模型下运行,这何不是一种_编程统一之美_。
或许喜爱编程的人都是痴儿,痴迷于编程既多样又统一的美感。也或许是这种痴迷使得编程成为一门富有乐趣和挑战性的艺术和技术。
欢迎关注 Java研究者 公众号、博客、腾讯专栏等,期待点赞、收藏。