以下はパフォーマンスの問題が多いスクリプトです。
#!/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
10進数のソース番号を対応するデフォルトの62桁に変換します。先行ゼロを削除する2桁の10進数のペアを出力します(つまり、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 -
3.131
grp_rem=`echo $sub_rem" "$grp_rem`
に変換grp_rem="$sub_rem $grp_rem"
- cut(
set IFS='|'; set -f
; およびset -- $1
) を使用してシェル分割を使用しないでください。IFS='|' read a x <<<"$i"
または(<<<
一時ファイルの作成中に)使用- すでに使用されているので、読み取りを交換してください。
0.454
- 1つのループに減らし(ifを削除)、最後に末尾のスペース
0.207を削除します。 $((...))
2つの0.113----シェルを接続するループをより堅くします
。変更時間は35.023秒より約300倍速いです。
++++ これはおそらくシェルスクリプトでできる最高のことでしょう。- 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スクリプトかどうかはわかりませんが、うまくいきます。シェルより10,000本以上のラインが高速です。
メモ:-M
これは、指定された19桁の順序で数字を処理するために必要な(任意の精度)オプションでGNU awkを使用することです。より長い数字を処理できます。どのくらいかかるかは確認していませんが、限度がかなり高いと確信しています。 :-) 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