一致する数を合計する方法

一致する数を合計する方法

私はスクリプトに初めて触れ、助けが必要です。答えてくれてありがとう。

私は、次の数字のグループのうちの2つを含む5桁の数字(10000 - 99999の範囲)の合計を求める任務を受けました:{4、5、6}。これは同じ回数内で繰り返すことができ、その場合は各発生は一度計算されます。

一致する数値の例には、42057、74638、および89515があります。私はこの小さなコードを持っています。

#! /bin/bash
for (( CON1=10000; CON1<=99999; CON1++ )) ;
    do
        ## UNKNOWN COMMANDS
    done

答え1

以下は、数字に4、5、6がいくつ出ているかを計算し、bash結果が2であるかどうかに基づいてステートメントを実行する1つの方法です。

$ con1=1457
$ a=${con1//[^456]/}; [ ${#a} -eq 2 ] && echo Yes
Yes

答え2

始める

私はこのようなプロジェクトがあるたびに段階的に進むのが好きです。私が最初にやりたいことは、echoループ内に追加して実行して、ループが私が望むものを提供していることを確認することです。

#! /bin/bash
for (( CON1=10000; CON1<=99999; CON1++ )) ;
do
  echo $CON1
done

これで実行すると、head -5出力される最初の5行だけが表示されます。

$ ./cmd.bash | head -5
10000
10001
10002
10003
10004

いいですね。次のようにエンディングを確認してください。

$ ./cmd.bash | tail -5
99995
99996
99997
99998
99999

よさそうだねここで、{4,5,6} セットで 2 桁の数字を識別する次の手順を実行するいくつかの方法を見てみましょう。私の最初の本能はそれを見つけることでしたgrep。 Bashでのみこれを行う方法がありますが、私はさまざまなツール、、、そしてgrepこれを行うのが好きです。主にそう思うからです。awksed

離れた

grepそれでは、{4,5,6}セットで2桁の数字を含む行をどのように見つけることができますか?これを行うには、正規表現として次のように書かれた集合表記法を使用できます[456]。セットで一致させる桁数を指定することもできます。次のように書いてください。

[456]{#}

#数字または数字の範囲はどこにありますか? 3つが欲しいと書いて、[456]{3}2~5桁がほしいと書いています[456]{2,5}。 3つ以上が欲しいなら[456]{3,}`と書いてください。

したがって、あなたのシナリオではです[456]{2}。で正規表現を使用するには、特定のgrepバージョンがそのスイッチをサポートするgrep必要があります-E。これは通常、ほとんどの標準で使用できますgrep

$ echo "45123" | grep -E "[456]{2}"
45123

動作しているように見えますが、数字3を指定すると問題が発生し始めます。

$ echo "45423" | grep -E "[456]{2}"
45423

これもよく当てはまります。grepこれは、文字列に数字という概念がないためです。これは愚かなことです。文字列の一連の文字がセットからのもので、文字列に2つの文字と2つの数字があるかどうかを知らせるように指示します45423

次の文字列も失敗します。

$ echo "41412" | grep -E "[456]{2}"
$

それでは、この方法はうまくいきますか?戦略を少し変更するだけで終わりですが、正規表現を再調整する必要があります。

はい

$ echo -e "41123\n44123\n44423\n41423" | grep -E "[^456]*([456][^456]*){2}"
44123
44423
41423

上記では、4種類の文字列を紹介します。echo -e "41123\n44123\n44423\n41423"私達の範囲の4つの数字だけを印刷します。

$ echo -e "41123\n44123\n44423\n41423"
41123
44123
44423
41423

この正規表現はどのように機能しますか? 0 個以上の「not[456]」文字と、1 個以上の [456] または 0 個以上の「not[456]」文字で構成される正規表現パターンを設定して、後者を 2 回検索します。

それでは、スクリプトでいくつかのアセンブリを実行してみましょう。

for (( CON1=10000; CON1<=99999; CON1++ )) ;
do
  if echo $CON1 | grep -q -E "[^456]*([456][^456]*){2}"; then
      echo $CON1
    fi
done

上記のhead&トリックを使用すると、動作してtailいることがわかります。

$ ./cmd.bash | head -5
10044
10045
10046
10054
10055

$ ./cmd.bash | tail -5
99955
99956
99964
99965
99966

しかし、この方法は非常に遅いことが判明しました。問題はですgrep。費用がかかり、ループで繰り返しごとにgrepを1回実行するので、約80,000回になります!

これを改善するために、grepコマンドをループから移動してリストを生成した後、次のように数字をエコーし​​たスクリプトの元のバージョンを使用して一度実行できます。

$ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}"

メモ:forループを完全に廃棄し、コマンドラインツールを使用できますseq。これにより、同じ数値シーケンスが生成されますseq 10000 99999

ライナー?

これを行う素晴らしい方法は、上記のコマンドから一連の数字を取得し、各数字の間にpasteaを挿入するコマンドにパイプ+し、その出力をコマンドライン計算機として実行することですbc

$ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}" | paste -s -d"+"
10044+10045+10046+10054+10055+10056+10064+10065+10066+10144+10145+...

$ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}" | paste -s -d"+" | bc
2409327540

しかし、これはこの問題を解決する全く異なる方法なので、ループに戻りますfor

純粋なバッシュを使う

したがって、Bashの数字が正確に2桁であるかどうかをテストする方法が必要ですが、grep80,000回呼び出すのと同じくらい高価ではありません。最新バージョンのBashには、=~ANDを実行できる演算子を使用する機能が含まれていますgrep

#!/bin/bash
for (( CON1=10000; CON1<=99999; CON1++ )) ;
  if [[ $CON1 =~ [^456]*([456][^456]*){2} ]]; then
    echo $CON1
  fi
done

これを実行することはまさに私たちが望むようです。

$ ./cmd1.bash  | head -5
10044
10045
10046
10054
10055

$ ./cmd1.bash  | tail -5
99955
99956
99964
99965
99966

確認の結果、41511で動作することがわかりました。

$ ./cmd1.bash | grep 41511
41511

引用する

答え3

私は純粋なBashスクリプトでこれを行う必要があるようですが、John1024のアルゴリズムをawkに変換すると、たくさん加速する:

awk 'BEGIN{k=0;for(i=10000;i<100000;i++){j=i;if(gsub(/[456]/,"",j)==2)k+=i};print k}'

bashのバージョン時間の1/20未満で実行され、str.count()Pythonの組み込みメソッドを使用するPythonのバージョンよりわずかに高速です。

関連情報