linux - sumar - sumas que den como resultado 10
¿Cómo puedo sumar rápidamente todos los números en un archivo? (26)
Perl 6
say sum lines
~$ perl6 -e ''.say for 0..1000000'' > test.in
~$ perl6 -e ''say sum lines'' < test.in
500000500000
Tengo un archivo que contiene varios miles de números, cada uno en su propia línea:
34
42
11
6
2
99
...
Estoy buscando escribir un script que imprima la suma de todos los números en el archivo. Tengo una solución, pero no es muy eficiente. (Toma varios minutos ejecutar). Estoy buscando una solución más eficiente. ¿Alguna sugerencia?
¡Solo por diversión, hagámoslo con PDL , el motor matemático de matriz de Perl!
perl -MPDL -E ''say rcols(shift)->sum'' datafile
rcols
lee las columnas en una matriz (1D en este caso) y sum
(sorpresa) suma todos los elementos de la matriz.
Aquí está otro:
open(FIL, "a.txt");
my $sum = 0;
foreach( <FIL> ) {chomp; $sum += $_;}
close(FIL);
print "Sum = $sum/n";
Aquí hay otro delineador
( echo 0 ; sed ''s/$/ +/'' foo ; echo p ) | dc
Esto supone que los números son enteros. Si necesitas decimales, prueba
( echo 0 2k ; sed ''s/$/ +/'' foo ; echo p ) | dc
Ajuste 2 a la cantidad de decimales necesarios.
Aquí hay una solución que usa python con una expresión generadora. Probado con un millón de números en mi vieja computadora portátil.
time python -c "import sys; print sum((float(l) for l in sys.stdin))" < file
real 0m0.619s
user 0m0.512s
sys 0m0.028s
C siempre gana por velocidad:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
ssize_t read;
char *line = NULL;
size_t len = 0;
double sum = 0.0;
while (read = getline(&line, &len, stdin) != -1) {
sum += atof(line);
}
printf("%f", sum);
return 0;
}
Tiempo para números 1M (misma máquina / entrada que mi respuesta python):
$ gcc sum.c -o sum && time ./sum < numbers
5003371677.000000
real 0m0.188s
user 0m0.180s
sys 0m0.000s
Con Ruby:
ruby -e "File.read(''file.txt'').split.inject(0){|mem, obj| mem += obj.to_f}"
Esto es derecho Bash:
sum=0
while read -r line
do
(( sum += line ))
done < file
echo $sum
Esto funciona:
{ tr ''/n'' +; echo 0; } < file.txt | bc
Más sucinto
# Ruby
ruby -e ''puts open("random_numbers").map(&:to_i).reduce(:+)''
# Python
python -c ''print(sum(int(l) for l in open("random_numbers")))''
Ninguna de la solución hasta ahora usa paste
. Aquí hay uno:
paste -sd+ filename | bc
Como ejemplo, calcule Σn donde 1 <= n <= 100000:
$ seq 100000 | paste -sd+ | bc -l
5000050000
(Para los curiosos, seq n
imprimiría una secuencia de números del 1
al n
dado un número positivo n
.)
No es más fácil reemplazar todas las líneas nuevas por +
, agregar un 0
y enviarlo al intérprete de Ruby
.
(sed -e "s/$/+/" file; echo 0)|irb
Si no tiene irb
, puede enviarlo a bc
, pero debe eliminar todas las líneas nuevas excepto la última (de echo
). Es mejor usar tr
para esto, a menos que tengas un doctorado en sed
.
(sed -e "s/$/+/" file|tr -d "/n"; echo 0)|bc
No lo he probado, pero debería funcionar:
cat f | tr "/n" "+" | sed ''s/+$//n/'' | bc
Es posible que tenga que agregar "/ n" a la cadena antes de bc (como a través de echo) si bc no trata EOF y EOL ...
No sé si puede obtener mucho mejor que esto, teniendo en cuenta que necesita leer todo el archivo.
$sum = 0;
while(<>){
$sum += $_;
}
print $sum;
Otra opción es usar jq
:
$ seq 10|jq -s add
55
-s
( --slurp
) lee las líneas de entrada en una matriz.
Otra por diversión
sum=0;for i in $(cat file);do sum=$((sum+$i));done;echo $sum
u otro bash solamente
s=0;while read l; do s=$((s+$l));done<file;echo $s
Pero la solución awk es probablemente la mejor ya que es más compacta.
Para ser ridículo:
cat f | tr "/n" "+" | perl -pne chop | R --vanilla --slave
Para un Perl one-liner, es básicamente lo mismo que la solución awk
en la respuesta de Ayman Hourieh :
% perl -nle ''$sum += $_ } END { print $sum''
Si tiene curiosidad por saber qué es lo que hace una línea de Perl, puede detallarlos:
% perl -MO=Deparse -nle ''$sum += $_ } END { print $sum''
El resultado es una versión más detallada del programa, en una forma que nadie podría escribir por sí mismo:
BEGIN { $/ = "/n"; $/ = "/n"; }
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
$sum += $_;
}
sub END {
print $sum;
}
-e syntax OK
Solo por risitas, probé esto con un archivo que contiene 1,000,000 de números (en el rango 0 - 9,999). En mi Mac Pro, vuelve virtualmente de forma instantánea. Eso es muy malo, porque esperaba usar mmap
sería muy rápido, pero es el mismo momento:
use 5.010;
use File::Map qw(map_file);
map_file my $map, $ARGV[0];
$sum += $1 while $map =~ m/(/d+)/g;
say $sum;
Prefiero usar R para esto:
$ R -e ''sum(scan("filename"))''
Prefiero usar datamash de GNU para tales tareas porque es más breve y legible que Perl o Awk. Por ejemplo
datamash sum 1 < myfile
donde 1 denota la primera columna de datos.
Puede hacerlo con Alacon - utilidad de línea de comandos para la base de datos Alasql .
Funciona con Node.js, por lo que necesita instalar Node.js y luego el paquete de Alasql :
Para calcular la suma del archivo TXT, puede usar el siguiente comando:
> node alacon "SELECT VALUE SUM([0]) FROM TXT(''mydata.txt'')"
Puedes usar awk:
awk ''{ sum += $1 } END { print sum }'' file
Solo por diversión, comparémoslo:
$ for ((i=0; i<1000000; i++)) ; do echo $RANDOM; done > random_numbers
$ time perl -nle ''$sum += $_ } END { print $sum'' random_numbers
16379866392
real 0m0.226s
user 0m0.219s
sys 0m0.002s
$ time awk ''{ sum += $1 } END { print sum }'' random_numbers
16379866392
real 0m0.311s
user 0m0.304s
sys 0m0.005s
$ time { { tr "/n" + < random_numbers ; echo 0; } | bc; }
16379866392
real 0m0.445s
user 0m0.438s
sys 0m0.024s
$ time { s=0;while read l; do s=$((s+$l));done<random_numbers;echo $s; }
16379866392
real 0m9.309s
user 0m8.404s
sys 0m0.887s
$ time { s=0;while read l; do ((s+=l));done<random_numbers;echo $s; }
16379866392
real 0m7.191s
user 0m6.402s
sys 0m0.776s
$ time { sed '':a;N;s//n/+/;ta'' random_numbers|bc; }
^C
real 4m53.413s
user 4m52.584s
sys 0m0.052s
He abortado la carrera sed después de 5 minutos
$ perl -MList::Util=sum -le ''print sum <>'' nums.txt
cat nums | perl -ne ''$sum += $_ } { print $sum''
(igual que la respuesta de Brian Foy, sin ''FIN'')
sed '':a;N;s//n/+/;ta'' file|bc