题目:求2的十万次方各位数之和
Raku、 Perl、Python、Java、Racket、D、C++、Haskell作答
分析
本题难点在于2的100,000次方,得数是一个大数,足有三万一百零三位,计算器算不出来,直接挂了。
感受一下2的十万次方有多大,翻页键往下好几页才装得下,图示是第一屏。
让我们往下看,各种编程语言,如何解答。
Raku代码
raku -e '(2**100_000).split("").sum.say'
Raku代码,一行短代码完事,读起来很顺 — 从左到右依次,2的十万次方(幂操作符**),然后分割为单个数(split),然后累加(sum)。
简洁背后藏着Raku强大的特性:
1) int类型尺寸只受限于内存大小
2) 一切皆为对象,包括整数。split是整数类型的方法。sum是容器的方法。
3)函数式编程风格,让步骤串联,形成流畅的数据流和逻辑,使编写和阅读代码贴合人类思维模式,达到“所想即所写、所写即所得”。
Perl 代码
perl -e 'use bigint; $sum += $_ for (split "", 2**100000); print $sum, "\n"'
感谢bigint,Perl搞定题目没费啥力气,一行代码完事。在终端上直接输入指令回车完成。只不过,Perl代码远不如Raku好懂易写。
Python代码
print(sum(map(int, str(2**100000))))
Python代码对比Raku和Perl代码,算法一模一样。大差别在于可读性。
Raku代码和Perl代码,从左到右读,流畅,接近自然语言。而Python代码呢?第一步,首先必须在大脑中解析括号对,找到嵌套最深的那层2**100000,然后是str,然后往左找到map,最后sum,以此往左逐步求出。初学者想哭。
Java代码
import java.math.BigInteger;
public class SumBigIntDigits {
public static void main(String[] args) {
BigInteger n = new BigInteger("2");
BigInteger total = n.pow(100_000);
System.out.println(sumDigits(total));
}
private static int sumDigits(BigInteger n) {
int sum = 0;
BigInteger bi10 = new BigInteger("10");
BigInteger bi0 = new BigInteger("0");
while ( n.compareTo(bi0) > 0 ) {
sum += n.remainder(bi10).intValue();
n = n.divide(bi10);
}
return sum;
}
}
感谢Java的java.math.BigInteger库,让大数计算不再是梦魇,逃脱了数据溢出。
Java的BigInteger类,有点糙,能干活,写和读不自然。
Racket代码
(define (sum-digits n)
;; sum up all the digits of n
(apply + (digits n)))
(define (digits n)
;; output the decimal digits of the given integer
(define (char->decimal-digit x)
(- (char->integer x)
(char->integer #\0)))
(map char->decimal-digit (string->list (~a n))))
(sum-digits (expt 2 100000))
Racket不仅支持大整数,而且自带expt函数,得精确大数解。优秀!
D代码
import std.stdio;
import std.bigint;
void main() {
exp(2,100_000).sum_digits
.writeln;
}
BigInt exp(int n, uint p) {
return p == 0 ? BigInt(1)
: p == 1 ? BigInt(n)
: p % 2 == 0 ? exp(n, p/2) * exp(n, p/2)
: exp(n, 1+p/2) * exp(n, p/2);
}
BigInt sum_digits(BigInt n) {
return n < BigInt(10) ? n
: n % 10 + sum_digits_rec(n / 10);
}
D语言深受C和C++影响,且引入了很多现代化特性,极大改善了表现力和可读性。
D代码简洁,在main函数里从左到右读顺序读出:2的十万次方(exp函数),数位累加(sum_digits),然后输出(writeln)。
简洁背后是强大的特性:
1) 引入了bigint类型
2)函数式编程风格,让方法串接起来,形成流畅的数据流。让代码编写和阅读贴合人类思考过程。
C++代码
#include <iostream>
#include "lib/bignum.h"
using namespace std;
Bignum exp(int n, int p) {
if ( p == 0 ) return 1;
if ( p == 1 ) return n;
return exp(n, p/2) * exp(n, p/2 + (p%2 ? 1 : 0) );
}
int main() {
Bignum n = exp(2, x);
cout << n.sumDigits() << endl;
}
这篇文章演示的八种语言,C++是唯一的程序员,没有在语言和标准库层面定义大整数类型,导致引入自造的bignum.h头文件,处理2的十万次方大整数计算。
自造轮子拼不过人家官家货,实际运行效率差强人意。
Haskll代码
digit '0' = 0
digit '1' = 1
digit '2' = 2
digit '3' = 3
digit '4' = 4
digit '5' = 5
digit '6' = 6
digit '7' = 7
digit '8' = 8
digit '9' = 9
sum_digits::Integer -> String
sum_digits = show . sum . map digit . show
main = putStrLn $ sum_digits (2 ^ 100000)
Haskell自带大整数类型,Integer,和幂运算。sum_digits函数求一个数的各位数之和。最后,执行计算,输出计算结果。
Haskell必须显示执行类型转换。这是它与以上所有程序语言不同之处。
运行时间对比
编译选项:
(Racket) raco exe
(D) dmd -dw -O -m64
(C++) g++ -Wall -std=c++17 -O2
(Haskell) ghc –make -O2
– 结束-
注:本文为原创文章,引用或转载请注明出处。若有任何错误、遗漏、纰漏,烦请指正。
请订阅微信公众号:IT之州
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/85626.html