关于添加单行的整数,存在几种提议的shell脚本解决方案;
然而,仔细观察所选择的每种解决方案,都存在固有的局限性:
awk
会以任意精度和整数大小扼杀(它表现得像C一样,毕竟)bc
我们宁愿对任意长输入感到不满:(sed 's/$/+\\/g';echo 0)|bc
了解跨平台可能存在可移植性问题(参见[1] [2]),这是不受欢迎的, 是否有一个通用的解决方案,它在实用性和简洁方面都是赢家?
提示:SunOS& MacOSX是可移植性问题的例子
网络连接。 can dc
命令允许处理任意大的2 ^ n,整数或其他输入?
[1] awk
:https://stackoverflow.com/a/450821/1574494或https://stackoverflow.com/a/25245025/1574494或Printing long integers in awk
答案 0 :(得分:3)
我通常使用的是paste -sd+|bc
:
$ time seq 1 20000000 | paste -sd+|bc
200000010000000
real 0m10.092s
user 0m10.854s
sys 0m0.481s
(对于严格的Posix兼容性,paste
需要提供一个显式参数:paste -sd+ -|bc
。显然,在OS X上默认安装BSD paste
实现是必要的。)
但是,对于较大的输入,这将失败,因为bc
在评估它之前在内存中缓冲整个表达式。在我的系统上,bc
内存不足,试图增加1亿个数字,尽管它能够达到7000万个。但其他系统的容量可能较小。
由于bc
具有变量,因此可以通过重复添加到变量而不是构造单个长表达式来避免长行。这是(据我所知)100%Posix兼容,但有3倍的时间惩罚:
$ time seq 1 20000000|sed -e's/^/s+=/;$a\' -es|bc
200000010000000
real 0m29.224s
user 0m44.119s
sys 0m0.820s
处理输入大小超过bc
缓冲容量的情况的另一种方法是使用标准xargs
工具在组中添加数字:
$ time seq 1 100000000 |
> IFS=+ xargs sh -c 'echo "$*"' _ | bc | paste -sd+ | bc
5000000050000000
real 1m0.289s
user 1m31.297s
sys 0m19.233s
每个xargs
评估使用的输入行数量因系统而异,但通常为数百个,可能更多。显然,xargs | bc
调用可以任意链接以增加容量。
在xargs
超出-s
命令容量的系统上,可能需要使用ARG_MAX
开关限制bc
扩展的大小。除了执行实验以确定bc
缓冲区限制之外,没有可移植的方法来确定该限制可能是什么,但它肯定应该不小于LINE_MAX
,保证至少为2048。即使使用100位数的加数,也可以减少20倍,因此假设您准备等待一对,10个xargs|bc
管道链将处理超过10个 13 加数完成的几个月。
作为构建大型固定长度管道的替代方法,您可以使用函数递归管道xargs|bc
的输出,直到只生成一个值:
radd () {
if read a && read b; then
{ printf '%s\n%s\n' "$a" "$b"; cat; } |
IFS=+ xargs -s $MAXLINE sh -c 'echo "$*"' _ |
bc | radd
else
echo "$a"
fi
}
如果对MAXLINE
使用一个非常保守的值,上面的速度相当慢,但是如果值较大,则它比简单的paste|bc
解决方案慢得多:
$ time seq 1 20000000 | MAXLINE=2048 radd
200000010000000
real 1m38.850s
user 0m46.465s
sys 1m34.503s
$ time seq 1 20000000 | MAXLINE=60000 radd
200000010000000
real 0m12.097s
user 0m17.452s
sys 0m5.090s
$ time seq 1 100000000 | MAXLINE=60000 radd
5000000050000000
real 1m3.972s
user 1m31.394s
sys 0m27.946s
除了bc
解决方案外,我还有其他一些可能性。如上所示,输入2000万个数字,paste|bc
需要10秒。这几乎与使用
gawk -M '{s+=$0} END{print s}'
python
和perl
等编程语言证明更快:
# 9.2 seconds to sum 20,000,000 integers
python -c $'import sys\nprint(sum(int(x) for x in sys.stdin))'
# 5.1 seconds
perl -Mbignum -lne '$s+=$_; END{print $s}'
我无法在大输入上测试dc -f - -e '[+z1<r]srz1<rp'
,因为它的性能似乎是二次的(或更差);它在3秒内总计了25000个数字,但花了19秒来总计5万和90秒来做10万。
虽然bc
不是最快的,并且内存限制需要笨拙的解决方法,但它具有在Posix兼容系统上开箱即用的优势,而无需安装任何标准实用程序的增强版本({{1 }}或Posix不需要的编程语言(awk
和perl
)。
答案 1 :(得分:2)
dc(1)
的最佳解决方案在读取输入时对其进行求和:
$ jot 1000000 | sed '2,$s/$/+/;$s/$/p/' | dc
500000500000
答案 2 :(得分:1)
您可以将$ seq 1 20000000 | gawk -M '{s+=$0} END{print s}'
200000010000000
与-M标志一起使用:
Perl
或启用bignum的$ seq 1 20000000 | perl -Mbignum -lne '$s+=$_; END{print $s}'
200000010000000
:
{{1}}
答案 3 :(得分:1)
$ seq 1000|(sum=0;while read num; do sum=`echo $sum+$num|bc -l`;done;echo $sum)
500500
此外,这个人不会赢得最高奖项,但它是:
答案 4 :(得分:0)
以下似乎可以解决问题:
$ seq 1000|dc -f - -e '[+z1<r]srz1<rp'
500500
但是,它是最佳解决方案吗?