アルゴリズム一覧

アルゴリズム一覧

両方のリストを減算する簡単な方法は何ですか?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 )

listrlist1からlist2のすべての要素を削除して、次のような結果list()を取得する方法:

listr=( 4 6 10 )

リストはファイル内に配置することもできますが、リストが大きい場合(メモリが多すぎる場合)には、それを行う必要があります。

簡単にするために、すべてのアルゴリズムをコミュニティの回答に入れました。

そこで行われたさまざまなテストについて読んでください。

最初の問題は、重複していないlist2の完全なリスト(list1)で欠落している要素を見つけることです。

ただし、リストが次の場合:

list1=( a a b b b c     d d   )
list2=(     b b   c c c d d e )

と定義複数のエピソード減算は次のとおりです。このページから、予想される結果は次のとおりです。

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はい、シェルでテキストファイル(行リスト)を処理するのは悪い考えで、ゆっくりと設計されていることを知っています。
しかしそこには避けられないような状況
私(私たち)は提案に開いています。

答え1

comm以下を使用して、両方のリストに共通の項目を削除できます。

listr=($(comm -3 <(printf "%s\n" "${list1[@]}" | sort) <(printf "%s\n" "${list2[@]}" | sort) | sort -n))

これは、2つのリストを予想される順序で並べ替え、比較し、両方のリストcommの一意の項目のみを出力し、番号順に並べ替えます。

両方のリストがアルファベット順にソートされている場合(~によるとLC_COLLATE)、再配置を避けることができます。

listr=($(comm --nocheck-order -3 <(printf "%s\n" "${list1[@]}") <(printf "%s\n" "${list2[@]}")))

比較する必要がある値がファイルに保存されている場合でもうまく機能します。

答え2

#!/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

答え3

抽象的な:

  • 長いリストの場合、リストがすでにソートされている場合は、comm(alg7)が最も高速です。

  • zshソリューションは(今まで)最速です。もしファイルを読みません。つまり、リストが「メモリに」提供されます。ただし、ファイルから値を読み取る必要がある他のすべてのソリューションと比較することは公平ではありません。元のコード(テストからファイルを読み取る時間を避ける)を、ファイルを読み取る時間も含むコードに変更しました。


これはコミュニティの回答であり、各回答の時間だけが報告されます。

お願いしますするすべてを比較するには、ソリューション/オプションを編集して追加します。


アルゴリズム一覧

  • alg1:素朴なループソリューション。
  • alg2: 外部sort合計の使用uniq -u
  • alg3: bash で文字列を処理します。
  • alg4:ソートされたリストに-vを追加します(ありがとう@クサラナンダ)
  • alg5:通信(ありがとうスティーブンキット)
  • alg6: zsh (ありがとう@ルア)
  • alg7:通信がすでにソートされているファイルにあります(ありがとうスティーブンキット)

zsh マニュアルの注意事項:

${name:|arrayname}
arrayname が配列変数の名前である場合(内容ではない)、arrayname に含まれるすべての要素は名前置換から削除されます。

テスト

この問題にアクセスするにはいくつかの方法があるため、答えを(公正に)テストするための一般的なフレームワークが必要です。

いくつかのガイドライン(不公平だと思われる場合は変更してください):

  • 合理的な精度を得るには、十分な繰り返しを測定します。
  • 使用しているエンクロージャの内部を測定します(エンクロージャを載せたり下げたりしないでください)。
  • /dev/null で印刷またはリダイレクトしないことで出力オーバーヘッドを防止します。

テストコード:

#!/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

50,000個の値テスト(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

関連情報