2つのリストを減算する高速な方法は何ですか1。リストは小さいかもしれませんが、シェルでの直接的な方法かもしれません。または、リストが長くなる可能性があります。外部ツールの方が速いかもしれません。
次の2つのリストがあるとします。
list1=( 1 2 3 4 5 6 7 8 9 10 11 12 )
list2=( 1 2 3 5 7 8 9 11 12 )
List1からlist2のすべての要素を削除して、次と同等の結果のリスト(listr
)を取得する方法:
listr=( 4 6 10 )
リストが大きい場合(大量のメモリを使用する可能性がある場合)も、リストはファイル内にある可能性があります。
この質問を簡潔にするために、私はすべてのアルゴリズムをコミュニティの回答に入れています。
そこで行われた複数のテストを読んでください。
元の質問は、繰り返しなしで、list2の完全なリスト(list1)の欠落している要素を見つけることを目的としていました。
ただし、リストが次の場合:
list1=( a a b b b c d d )
list2=( b b c c c d d e )
そして multiset 減算の定義は このページでは のようになり、期待される結果は次のとおりです。
listr= ( a a b )
アルゴリズム1と3のみが正しく機能します。
アルゴリズム2も4もこれを行うことはできません。
アルゴリズム5(comm)は、comm -23
。
アルゴリズム6(zsh)が失敗します。私はそれを動作させる方法を知りません。
アルゴリズム7(通信)。上記のように、-23
機能します。
対称差の設定 定義のすべてのアルゴリズムを分析したわけではありません。
listr=( a a b c c e )
だが comm -3 list1.txt list2.txt | tr -d ' \t'
機能します。
1 はい、テキストファイル(行のリスト)をシェルで処理することは悪い考えであることを知っています。設計上、遅いです。
しかし 避けられないように見える場合 があります。
私(私たち)は提案を受け入れています。
comm
を使用すると、両方のリストに共通するものをすべて削除できます。
listr=($(comm -3 <(printf "%s\n" "${list1[@]}" | sort) <(printf "%s\n" "${list2[@]}" | sort) | sort -n))
これにより、両方のリストがcomm
が予期する順序で並べ替えられ、それらが比較され、どちらかのリストに固有のアイテムのみが出力され、数値順に再度並べ替えられます。
両方のリストが辞書式にソートされている場合( LC_COLLATE
)、再度ソートすることを避けることができます:
listr=($(comm --nocheck-order -3 <(printf "%s\n" "${list1[@]}") <(printf "%s\n" "${list2[@]}")))
これは、比較する必要のある値がファイルに保存されている場合にもうまく機能します。
#!/bin/zsh
list1=( 1 2 3 4 5 6 7 8 9 10 11 12 )
list2=( 1 2 3 5 7 8 9 11 12 )
listr=("${(@)list1:|list2}")
typeset -p listr
概要:
長いリストの場合、リストがすでにソートされている場合は、comm(alg7)が最速です。
Zshソリューションは(はるかに)最も高速ですifファイルが読み込まれない場合、つまり、リストが「メモリ内」にある場合。ただし、これはファイルから値を読み取る必要がある他のすべてのソリューションとの公平な比較ではありません。元のコード(ファイルを読み取る時間をテストから回避する)を、ファイルを読み取る時間も含むコードに変更しました。
これはコミュニティの回答です。各回答の時間のみを報告することを目的としています。
[〜#〜] do [〜#〜]ソリューション/オプションを編集して追加し、すべてを比較してください。
sort
およびuniq -u
の使用Zshマニュアルからのメモ:
$ {name:| arrayname}
arraynameが配列変数の名前(内容ではなくN.B.)の場合、arraynameに含まれる要素はすべて、nameの置換から削除されます。
これを解決するにはいくつかの方法があるため、答えを(公平に)テストするための一般的なフレームワークが必要です。
いくつかのガイドライン(不当なものを見つけた場合は変更してください):
テストコード:
#!/bin/bash
alg1(){
arr=( "${list1[@]}" )
for i in "${list2[@]}"; do
for j in "${!arr[@]}"; do
if [[ "$i" == "${arr[j]}" ]]; then
unset arr["$j"]
break
fi
done
done
printf '%s ' "${arr[@]}"; echo
}
alg2(){
arr=($({ printf '%s\n' "${list1[@]}" "${list2[@]}"; } | sort | uniq -u))
printf '%s ' "${arr[@]}"; echo
}
alg3(){
a=" $(printf '%s ' ${list1[@]})" # Watch the space at the start!!.
for i in "${list2[@]}"; do
a=${a/ "$i" / };
done
printf '%s\n' "$a"
}
alg4(){ join -v 1 list1.txt list2.txt ; }
alg5(){ #listr=$(
comm -3 <(printf "%s\n" "${list1[@]}" | sort) \
<(printf "%s\n" "${list2[@]}" | sort) |
sort -n
#)
}
alg6(){ zsh -c ' alg6(){
list1=( $(cat list1.txt) )
list2=( $(cat list2.txt) )
listr=("${(@)list1:|list2}")
typeset -p listr
}
TIMEFMT="%E %U %S"
time ( for ((k=0;k<'"$1"';k++)); do alg6; done; )
'
}
#: <<-\_comment_
alg7(){ comm -23 list1.txt list2.txt; }
try(){ for ((k=0;k<$1;k++)); do "$2"; done; }
#list1=( 1 2 3 4 5 6 7 8 9 10 11 12 )
#list2=( 1 2 3 5 7 8 9 11 12 )
#list1=( a a b b b c d d )
#list2=( b b c c c d d e )
size=1000000
list1=( "0" $(seq 1 "$size") )
list2=( "${list1[@]}" ); unset "list2[123]" "list2[234]" "list2[345]"
printf '%s\n' "${list1[@]}" | sort >list1.txt
printf '%s\n' "${list2[@]}" | sort >list2.txt
repeats=${1:-10}; shift
out=${1:-no} ; shift
(($#==0)) && set -- alg{1..7}
TIMEFORMAT='%R %U %S'
for i
do printf '%s ' "$i"
if [[ $out == no ]]; then
[[ $i != alg6 ]] &&
time try "$repeats" "$i" >/dev/null ||
time alg6 "$repeats" >/dev/null
else
[[ $i != alg6 ]] &&
time try "$repeats" "$i" ||
time alg6 "$repeats"
fi
done
$ ./script
alg1 2.056 0.806 1.237
alg2 3.478 3.126 1.756
alg3 0.999 0.728 0.304
alg4 1.186 0.780 0.434
alg5 5.234 1.926 1.722
alg6 2.71s 1.64s 1.26s
2.758 1.637 1.265
alg7 1.156 0.799 0.422
Alg6の時間は、zshによって報告された時間であり、その後bashによって報告された時間です。
また、ファイルの読み取りを関数から外部に削除すると、zshの実行時間は本当に短くなります(0.050)。
500個の値(10回の繰り返し)のみのリストをテストすると、alg1は非常に非効率的であることがわかります。さらなるテストからの削除:
alg1 4.149 3.471 0.657
alg2 0.145 0.055 0.063
alg3 0.219 0.180 0.009
alg4 0.039 0.015 0.003
alg5 0.149 0.018 0.027
alg6 0.06s 0.02s 0.02s
0.087 0.030 0.018
alg7 0.033 0.008 0.008
5kの値(10回の繰り返し)をテストすると、alg3も非効率的であることがわかります。
alg2 0.590 0.526 0.187
alg3 12.957 12.888 0.044
alg4 0.098 0.047 0.008
alg5 0.654 0.028 0.036
alg6 0.16s 0.12s 0.04s
0.211 0.118 0.044
alg7 0.038 0.022 0.014
5万個の値のテスト(10回の繰り返し):
alg2 6.487 5.838 1.611
alg4 0.488 0.469 0.019
alg5 5.073 0.250 0.056
alg6 1.42s 1.20s 0.21s
1.467 1.206 0.216
alg7 0.271 0.247 0.014
500kの場合(10回の繰り返し)
alg4 5.471 5.269 0.156
alg6 15.14s 13.33s 1.91s
15.215 13.335 1.926
alg7 2.833 2.655 0.138
1Mの場合(10回の繰り返し)
alg4 11.127 10.804 0.251
alg7 5.772 5.525 0.230