アルゴリズムと計算量 -情報処理シンプルまとめ

アルゴリズムと計算量に関するブログのアイキャッチ画像 アルゴリズムとプログラミング

 基本情報技術者試験など情報処理技術者試験を受験する方にとっては必須の,アルゴリズムと計算量についてシンプルにまとめています。はじめに計算量について説明し,その後,整列(ソート)(基本交換法(隣接交換法,バブルソート),基本選択法(選択ソート),基本挿入法(挿入ソート),シェルソート(改良挿入法),クイックソート,マージソート,ヒープソート),探索(線形探索(番兵を使用した方法),2分探索,ハッシュ法(データの追加,データの探索)),文字列に対する処理(文字列の探索)ついて説明します。擬似言語,Python,C#の例を載せていますので,(時間があれば)実際にコーディングをしながら進めてみてください。

アルゴリズム

 アルゴリズムとは,ある問題を解く手順や計算方法のことをいいます。同じ処理結果が得られる場合でも,コンピューターに負荷をかけず,高速で,無駄なリソースを使わないアルゴリズムを採用することが重要です。

計算量

 アルゴリズムの計算量とは,処理内容の効率性を大雑把に評価するもので,O(オーダー)という単位を使用します。

※ ここでは,時間計算量(実行時間を対象とする計算量)について説明する

例)入力データn件,実行回数n回の場合の計算量:O(n)

文字列型の配列: x ← { "おはよう", "こんにちは", "こんばんは" }
整数型: n ← 配列xの要素数
for ( iを1からnまで1ずつ増やす )
 x[i]を出力
endfor

※ 1行目の実行回数:1回,2行目の実行回数:1回,3行目の実行回数:n回,4行目の実行回数:n回 ⇒ 全体の実行回数:2n+2回 ⇒ 計算量は,処理の効率性を大雑把に評価するものなので,O(n)となる

O(n)とは,処理件数nに計算量が比例していることを意味している

例)入力データm×n件,実行回数m×n回の場合の計算量:O(mn)

整数型の配列: k ← { {11, 12, 13, 14}, {21, 22, 23, 24}, {31, 32, 33, 34} }
整数型: m ← 配列kの行数
整数型: n ← 配列kの列数
for ( iを1からmまで1ずつ増やす )
 for ( jを1からnまで1ずつ増やす )
  k[i][j] の値をコンマ区切りで出力する
 endfor
 改行を出力する
endfor

※ 1行目の実行回数:1回,2行目の実行回数:1回,3行目の実行回数:1回,4行目の実行回数:m回,5行目の実行回数:m×n回,6行目の実行回数:m×n回,8行目の実行回数:m回 ⇒ 全体の実行回数:2mn+2m+3回 ⇒ 計算量は,処理の効率性を大雑把に評価するものなので,O(mn)となる

※ 入力データn2件,実行回数n2回の場合の計算量はO(n2)となる

入力データがn件で,

実行回数がn回の場合の計算量=O(n)

実行回数が3n+2回の場合の計算量=O(n)

実行回数が12n2+3n+5回の場合の計算量=O(n2)

実行回数がlog2n2の場合の計算量=O(log2n)

※ 計算量は,最高次の項以外は無視し,さらに,最高次の係数も無視して表す

※ ax=b ⇔ x=logab(aを底,bを真数,xをaを底とする対数という)

計算量の大小関係

 計算量の大小関係は,次のようになります。

O(1) < O(log2n) < O(n) < O(nlog2n) < O(n2) < O(2n) < O(n!)

計算量に関する説明画像

整列(ソート)

基本交換法(隣接交換法,バブルソート)

 基本交換法は,隣り合うデータの比較・交換を,範囲を狭めながら繰り返し行い,昇順(または降順)に並べ替えるアルゴリズムです。

※ 未整列データの中で最も小さい(または最も大きい)データが,泡のように(泡が浮き上がるように)移動するので,バブルソートとも呼ばれる

※ 計算量:O(n2)

例)配列aを昇順に整列する

基本交換法に関する説明画像

流れ図

基本交換法の流れ図に関する説明画像

※ 計算量(処理の効率性を大雑把に評価するもの)は,ループ1,ループ2の2重ループの部分で評価すればよい ⇒ 入力データn件の場合のループ2の実行回数:約n2回 ⇒ 全体の実行回数:約n2回となり,O(n2)となる

擬似言語

// 主プログラム
〇main()
 整数型の配列:a ← {30, 20, 40, 10}
 bubble_sort (a)

// 副プログラム
○ bubble_sort ( 整数型の配列:arr )
 整数型:temp
 for ( iをarrの長さ-1から1まで1ずつ減らす )
  for ( jを0からi-1まで1ずつ増やす )
   if ( arr[j] > arr[j + 1] )
    temp ← arr[j]
    arr[j] ← arr[j + 1]
    arr[j + 1] ← temp
   endif
  endfor
  endfor

Python

import numpy as np

# クラスSort
class Sort:
    def __init__(self):
       pass 
    
    def bubble_sort(self, arr: np.array):
        temp: int
        for i in range(len(arr) - 1, 0, -1):
            for j in range(0, i, 1):
                if arr[j] > arr[j + 1]:
                    temp = arr[j]
                    arr[j] = arr[j + 1]
                    arr[j + 1] = temp

# 主プログラム
def main():
    sort: Sort = Sort()
    a: np.array = np.array([30, 20, 40, 10])
    sort.bubble_sort (a)

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSort
class Sort {
    public Sort() {}

    public void bubble_sort(int[] arr) {
        int temp;
        for(int i = arr.Length - 1; i > 0; i--) {
            for(int j = 0; j < i; j++) {
                if(arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }
}

class a_001 {
    // 主プログラム
    static void Main(string[] args) {
        Sort sort = new Sort();
        int[] a = new int[]{30, 20, 40, 10};
        sort.bubble_sort (a);
    }
 }

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

基本交換法のプログラムの実行結果に関する説明画像

基本選択法(選択ソート)

 基本選択法は,未整列データの中で最も小さい(または最も大きい)データを,範囲を狭めながら繰り返し求め,昇順(または降順)に並べ替えるアルゴリズムです。

※ 計算量:O(n2)

例)配列aを昇順に整列する

基本選択法に関する説明画像

流れ図

基本選択法の流れ図に関する説明画像

※ 計算量(処理の効率性を大雑把に評価するもの)は,ループ1,ループ2の2重ループの部分で評価すればよい ⇒ 入力データn件の場合のループ2の実行回数:約n2回 ⇒ 全体の実行回数:約n2回となり,O(n2)となる

擬似言語

// 主プログラム
〇main()
 整数型の配列:a ← {30, 20, 40, 10}
  basic_selection_sort (a)

// 副プログラム
○ basic_selection_sort ( 整数型の配列:arr )
 整数型:min, temp
 for ( iを0からarrの長さ-2まで1ずつ増やす )
  min ← i
  for ( jをi+1からarrの長さ-1まで1ずつ増やす )
   if ( arr[min] > arr[j] )
    min = j
   endif
  endfor
  temp ← arr[i]
  arr[i] ← arr[min]
  arr[min] ← temp
  endfor

Python

import numpy as np

# クラスSort
class Sort:
    def __init__(self):
       pass 

    def basic_selection_sort(self, arr: np.array):
        min: int; temp: int
        for i in range(0, len(arr) - 1, 1):
            min = i
            for j in range(i + 1, len(arr), 1):
                if arr[min] > arr[j]:
                    min = j
            temp = arr[i]
            arr[i] = arr[min]
            arr[min] = temp

# 主プログラム
def main():
    sort: Sort = Sort()
    a: np.array = np.array([30, 20, 40, 10])
    sort.basic_selection_sort(a)

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSort
class Sort {
    public Sort() {}

    public void basic_selection_sort(int[] arr) {
        int min, temp;
        for(int i = 0; i < arr.Length - 1; i++) {
            min = i;
            for(int j = i + 1; j < arr.Length; j++){
                if(arr[min] > arr[j]) {
                    min = j;
                }
            }
            temp = arr[i];
            arr[i] = arr[min];
            arr[min] = temp;
        }
    }
}

class a_002 {
    // 主プログラム
    static void Main(string[] args) {
        Sort sort = new Sort();
        int[] a = new int[]{30, 20, 40, 10};
        sort.basic_selection_sort (a);
    }
 }

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

基本選択法のプログラムの実行結果に関する説明画像

基本挿入法(挿入ソート)

 基本挿入法は,未整列データから順にデータを取り出して,整列済みデータの正しい位置にデータを追加するという操作を繰り返し行い,昇順(または降順)に並べ替えるアルゴリズムです。

※ 計算量:O(n2)

例)配列aを昇順に整列する

基本挿入法に関する説明画像

流れ図

基本挿入法の流れ図に関する説明画像

※ 計算量(処理の効率性を大雑把に評価するもの)は,ループ1,ループ2の2重ループの部分で評価すればよい ⇒ 入力データn件の場合のループ2の実行回数:約n2回 ⇒ 全体の実行回数:約n2回となり,O(n2)となる

擬似言語

// 主プログラム
〇main()
 整数型の配列:a ← {30, 10, 40, 20}
  basic_insertion_sort (a)

// 副プログラム
○ basic_insertion_sort ( 整数型の配列:arr )
 整数型:j, temp
 for ( iを1からarrの長さ-1まで1ずつ増やす )
  j ← i
  while ( (j > 0) and (arr[j] < arr[j - 1]) )
   temp ← arr[j]
   arr[j] ← arr[j - 1]
   arr[j - 1] ← temp
   j ← j - 1
  endwhile
  endfor

Python

import numpy as np

# クラスSort
class Sort:
    def __init__(self):
       pass 

    def basic_insertion_sort(self, arr: np.array):
        temp: int; j: int
        for i in range(1, len(arr), 1):
            j = i
            while (j > 0) & (arr[j] < arr[j - 1]):
                temp = arr[j]
                arr[j] = arr[j - 1]
                arr[j - 1] = temp
                j = j - 1

# 主プログラム
def main():
    sort: Sort = Sort()
    a: np.array = np.array([30, 10, 40, 20])
    sort.basic_insertion_sort(a)

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSort
class Sort {
    public Sort() {}

    public void basic_insertion_sort(int[] arr) {
        int temp, j;
        for(int i = 1; i < arr.Length; i++) {
            j = i;
            while((j > 0) && (arr[j] < arr[j - 1])) {
                temp = arr[j];
                arr[j] = arr[j - 1];
                arr[j - 1] = temp;
                j = j - 1;
            }
        }
    }
}

class a_003 {
    // 主プログラム
    static void Main(string[] args) {
        Sort sort = new Sort();
        int[] a = new int[]{30, 10, 40, 20};
        sort.basic_insertion_sort (a);
    }
 }

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

基本選択法のプログラムの実行結果に関する説明画像

シェルソート(改良挿入法)

 シェルソートは,一定の間隔ごとに取り出したデータをそれぞれ並べ替え,間隔を狭めながら,間隔が1になるまで繰り返し,昇順(または降順)に並べ替えるアルゴリズムです。

※ 基本挿入法を改良したアルゴリズム

※ 計算量(平均):O(n1.5)程度,最悪の場合:O(n2)

例)配列aを昇順に整列する

シェルソートに関する説明画像

流れ図

シェルソートの流れ図に関する説明画像

擬似言語

// 主プログラム
〇main()
 整数型の配列:a ← {20, 40, 10, 30, 15}
  shell_sort (a)

// 副プログラム
○ shell_sort ( 整数型の配列:arr )
 整数型:w ← arrの長さ ÷ 2
 整数型:i, j, r_cnt, temp
 while ( w ≧ 1 )
  i ← 0
  while ( i < w )
   j ← 1
   while ( i + j × w < arrの長さ )
    r_cnt ← 0
    while ( ( j > 0 ) and ( arr[i + j × w] < arr[i + (j - 1) × w)] ) )
     temp ← arr[i + j × w]
     arr[i + j × w] ← arr[i + (j - 1) × w]
     arr[i + (j - 1) × w] ← temp
     j ← j - 1
     r_cnt ← r_cnt + 1
    endwhile
    if ( r_cnt ≠ 0 )
     j ← j + r_cnt + 1
    else
     j ← j + 1
    endif
   endwhile
   i ← i + 1
  endwhile
  w ← w ÷ 2
 endwhile

Python

import numpy as np

# クラスSort
class Sort:
    def __init__(self):
       pass 

    def shell_sort (self, arr: np.array):
        w: int = int(len(arr) / 2)
        i: int; j: int; r_cnt: int; temp: int
        while(w >= 1):
            i = 0
            while(i < w):
                j = 1
                while(i + j * w < len(arr)):
                    r_cnt = 0
                    while (j > 0) and (arr[i + (j - 1) * w] >= arr[i + j * w]):
                        temp = arr[i + j * w]
                        arr[i + j * w] = arr[i + (j - 1) * w]
                        arr[i + (j - 1) * w] = temp
                        j = j - 1
                        r_cnt +=1
                    if r_cnt != 0:
                        j = j + r_cnt + 1
                    else:
                        j = j + 1
                i = i + 1
            w = int(w / 2)
    
# 主プログラム
def main():
    sort: Sort = Sort()
    a: np.array = np.array([20, 40, 10, 30, 15])
    sort.shell_sort(a)

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSort
class Sort {
    public Sort() {}

 public void shell_sort (int[] arr) {
        int w = arr.Length / 2;
        int i, j, r_cnt, temp;
        while(w >= 1) {
            i = 0;
            while(i < w) {
                j = 1;
                while(i + j * w < arr.Length) {
                    r_cnt = 0;
                    while((j > 0) && (arr[i + j * w] < arr[i + (j - 1) * w])) {
                        temp = arr[i + j * w];
                        arr[i + j * w] = arr[i + (j - 1) * w];
                        arr[i + (j - 1) * w] = temp;
                        j = j - 1;
                        r_cnt++;
                    }
                    if(r_cnt != 0){
                        j = j + r_cnt + 1;
                    } else {
                        j = j + 1;
                    }
                }
                i = i + 1;
            }
            w = w / 2;
        }
    }
}

class a_004 {
    // 主プログラム
    static void Main(string[] args) {
        Sort sort = new Sort();
        int[] a = new int[]{20, 40, 10, 30, 15}; 
        sort.shell_sort(a);
    }
 }

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

シェルソートのプログラムの実行結果に関する説明画像

クイックソート

 クイックソートは,データの中で基準値(ピボット)を選択し,その基準値よりも値の小さいグループと大きいグルーに分け,さらに,それぞれのグループの中で基準値を選択し、その基準値よりも値の小さいグループと大きいグループに分け…,という操作を繰り返し行い,昇順(または降順)に並べ替えるアルゴリズムです。

※ 計算量(平均):O(n log2n),最悪の場合:O(n2)

例)配列aを昇順に整列する

クイックソートに関する説明画像

流れ図

クイックソートの流れ図に関する説明画像

擬似言語

// 主プログラム
〇main()
 整数型の配列:a ← {30, 50, 40, 20, 10, 70, 15}
  quick_sort (a, 0, aの長さ-1)

// 副プログラム
○ quick_sort ( 整数型の配列:arr, 整数型:first, 整数型:last)
 整数型:i ← first
 整数型:j ← last
 整数型:p ← arr[(i + j) / 2]
 整数型:temp
 while ( true )
  while ( arr[i] < p )
   i ← i + 1
  endwhile
  while ( arr[j] > p )
   j ← j - 1
  endwhile
  if ( i ≧ j )
   break
  endif
  temp ← arr[i]
  arr[i] ← arr[j]
  arr[j] ← temp
  i ← i + 1
  j ← j - 1
 endwhile
 if ( first < i - 1 )
  quick_sort (arr, first, i - 1 )
 endif
 if ( last > j + 1 )
  quick_sort (arr, j + 1, last )
 endif

Python

import numpy as np

# クラスSort
class Sort:
    def __init__(self):
       pass 

    def quick_sort(self, arr: np.array, first:int, last: int):
        i: int = first
        j: int = last
        p: int = arr[int((i + j) / 2)]
        temp: int
        while True:
            while arr[i] < p:
                i += 1
            while arr[j] > p:
                j -= 1
            if i >= j:
                break
            temp = arr[i]
            arr[i] = arr[j]
            arr[j] = temp
            i += 1
            j -= 1
        if first < i - 1:
            self.quick_sort(arr, first, i - 1)
        if last > j + 1:
            self.quick_sort(arr, j + 1, last)
    
# 主プログラム
def main():
    sort: Sort = Sort()
    a: np.array = np.array([30, 50, 40, 20, 10, 70, 15])
    sort.quick_sort(a, 0, len(a) - 1)

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSort
class Sort {
    public Sort() {}

 public void quick_sort(int[] arr, int first, int last) {
        int i = first;
        int j = last;
        int p = arr[(i + j) / 2];
        int temp;
        while(true) {
            while(arr[i] < p) {
                i++;
            }
            while(arr[j] > p) {
                j--;
            }
            if(i >= j) {
                break;
            }
            temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
            i++;
            j--;
        }
        if(first < i - 1) {
            quick_sort(arr, first, i - 1);
        }
        if(last > j + 1) {
            quick_sort(arr, j + 1, last);
        }
    }
}

class a_005 {
    // 主プログラム
    static void Main(string[] args) {
        Sort sort = new Sort();
        int[] a = new int[]{30, 50, 40, 20, 10, 70, 15}; 
        sort.quick_sort(a, 0, a.Length - 1);
    }
 }

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

クイックソートのプログラムの実行結果に関する説明画像

マージソート

 マージソートは,データ列を繰り返し分割して,それぞれを並べ替えては併合するという操作を繰り返し行い,昇順(または降順)に並べ替えるアルゴリズムです。

※ 計算量:O(n log2n)

例)配列aを昇順に整列する

マージソートに関する説明画像

流れ図

マージソートの流れ図に関する説明画像

擬似言語

// 主プログラム
〇main()
 整数型の配列:a ← {30, 50, 20, 10, 40, 70, 15}
  merge_sort ( a, aの長さ )

// 副プログラム
○ merge_sort ( 整数型の配列:arr, 整数型:num )
 整数型の配列:arr1, arr2
 整数型:num1, num2
 if ( num > 1 )
  num1 ← num ÷ 2
  num2 ← num - num1
  for ( iを0からnum1-1まで1ずつ増やす )
   arr1[i] ← arr[i]
  endfor
  for ( iを0からnum2-1まで1ずつ増やす )
   arr2[i] ← arr[num1 + i]
  endfor
  merge_sort ( arr1, num1 )
  merge_sort ( arr2, num2 )
  merge ( arr1, num1, arr2, num2, arr )
 endif

○ merge ( 整数型の配列:arr1, 整数型:num1, 整数型の配列:arr2, 整数型:num2, 整数型の配列:arr )
 整数型:i, j
 i ← 0
 j ← 0
 while (( i < num1) and ( j < num2 ))
  if ( arr1[i] < arr2[j] )
   arr[i + j] ← arr1[i]
   i ← i + 1
  eise
   arr[i + j] ← arr2[j]
   j ← j + 1
  endif
 endwhile
 while (( i < num1) or ( j < num2 ))
  if ( i < num1 )
   arr[i + j] ← arr1[i]
   i ← i + 1
  eiseif ()
   arr[i + j] ← arr2[j]
   j ← j + 1
  endif
 endwhile

Python

import numpy as np

# クラスSort
class Sort:
    def __init__(self):
       pass 

    def merge_sort(self, arr: np.array, num:int):
        arr1: np.array; arr2: np.array
        num1: int; num2: int
        if num > 1:
            num1 = int(num / 2)
            num2 = num - num1
            arr1 = np.zeros(num1, dtype=np.int32)
            arr2 = np.zeros(num2, dtype=np.int32)
            for i in range(0, num1, 1):
                arr1[i] = arr[i]
            for i in range(0, num2, 1):
                arr2[i] = arr[num1 + i]
            self.merge_sort(arr1, num1)
            self.merge_sort(arr2, num2)
            self.merge(arr1, num1, arr2, num2, arr)

    def merge(self, arr1: np.array, num1:int, arr2: np.array, num2: int, arr: np.array):
        i: int = 0; j: int = 0
        while (i < num1) and (j < num2):
            if arr1[i] < arr2[j]:
                arr[i + j] = arr1[i]
                i += 1
            else:
                arr[i + j] = arr2[j]
                j += 1
        while (i < num1) or (j < num2):
            if i < num1:
                arr[i + j] = arr1[i]
                i += 1
            else:
                arr[i + j] = arr2[j]
                j += 1

# 主プログラム
def main():
    sort: Sort = Sort()
    a: np.array = np.array([30, 50, 20, 10, 40, 70, 15])
    sort.merge_sort(a, len(a))
    print('ソート後:', a)

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSort
class Sort {
    public Sort() {}

    public void merge_sort(int[] arr, int num) {
        int[] arr1, arr2;
        int num1, num2;
        if(num > 1) {
            num1 = num / 2;
            num2 = num - num1;
            arr1 = new int[num1];
            arr2 = new int[num2];
            for(int i = 0; i < num1; i++) {
                arr1[i] = arr[i];
            }
            for(int i = 0; i < num2; i++) {
                arr2[i] = arr[num1 + i];
            }
            merge_sort(arr1, num1);
            merge_sort(arr2, num2);
            merge(arr1, num1, arr2, num2, arr);
            Console.WriteLine("ソート:{0}", String.Join(",", arr));
        }
    }

    void merge(int[] arr1, int num1, int[] arr2, int num2, int[] arr) {
        int i = 0, j = 0;
        while((i < num1) && (j < num2)) {
            if(arr1[i] < arr2[j]) {
                arr[i + j] = arr1[i];
                i += 1;
            } else {
                arr[i + j] = arr2[j];
                j += 1;
            }
        }
        while((i < num1) || (j < num2)) {
            if(i < num1) {
                arr[i + j] = arr1[i];
                i += 1;
            } else {
                arr[i + j] = arr2[j];
                j += 1;
            }
        }
    }
}

class a_006 {
    // 主プログラム
    static void Main(string[] args) {
        Sort sort = new Sort();
        int[] a = new int[]{30, 50, 20, 10, 40, 70, 15}; 
        sort. merge_sort(a, a.Length);
    }
}

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

マージソートのプログラムの実行結果に関する説明画像

ヒープソート

 ヒープソートは,未整列データからヒープを作成して,昇順(または降順)に並べ替えるアルゴリズムです。

※ ヒープ…親ノード≧子ノード(または,子ノード≧親ノード)という性質を持つ2分木

※ 計算量:O(n log2n)

例)配列aを昇順に整列する

ヒープソートに関する説明画像01
ヒープソートに関する説明画像02

※ ① ヒープの作成,② 木の根と最後の節の値を交換,③ ヒープの再構成,④ ②,③を,③の結果が木の根になるまで繰り返す

流れ図

ヒープソートの流れ図に関する説明画像

擬似言語

// 主プログラム
〇main()
 整数型の配列:a ← {20, 40, 30, 50, 10}
 整数型の配列:h
  heap_sort ( a, h, aの長さ )

// 副プログラム
○ heap_sort ( 整数型の配列:arr, 整数型の配列:heap, 整数型:num )
 make_heap ( arr, heap, num )
 for ( iをnum-1から1まで1ずつ減らす)
  swap ( heap, 0, i )
  remake_heap ( heap, i - 1 )
 endfor

○ make_heap ( 整数型の配列:arr, 整数型の配列:heap, 整数型:num )
 整数型:k
 for ( iを0からnum-1まで1ずつ増やす )
  heap[i] ← arr[i]
  k ← i
  while ( k > 0 )
   if ( heap[k] > heap[parent ( k )] )
    swap ( heap, k, parent ( k ) )
    k ← parent ( k )
   else
    break
   endif
  endwhile
 endfor

○ remake_heap ( 整数型の配列:heap, 整数型:last )
 整数型:n, temp
 n ← 0
 while ( lchild ( n ) ≦ last )
  temp ← lchild ( n )
  if ( rchild ( n ) ≦ last )
   if ( heap[temp] ≦ heap[rchild ( n )] )
    temp ← rchild ( n )
   endif
  endif
  if ( heap[temp] > heap[n] )
   swap ( heap, n, temp )
  else
   return
  endif
  n ← temp
 endwhile

○ swap ( 整数型の配列:heap, 整数型:i, 整数型:j )
 整数型:temp
 temp ← heap[i]
 heap[i] ← heap[j]
 heap[j] ← temp

○ parent (整数型:i )
 ( i - 1 ) ÷ 2 を返す

○ lchild ( 整数型:i )
 2 × i + 1 を返す

○ rchild (整数型:i )
 2 × i + 2 を返す

Python

import numpy as np

# クラスSort
class Sort:
    def __init__(self):
       pass 

    def heap_sort(self, arr: np.array, heap: np.array, num: int):
        self.make_heap(arr, heap, num)
        for i in range(num - 1, 0, -1):
            self.swap(heap, 0, i)
            self. remake_heap(heap, i - 1)

    def make_heap(self, arr: np.array, heap: np.array, num: int):
        k: int
        for i in range(0, num, 1):
            heap[i] = arr[i]
            k = i
            while k > 0:
                if heap[k] > heap[self.parent(k)]:
                    self.swap(heap, k, self.parent(k))
                    k = self.parent(k)
                else:
                    break

    def remake_heap(self, heap: np.array, last: int):
        n: int; temp: int
        n = 0
        while self.lchild(n) <= last:
            temp = self.lchild(n)
            if self.rchild(n) <= last:
                if heap[temp] <= heap[self.rchild(n)]:
                    temp = self.rchild(n)
            if heap[temp] > heap[n]:
                self.swap(heap, n, temp)
            else:
                return
            n = temp

    def swap(self, heap: np.array, i: int,j: int):
        temp: int
        temp = heap[i]
        heap[i] = heap[j]
        heap[j] = temp

    def parent(self, i: int):
        return int((i - 1) / 2)

    def lchild(self, i: int):
        return int(2 * i + 1)
    
    def rchild(self, i: int):
        return int(2 * i + 2)
        
# 主プログラム
def main():
    sort: Sort = Sort()
    a: np.array = np.array([20, 40, 30, 50, 10])
    h: np.array = np.array([0, 0, 0, 0, 0])
    sort.heap_sort(a, h, len(a))

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSort
using System.ComponentModel;

class Sort {
    public Sort() {}

    public void heap_sort(int[] arr, int[] heap, int num) {
        make_heap(arr, heap, num);
        for(int i = num - 1; i > 0; i--) {
            swap(heap, 0, i);
            remake_heap(heap, i - 1);
        }
    }

    void make_heap(int[] arr, int[] heap, int num) {
        int k;
        for(int i = 0; i < num; i++) {
            heap[i] = arr[i];
            k = i;
            while(k > 0) {
                if(heap[k] > heap[parent(k)]) {
                    swap(heap, k, parent(k));
                    k = parent(k);
                } else {
                    break;
                }
            }
        }
    }

    void remake_heap(int[] heap, int last) {
        int n, temp;
        n = 0;
        while(lchild(n) <= last) {
            temp = lchild(n);
            if(rchild(n) <= last) {
                if(heap[temp] <= heap[rchild(n)]) {
                    temp = rchild(n);
                }
            }
            if(heap[temp] > heap[n]) {
                swap(heap, n, temp);
            } else {
                return;
            }
            n = temp;
        }
    }

    void swap(int[] heap, int i, int j) {
        int temp;
        temp = heap[i];
        heap[i] = heap[j];
        heap[j] = temp;
    }

    int parent(int i) {
        return (i - 1) / 2;
    }

    int lchild(int i) {
        return 2 * i + 1;
    }

    int rchild(int i) {
        return 2 * i + 2;
    }
}

class a_007 {
    // 主プログラム
    static void Main(string[] args) {
        Sort sort = new Sort();
        int[] a = new int[]{20, 40, 30, 50, 10}; 
        int[] h = new int[a.Length];
        sort.heap_sort(a, h, a.Length);
    }
}

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

ヒープソートのプログラムの実行結果に関する説明画像

探索

線形探索

 線形探索は,探索データと探索対象の配列のデータとの比較を先頭から順に行い探索するアルゴリズムです。

※ 計算量:O(n)

例)配列aの中に20があるかを探索し,見つかった場合はデータの位置を示す添字を,見つからなかった場合は-1を返す

線形探索に関する説明画像

流れ図

線形探索の流れ図に関する説明画像

※ 計算量(処理の効率性を大雑把に評価するもの)は,ループ1の部分で評価すればよいので,入力データn件の場合のループ1の実行回数:約n回 ⇒ 全体の実行回数:n回となり,O(n)となる

擬似言語

// 主プログラム
〇 main()
 整数型の配列:a ← {30, 50, 40, 20, 10}
 sequential_search( 20, a )

// 副プログラム
○ sequential_search( k, arr )
 整数型:i ← 0
 while ( i < arrの長さ and arr[i] ≠ k )
  i ← i + 1
 endwhile
 if ( i < arrの長さ )
  return i
 else
  return -1
 endif

Python

import numpy as np

# クラスSort
class Search:
    def __init__(self):
       pass 

    def sequential_search(self, k: int, arr: np.array) -> int:
        i: int = 0
        while i < len(arr) and arr[i] != k:
            i += 1
        if i < len(arr):            
            return i
        else:
            return -1

# 主プログラム
def main():
    sort: Search = Search()
    a: np.array = np.array([30, 50, 40, 20, 10])
    r: int = sort.sequential_search(20, a)
    if r != -1:
        print('探索結果:添字:', r, ',値:', a[r])
    else:
        print('探索結果:探索データはありませんでした。')

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSort
class Search {
    public Search() {}

    public int sequential_search(int k, int[] arr) {
        int i = 0;
        while(i < arr.Length && arr[i] != k) {
            i += 1;
        }
        if(i < arr.Length) {
            return i;
        } else {
            return -1;
        }
    }
}

class b_001 {
    // 主プログラム
    static void Main(string[] args) {
        Search search = new Search();
        int[] a = new int[]{30, 50, 40, 20, 10};
        int r = search.sequential_search(20, a);
        if(r != -1) {
            Console.WriteLine("探索結果:添字:{0},値:{1}", r, a[r]);
        } else {
            Console.WriteLine("探索結果:探索データはありませんでした。");
        }
    }
 }

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

線形探索のプログラムの実行結果に関する説明画像
番兵を使用した方法

 線形探索の効率を上げるために,番兵を使用する方法があります。

※ 番兵を使用すると,ループのたびに「すべてのデータと比較したかを確認」する必要がないので,探索の効率を上げることができる

例)配列aの中に70があるかを探索し,見つかった場合はデータの位置を示す添字を,見つからなかった場合は-1を返す

番兵を使用した線形探索に関する説明画像

流れ図

番兵を使用した線形探索の流れ図に関する説明画像

※ 計算量(処理の効率性を大雑把に評価するもの)は,ループ1の部分で評価すればよいので,入力データn件の場合のループ1の実行回数:約n回 ⇒ 全体の実行回数:n回となり,O(n)となる

擬似言語

// 主プログラム
〇 main()
 整数型の配列:a ← {30, 50, 40, 20, 10}
 sequential_search1 ( 70, a )

// 副プログラム
○ sequential_search1 ( k, arr )
 arrの長さを1増やす
 arr[arrの長さ - 1] ← k
 整数型:i ← 0
 while ( arr[i] ≠ k )
  i ← i + 1
 endwhile
 if ( i < arrの長さ - 1 )
  return i
 else
  return -1
 endif

Python

import numpy as np

# クラスSort
class Search:
    def __init__(self):
       pass 

    def sequential_search1(self, k: int, arr: np.array) -> int:
        arr = np.append(arr, k)
        i: int = 0
        while arr[i] != k:
            i += 1
        if i < len(arr) - 1:
            return i
        else:
            return -1

# 主プログラム
def main():
    sort: Search = Search()
    a: np.array = np.array([30, 50, 40, 20, 10])
    r: int = sort.sequential_search1(20, a)
    if r != -1:
        print('探索結果:添字:', r, ',値:', a[r])
    else:
        print('探索結果:探索データはありませんでした。')

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSort
class Search {
    public Search() {}

    public int sequential_search1(int k, int[] arr) {
        Array.Resize(ref arr, arr.Length + 1);
        arr[arr.Length - 1] = k;
        int i = 0;
        while(arr[i] != k) {
            i += 1;
        }
        if(i < arr.Length - 1) {
            return i;
        } else {
            return -1;
        }
    }
}

class b_001 {
    // 主プログラム
    static void Main(string[] args) {
        Search search = new Search();
        int[] a = new int[]{30, 50, 40, 20, 10};
        int r = search.sequential_search1(20, a);
        if(r != -1) {
            Console.WriteLine("探索結果:添字:{0},値:{1}", r, a[r]);
        } else {
            Console.WriteLine("探索結果:探索データはありませんでした。");
        }
    }
 }

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

番兵を使用した線形探索のプログラムの実行結果に関する説明画像

 2分探索

 2分探索は,整列済みデータの中から探索データを探索する場合に有効なアルゴリズムです。探索データと整列済みデータの中心に位置するデータを比較し,整列済みデータの前半分か後半分を次の探索範囲として,これを繰り返すことによりデータを探索します。

※ 計算量:O(log2n)

例)配列aの中に70があるかを探索し,見つかった場合はデータの位置を示す添字を,見つからなかった場合は-1を返す

2分探索に関する説明画像

流れ図

2分探索の流れ図に関する説明画像

※ 計算量(処理の効率性を大雑把に評価するもの)は,入力データn件の場合のループ1の部分で評価すればよいので,ループ1の実行回数:約log2n回 ⇒ 全体の実行回数:log2n回となり,O(log2n)となる

x=log2nn=2x。データ件数が16件の場合,4=log216=log224(⇔ 16=24)となり,探索(分割)回数は最大で4回となる

擬似言語

// 主プログラム
〇 main()
 整数型の配列:a ← {10, 15, 20, 30, 40, 50, 70, 85}
 binary_search ( 70, a )

// 副プログラム
○ binary_search ( k, arr )
 整数型:l ← 0
 整数型:h ← arrの長さ-1
 整数型:m ← (l + h) / 2
 while ( l ≦ h and arr[m] ≠ k )
  if ( arr[m] < k )
   l ← m + 1
  else
   h ← m - 1
  endif
  m ← (l + h) / 2
 endwhile
 if ( l ≦ h )
  return m
 else
  return -1
 endif

Python

import numpy as np

# クラスSort
class Search:
    def __init__(self):
       pass 

    def binary_search(self, k: int, arr: np.array) -> int:
        l: int = 0
        h: int = len(arr) - 1
        m: int = int((l + h) / 2)
        while l <= h and arr[m] != k:
            if arr[m] < k:
                l = m + 1
            else:
                h = m - 1
            m = int((l + h) / 2)
        if l <= h:
            return m
        else:
            return -1

# 主プログラム
def main():
    sort: Search = Search()
    a: np.array = np.array([10, 15, 20, 30, 40, 50, 70, 85])
    r: int = sort.binary_search(70, a)
    if r != -1:
        print('探索結果:添字:', r, ',値:', a[r])
    else:
        print('探索結果:探索データはありませんでした。')

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSort
class Search {
    public Search() {}

    public int binary_search(int k, int[] arr) {
        int l = 0;
        int h = arr.Length - 1;
        int m = (l + h) / 2;
        while(l <= h && arr[m] != k) {
            if(arr[m] < k){
                l = m + 1;
            } else {
                h = m - 1;
            }
            m = (l + h) / 2;
        }
        if(l <= h) {
            return m;
        } else {
            return -1;
        }
    }
}

class b_002 {
    // 主プログラム
    static void Main(string[] args) {
        Search search = new Search();
        int[] a = new int[]{10, 15, 20, 30, 40, 50, 70, 85};
        int r = search.binary_search(70, a);
        if(r != -1) {
            Console.WriteLine("探索結果:添字:{0},値:{1}", r, a[r]);
        } else {
            Console.WriteLine("探索結果:探索データはありませんでした。");
        }
    }
 }

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

2分探索のプログラムの実行結果に関する説明画像

ハッシュ法

 ハッシュ法は,直接探索が可能なアルゴリズムです。データのキー値をハッシュ関数に通して得られるハッシュ値によりデータの格納位置が決まりますので,探索時には同じ計算により格納位置を見つけることができます。ただし,異なるキー値から同じハッシュ値が得られる場合があります(衝突(シノニム))。

※ 計算量:O(1)

データの追加
ハッシュ探索のデータの追加に関する説明画像
データの探索
ハッシュ探索に関する説明画像

文字列に対する処理

文字列の探索

 文字列の探索に関しては,まず,探索する文字列の1文字目と同じ文字を探索して,一致した場合に2文字目以降も一致するかを順に調べたりします。

例)配列aの中に「ひかり」という文字列があるかを探索し,見つかった場合はデータの1文字目の位置を示す添字を,見つからなかった場合は-1を返す

文字列の探索に関する説明画像

流れ図

文字列の探索の流れ図に関する説明画像

擬似言語

// 主プログラム
〇 main()
 文字列型:a ← {かひとひかあぜひかりす}
 文字列型:b ← {ひかり]
 search_str(a, b)

// 副プログラム
○ search_str (文字列型:arr1, 文字列型:arr2)
 整数型:i ← 0
 整数型:j ← 0
 while ( i < arr1の長さ and j < arr2の長さ )
  if ( arr1[i] ≠ arr2[j] )
   j ← 0
  else
   j ← j + 1
  endif
  i ← i + 1
 endwhile
 if ( j > arr2の長さ - 1 )
  return i - arr2.Length
 else
  return -1
 endif

Python

import numpy as np

# クラスSearch
class Search:
    def __init__(self):
       pass 

    def search_str(self, arr1: str, arr2: str) ->int:
        i: int = 0; j: int = 0
        while i < len(arr1) and j < len(arr2):
            if arr1[i] != arr2[j]:
                j = 0
            else:
                j += 1
            i += 1
        if j > len(arr2) - 1:
            return i - len(arr2)
        else:
            return -1

# 主プログラム
def main():
    a: str = 'かひとひかあぜひかりす'
    b: str = 'ひかり'
    r: int = 0
    hash: Search = Search()
    r = hash.search_str(a, b)
    print(r)

if __name__ == "__main__":
    main()

※ Pythonプログラムの作成と実行については,「Pythonプログラムの作成と実行 -情報処理シンプルまとめ」を参照

C#

// クラスSearch
class Search {
    public Search() {}

    public int search_str(string arr1, string arr2) {
        int i = 0, j = 0;
        while(i < arr1.Length && j < arr2.Length) {
            if(arr1[i] != arr2[j]) {
                j = 0;
            } else {
                j += 1;
            }
            i += 1;
        }
        if(j > arr2.Length - 1) {
            return i - arr2.Length;
        } else {
            return -1;
        }
    }
}

class c_001 {
    // 主プログラム
    static void Main(string[] args) {
        string a = "かひとひかあぜひかりす";
        string b = "ひかり";
        int r = 0;
        Search hash = new Search();
        r = hash.search_str(a, b);
        Console.WriteLine(r);
    }
 }

※ C#プログラムの作成と実行については,「C#プログラムの作成と実行 -情報処理シンプルまとめ」を参照

実行結果

文字列の探索のプログラムの実行結果に関する説明画像

まとめ

 今回は,アルゴリズムと計算量について,シンプルにまとめてみました。各アルゴリズムについては,丁寧にトレースすると理解が深まると思います。最初は難しいかもしれませんが,慣れだと思います。根気よく頑張りましょう。