【アルゴリズム バブルソート】ざっくりわかるシェルスクリプト10

バブルソート

バブルソートは単純選択方法と同様、実現は簡単です。
しかし、比較回数と交換回数は最悪の場合、O(N^2)です。
ソート中に選ばれた最大値が水の中の泡のように水面に向かって浮かび上がっていく過程から、バブルソートと呼ばれています。
データの交換を中心に行っているため、単純交換法とも言われています。

バブルソートのイメージは以下の図を見てもらえればと思います。

プログラムソース

この章で使っているプログラムソースは以下にあります。
02_1BubbleSort.sh 一般的な配列のバブルソート
02_1Eval_BubbleSort.sh 擬似的な2次元配列で実装したバブルソート

バブルソートの処理手順

データは1次元配列に格納されていると仮定します。
図の赤枠で囲まれた左右の要素を比較して、
大きな要素を右に小さな要素を左に配置します。
移動した段階で左に小さな要素、右に大きな要素がある場合は、要素の交換が不要なので、赤枠は一つ場所を右に移動します。
右端まで移動しきった場合、一番右の要素には配列の中で最も大きな値を持つ要素が配置されています。
しかし、それ以外の要素はまだ並べ替えが完了していないので、交換の場所を一番左に移動し、並べ替えが完了した一番右端の一つ手前まで、上記の処理を繰り返します。

2つのループ

バブルソートは2つのforループで構成されます。
外側のforループは、一番右端に最大の値を持つ要素を配置すると、次からはその一つ手前まで、さらにその次は、その一つ手前までといった具合に、配列全体の移動範囲を狭めていきます。
外側のforループではこうした理由からデクリメントされているわけです。
内側のforループは左右の要素の交換処理を行います。
配列の要素(自分自身)と、その要素の一つ右の要素を比較し、配列の要素(自分自身)が、一つ右の要素よりも値が大きければ、自分自身と右側の要素を交換し、右側の要素の値が大きくなるようにした上で、交換場所を一つ右に移動します。

##
# <>bubbleSort()
# バブルソートの1次元配列版
function bubbleSort(){
  for((i=nElems;i>0;i--));do
    for((j=0;j<i-1;j++));do
      # 自分自身と右側の要素を比較
      if (( array[j] > array[j+1] ));then
        # 交換
        tmp=${array[j]};
        array[j]=${array[j+1]};
        array[j+1]=$tmp;
        # 交換
      fi
    done
  done  
}

bash/シェルスクリプトによる疑似2次元配列の実装

#######################################
# 02_1BubbleSort.shを、少しだけオブジェクティブに
# aRray[0].getValue() で値を取得できるように改変した
# 配列にIDと値を入れるだけのbashスクリプト
#######################################
##
# グローバル変数
declare -i nElems=0;
#
# <>display()  
# 配列を表示
function display(){
  for((i=0;i<nElems;i++)){
    echo -n "aRray[$i]  \
    ID: " $( aRray[$i].getID ) " \
    Value:" $( aRray[$i].getValue ) ; 
    echo "";
  }
}
##
# <>setValue() 
# セッター
function setValue() {
  #今後挿入や置き換えに備えてnElemsとは別の変数を用意しておく
  local Elem="$1";      
  local value="$2";
    eval "aRray[$Elem].getValue()      { echo "$value"; }"
}
##
# <>setID()
# セッター
function setID(){
  #今後挿入や置き換えに備えてnElemsとは別の変数を用意しておく
  local Elem="$1";      
  local ID="$2";
    eval "aRray[$Elem].getID()         { echo "$ID"; }"
}
##
# <> insert
# 配列の要素に値を代入
function insert(){
  local ID=$1;          #100からの連番
  local value=$2;       #配列に代入される要素の値
  setID     "$nElems"    "$ID";      #IDをセット
  setValue  "$nElems"    "$value";   #Valueをセット
  ((nElems++));
}
##
# <> set Array
# 配列を作成
function setArray(){
  local N=$1;           #すべての要素数
  local ID=100;         #100からの連番
  local value;          #配列に代入される要素の値
  for((i=0;i<N;i++)){
    value=$( echo $RANDOM );
    insert $((ID++)) $value;
  }
}
##
# <>bubbleSort() 
# バブルソート
# URL:https://www.youtube.com/watch?v=xli_FI7CuzA
function bubbleSort(){
  local tmp_id;
  local tmp_value;
  for((i=nElems;i>0;i--)){
    for((j=0;j<i-1;j++)){
      if(($(aRray[$j].getValue)>$(aRray[$((j+1))].getValue)));then
        # 交換
        tmp_id=$(aRray[$j].getID);
        tmp_value=$(aRray[$j].getValue);
        setID     "$j"    $(aRray[$((j+1))].getID);      #IDをセット
        setValue  "$j"    $(aRray[$((j+1))].getValue);   #Valueをセット
        setID     $((j+1))    $tmp_id;      #IDをセット
        setValue  $((j+1))    $tmp_value;   #Valueをセット
        # 交換
      fi 
    } 
  }  
}
##
# <>execSort()
# メインルーチン
function execSort(){
  local N=$1;
  setArray $N;    #配列をセット
  echo "修正前"
  display;
  bubbleSort;     # バブルソート
  echo "修正後"
  display;
}
##
# 実行
time execSort 10;
exit;

実行結果

bash-5.1$ bash 02_1Eval_BubbleSort.sh
修正前
aRray[0]      ID:  100      Value: 14256
aRray[1]      ID:  101      Value: 2502
aRray[2]      ID:  102      Value: 11843
aRray[3]      ID:  103      Value: 21197
aRray[4]      ID:  104      Value: 30372
aRray[5]      ID:  105      Value: 10460
aRray[6]      ID:  106      Value: 440
aRray[7]      ID:  107      Value: 6641
aRray[8]      ID:  108      Value: 12185
aRray[9]      ID:  109      Value: 8073
修正後
aRray[0]      ID:  106      Value: 440
aRray[1]      ID:  101      Value: 2502
aRray[2]      ID:  107      Value: 6641
aRray[3]      ID:  109      Value: 8073
aRray[4]      ID:  105      Value: 10460
aRray[5]      ID:  102      Value: 11843
aRray[6]      ID:  108      Value: 12185
aRray[7]      ID:  100      Value: 14256
aRray[8]      ID:  103      Value: 21197
aRray[9]      ID:  104      Value: 30372

real    0m0.289s
user    0m0.125s
sys    0m0.162s
bash-5.1$

Valueの値がソートされているのが見てわかると思います。

処理コスト

バブルソートの処理コストは非常に高いわけですが、まずは一番最初のバブルソートの実装ができたわけです。次回からは、もう少しだけ効率的なソート手法の紹介をします。

処理コストを可視化(いずれ何がどうなのかはわかります)

「ざっくり」シリーズのご紹介

【アルゴリズム 再帰】ざっくりわかるシェルスクリプト15
https://suzukiiichiro.github.io/posts/2022-10-07-01-algorithm-recursion-suzuki/
【アルゴリズム キュー】ざっくりわかるシェルスクリプト14
https://suzukiiichiro.github.io/posts/2022-10-06-01-algorithm-queue-suzuki/
【アルゴリズム スタック】ざっくりわかるシェルスクリプト13
https://suzukiiichiro.github.io/posts/2022-10-06-01-algorithm-stack-suzuki/
【アルゴリズム 挿入ソート】ざっくりわかるシェルスクリプト12
https://suzukiiichiro.github.io/posts/2022-10-05-01-algorithm-insertionsort-suzuki/
【アルゴリズム 選択ソート】ざっくりわかるシェルスクリプト11
https://suzukiiichiro.github.io/posts/2022-10-05-01-algorithm-selectionsort-suzuki/
【アルゴリズム バブルソート】ざっくりわかるシェルスクリプト10
https://suzukiiichiro.github.io/posts/2022-10-05-01-algorithm-bubblesort-suzuki/
【アルゴリズム ビッグオー】ざっくりわかるシェルスクリプト9
https://suzukiiichiro.github.io/posts/2022-10-04-01-algorithm-bigo-suzuki/
【アルゴリズム 2次元配列編】ざっくりわかるシェルスクリプト8
https://suzukiiichiro.github.io/posts/2022-10-03-01-algorithm-eval-array-suzuki/
【アルゴリズム 配列準備編】ざっくりわかるシェルスクリプト7
https://suzukiiichiro.github.io/posts/2022-10-03-01-algorithm-array-suzuki/
【アルゴリズム 配列編】ざっくりわかるシェルスクリプト6
https://suzukiiichiro.github.io/posts/2022-09-27-01-array-suzuki/
【grep/sed/awkも】ざっくりわかるシェルスクリプト5
https://suzukiiichiro.github.io/posts/2022-02-02-01-suzuki/
【grep特集】ざっくりわかるシェルスクリプト4
https://suzukiiichiro.github.io/posts/2022-01-24-01-suzuki/
【はじめから】ざっくりわかるシェルスクリプト3
https://suzukiiichiro.github.io/posts/2022-01-13-01-suzuki/
【はじめから】ざっくりわかるシェルスクリプト2
https://suzukiiichiro.github.io/posts/2022-01-12-01-suzuki/
【はじめから】ざっくりわかるシェルスクリプト1
https://suzukiiichiro.github.io/posts/2022-01-07-01-suzuki/

【TIPS】ざっくりわかるシェルスクリプト
https://suzukiiichiro.github.io/posts/2022-09-26-01-tips-suzuki/

書籍の紹介

【アルゴリズム 選択ソート】ざっくりわかるシェルスクリプト11

【アルゴリズム 選択ソート】ざっくりわかるシェルスクリプト11

【アルゴリズム ビッグオー】ざっくりわかるシェルスクリプト9

【アルゴリズム ビッグオー】ざっくりわかるシェルスクリプト9