下面是我的脚本,它有很多性能问题
#!/usr/bin/ksh
while read i
do
x=`echo $i |cut -d"|" -f2`
rem=`expr $x % 62`
echo "reminder is " $rem
quo=`expr $x / 62`
echo "quotiont is " $quo
grp_rem=" "
if [[ ${#quo} -ge 2 ]]
then
while [ $quo -ge 62 ]
do
sub_rem=`expr $quo % 62`
quo=`expr $quo / 62`
grp_rem=`echo $sub_rem" "$grp_rem`
done
fi
echo $i"|"$quo" "$grp_rem" "$rem >> base62_while.out
done < base62_while.txt
无论如何,我可以使用上面的脚本提高性能吗?
示例输入:
1|5147634738948389685
样本输出
1|5147634738948389685|6 8 16 13 46 17 20 35 9 49 43
答案1
您不需要调用任何外部工具:ksh 可以进行算术运算。我还使用数组来存储余数
#!/usr/bin/ksh
div=62
while IFS='|' read -r n x; do
rem=$(( x % div ))
quo=$(( x / div ))
echo "reminder is $rem" >&2
echo "quotiont is $quo" >&2
remainders=( $rem )
while (( quo >= div )); do
sub_rem=$(( quo % 62 ))
quo=$(( quo / 62 ))
echo "reminder is $sub_rem" >&2
echo "quotiont is $quo" >&2
remainders=( $sub_rem "${remainders[@]}" )
done
echo "$n|$x|$quo ${remainders[*]}"
x=$quo
for r in "${remainders[@]}"; do
x=$(( x * div + r ))
done
echo Verification: $x
done <<END
1|5147634738948389685
END
答案2
这应该会快得多
#!/usr/bin/ksh
#
while IFS='|' read n x
do
base62="$(echo "obase=62; $x" | bc | sed -re 's/ 0/ /g' -e 's/^ //')"
printf "%d|%s|%s\n" $n "$x" "$base62"
done <base62_while.txt >>base62_while.out
该base62
行用于bc
将十进制源数字转换为等效的 62 基数。它输出两位十进制对,我们从中去掉任何前导零(即02
重写为2
,但45
保持不变)。
输入
1|5147634738948389685
输出
1|5147634738948389685|6 8 16 13 46 17 20 35 9 49 43
答案3
有几件事可以做(并提高速度):
- 原版 1000 个号码
35.023 秒 - 将所有 expr 命令替换为算术扩展 $((x%62))
14.473 - 转换
grp_rem=`echo $sub_rem" "$grp_rem`
为grp_rem="$sub_rem $grp_rem"
3.131 - 避免使用 cut (
set IFS='|'; set -f
; 并使用 shell split withset -- $1
)- 或使用
IFS='|' read a x <<<"$i"
(虽然<<<
创建一个临时文件) - 由于一个读取已被使用,因此替换该读取。
0.454
- 或使用
- 减少到只有一个循环(删除 if)并删除末尾的尾随空格
0.207 - 使循环更紧密 连接两者
$((...))
0.113
---- shell:变化比 35.023 秒快约 300 倍。
++++ 这可能是使用 shell 脚本可以完成的最好的事情。 - 更改为 awk 0.123
---- awk:总共更改速度快了约 280 倍
结果脚本:
#!/usr/bin/ksh
while IFS='|' read a b # read both values split on '|'
do
x=$b # set value of x (quotient)
grp_rem="" # clear value of group
while (( rem=x%62 , x/=62 )) # do both math expressions.
do
grp_rem="$rem $grp_rem" # concatenate resulting values
done
grp_rem=${grp_rem%?} # remove one character (an space)
echo "$a|$b|$rem $grp_rem"
done < base62_while.txt >> base62_while.out
相当于 awk 脚本。我不知道这是否是更快的 awk 脚本,但工作正常。比 shell 快 10k 行以上。
笔记:这是使用带有(任意精度)选项的 GNU awk,-M
这是按照您提供的 19 位数字顺序处理数字所必需的。它可以处理更长的数字,我没有检查多长时间,但我很确定限制相当高。 :-) 请注意,awk 必须在包含该选项的情况下进行编译(使用 进行检查awk 'BEGIN{ print( PROCINFO["gmp_version"], PROCINFO["prec_max"]) }'
)
awk -MF'|' '{ x=$2; grp_rem="";
while(x>0){
rem=x%62;
x=int(x/62);
grp_rem=rem" "grp_rem
}
printf("%-22s|%s\n",$0,grp_rem)
}
' <base62_while.txt >>base62_while.out
答案4
和dc
:
sed 's/.*|\(.*\)/[&|]P\1p/;1s/^/62o/' base62_while.txt | dc > base62_while.out
或者bc
(请注意, 的历史实现bc
实际上是 的包装器dc
):
sed 's/.*|\(.*\)/"&|";\1/;1s/^/obase=62;/' base62_while.txt | bc > base62_while.out
请注意,dc
并对bc
长行输出进行换行。使用 GNU 实现,您可以将DC_LINE_LENGTH
和BC_LINE_LENGTH
环境变量设置为 0 以避免这种情况。
$ echo '1|167883826163764944817996215305490271305728' | sed 's/.*|\(.*\)/[&|]P\1p/;1s/^/62o/' | dc
1|167883826163764944817996215305490271305728| 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
00
$ echo '1|167883826163764944817996215305490271305728' | sed 's/.*|\(.*\)/[&|]P\1p/;1s/^/62o/' | DC_LINE_LENGTH=0 dc
1|167883826163764944817996215305490271305728| 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00