LoginSignup
2
2

【Fortran】Fortranでクイックソートを実装する

Last updated at Posted at 2023-09-29

目次

概要

クイックソートとは、データを昇順降順に並び替えるアルゴリズムの1つです。

例えば、
99, 3, 4, 7, 55, 1, 6, 2という順列を与えると、
1, 2, 3, 4, 6, 7, 55, 99 99, 55, 7, 6, 4, 3, 2, 1が求められます。

データを並び替える機能はCやPythonでは、qsort関数sort関数によって標準で搭載されています。

Fortranには同様の機能は標準で搭載されていませんので、クイックソートのプログラムを自分で書いて実装してみます。

クイックソートには`安定の保証がないデメリットがあります。
今回は単純な数列の並び替えを想定しますので、解説の対象外とします。
今後ソート関係の記事を書き終えたら個別に記事を作成して解説します。
[記事ができたらここにURLを入れる。]

アルゴリズム

アルゴリズムについての詳細な説明は既に多くのWebサイトで解説されていますので、本記事では説明を省略します。
参考として、以下のWebサイトのリンクを記載します。

プログラム例

99 3 4 7 55 1 6 2をクイックソートのアルゴリズムにより、昇順に並び替えてみます。

実行結果

途中過程を含めたクイックソートによる並び替えの過程を以下に示します。

【実行結果】長いので畳んでいます。 
元の順番 >>
99  3  4  7 55  1  6  2

【99  3  4  7 55  1  6  2 】を対象に調べます。
   STEP:1
          中央値: 7
   LOOP
     STEP:2
          【99】 から前進して【 7】より”小さい”かチェック
               NG : 99 >  7
     STEP:3
          【 2】 から後進して【 7】より”大きい”かチェック
               NG :  7 >  2
     STEP:4
          99 と  2 を入れ替えます。
               before : 99  3  4  7 55  1  6  2
               after  :  2  3  4  7 55  1  6 99
   LOOP
     STEP:2
          【 3】 から前進して【 7】より”小さい”かチェック
               OK :  3 <  7
               OK :  4 <  7
               NG :  7 >  7
     STEP:3
          【 6】 から後進して【 7】より”大きい”かチェック
               NG :  7 >  6
     STEP:4
           7 と  6 を入れ替えます。
               before :  2  3  4  7 55  1  6 99
               after  :  2  3  4  6 55  1  7 99
   LOOP
     STEP:2
          【55】 から前進して【 7】より”小さい”かチェック
               NG : 55 >  7
     STEP:3
          【 1】 から後進して【 7】より”大きい”かチェック
               NG :  7 >  1
     STEP:4
          55 と  1 を入れ替えます。
               before :  2  3  4  6 55  1  7 99
               after  :  2  3  4  6  1 55  7 99
   LOOP
     STEP:2
          【55】 から前進して【 7】より”小さい”かチェック
               NG : 55 >  7
     STEP:3
          【 1】 から後進して【 7】より”大きい”かチェック
               NG :  7 >  1
     GOAL
          分類結果(iとjがすれ違っているため終了)
           6           5
               前半部分: 2  3  4  6  1
               後半結果:55  7 99

【 2  3  4  6  1 】を対象に調べます。
     STEP:1
          中央値: 4
   LOOP
     STEP:2
          【 2】 から前進して【 4】より”小さい”かチェック
               OK :  2 <  4
               OK :  3 <  4
               NG :  4 >  4
     STEP:3
          【 1】 から後進して【 4】より”大きい”かチェック
               NG :  4 >  1
     STEP:4
           4 と  1 を入れ替えます。
               before :  2  3  4  6  1
               after  :  2  3  1  6  4
   LOOP
     STEP:2
          s【 6】 から前進して【 4】より”小さい”かチェック
               NG :  6 >  4
     STEP:3
          【 6】 から後進して【 4】より”大きい”かチェック
               OK :  4 <  6
               NG :  4 >  1
     GOAL
          分類結果(iとjがすれ違っているため終了)
           4           3
               前半部分: 2  3  1
               後半結果: 6  4

【 2  3  1 】を対象に調べます。
     STEP:1
          中央値: 3
   LOOP
     STEP:2
          【 2】 から前進して【 3】より”小さい”かチェック
               OK :  2 <  3
               NG :  3 >  3
     STEP:3
          【 1】 から後進して【 3】より”大きい”かチェック
               NG :  3 >  1
     STEP:4
           3 と  1 を入れ替えます。
               before :  2  3  1
               after  :  2  1  3
   LOOP
     STEP:2
          【 3】 から前進して【 3】より”小さい”かチェック
               NG :  3 >  3
     STEP:3
          【 1】 から後進して【 3】より”大きい”かチェック
               NG :  3 >  1
     GOAL
          分類結果(iとjがすれ違っているため終了)
           3           2
               前半部分: 2  1
               後半結果: 3

【 2  1 】を対象に調べます。
     STEP:1
          中央値: 2
   LOOP
     STEP:2
          【 2】 から前進して【 2】より”小さい”かチェック
               NG :  2 >  2
     STEP:3
          【 1】 から後進して【 2】より”大きい”かチェック
               NG :  2 >  1
     STEP:4
           2 と  1 を入れ替えます。
               before :  2  1
               after  :  1  2
   LOOP
     STEP:2
          【 2】 から前進して【 2】より”小さい”かチェック
               NG :  2 >  2
     STEP:3
          【 1】 から後進して【 2】より”大きい”かチェック
               NG :  2 >  1
     GOAL
          分類結果(iとjがすれ違っているため終了)
           2           1
               前半部分: 1
               後半結果: 2

【 6  4 】を対象に調べます。
     STEP:1
          中央値: 6
   LOOP
     STEP:2
          【 6】 から前進して【 6】より”小さい”かチェック
               NG :  6 >  6
     STEP:3
          【 4】 から後進して【 6】より”大きい”かチェック
               NG :  6 >  4
     STEP:4
           6 と  4 を入れ替えます。
               before :  1  2  3  6  4
               after  :  1  2  3  4  6
   LOOP
     STEP:2
          【 6】 から前進して【 6】より”小さい”かチェック
               NG :  6 >  6
     STEP:3
          【 4】 から後進して【 6】より”大きい”かチェック
               NG :  6 >  4
     GOAL
          分類結果(iとjがすれ違っているため終了)
           5           4
               前半部分: 4
               後半結果: 6

【55  7 99 】を対象に調べます。
     STEP:1
          中央値: 7
   LOOP
     STEP:2
          【55】 から前進して【 7】より”小さい”かチェック
               NG : 55 >  7
     STEP:3
          【99】 から後進して【 7】より”大きい”かチェック
               OK :  7 < 99
               NG :  7 >  7
     STEP:4
          55 と  7 を入れ替えます。
               before :  1  2  3  4  6 55  7 99
               after  :  1  2  3  4  6  7 55 99
   LOOP
     STEP:2
          【55】 から前進して【 7】より”小さい”かチェック
               NG : 55 >  7
     STEP:3
          【 7】 から後進して【 7】より”大きい”かチェック
               NG :  7 >  7
     GOAL
          分類結果(iとjがすれ違っているため終了)
           7           6
               前半部分: 7
               後半結果:55 99

【55 99 】を対象に調べます。
     STEP:1
          中央値:55
   LOOP
     STEP:2
          【55】 から前進して【55】より”小さい”かチェック
               NG : 55 > 55
     STEP:3
          【99】 から後進して【55】より”大きい”かチェック
               OK : 55 < 99
               NG : 55 > 55
     GOAL
          分類結果(iとjがすれ違っているため終了)
           7           7
               前半部分:55
               後半結果:99
並び替え後 >>
 1  2  3  4  6  7 55 99

サンプルプログラム

module global_variable
    implicit none
    ! グローバル変数
    integer(4) num(8)
    integer(4) N
    data num/99, 3, 4, 7, 55, 1, 6, 2/
end module
program ABC306c
    use global_variable
    N = size(num)
    !入力
    !並び替え前
    write (*, '(a)') '元の順番 >>'
    write (*, '(*(i2,1x))') num

    !並び替え
    call quicksort(num, 1, N)

    !結果の出力
    write (*, '(a)') '並び替え後 >>'
    write (*, '(*(i2,1x))') num
end
!sub:クイックソート
recursive subroutine quicksort(a, first, last)
    use global_variable
    implicit none
    integer(4) a(N), i, j, t, k
    integer(4) first, last, center

    i = first
    j = last
    write (*, '(/,a)', advance='no') '【'
    write (*, '(*(i2,1x),a)', advance='no') (a(k), k=i, j)
    write (*, '(a)') '】を対象に調べます。'
    !基準値の設定
    !先頭と末尾の平均を基準値とする例
    write (*, '(5x,a)') 'STEP:1'
    center = a(int((first + last)/2))
    write (*, '(10x,a,i2)') '中央値:', center
    do
        write (*, '(3X,a)') 'LOOP'

        !STEPX:中央値より"大きい"値を先頭から検索
        write (*, '(5x,a)') 'STEP:2'
        write (*, '(10x,a,i2,a,i2,a)') '【', a(i), '】 から前進して【', center, '】より”小さい”かチェック'
        do while (a(i) < center)
            write (*, '(15x,a,i2,a,i2)') 'OK : ', a(i), ' < ', center
            i = i + 1
        end do
        write (*, '(15x,a,i2,a,i2)') 'NG : ', a(i), ' > ', center

        !STEPX:中央値より"小さい"値を末尾から検索
        write (*, '(5x,a)') 'STEP:3'
        write (*, '(10x,a,i2,a,i2,a)') '【', a(j), '】 から後進して【', center, '】より”大きい”かチェック'
        do while (center < a(j))
            write (*, '(15x,a,i2,a,i2)') 'OK : ', center, ' < ', a(j)
            j = j - 1
        end do
        write (*, '(15x,a,i2,a,i2)') 'NG : ', center, ' > ', a(j)

        !GOAL:分類の終了
        if (i > j) then
            write (*, '(5x,a)') 'GOAL'
            write (*, '(10x,a,i2)') '分類結果(iとjがすれ違っているため終了)'
            write (*, *) i, j
            write (*, '(15x,a,*(i2,1x))') '前半部分:', (a(k), k=first, i - 1)
            write (*, '(15x,a,*(i2,1x))') '後半結果:', (a(k), k=i, last)
            exit
        else if (i == j) then
            write (*, '(5x,a)') 'GOAL'
            write (*, '(10x,a,i2)') '分類結果(iとjがすれ違っているため終了)'
            write (*, *) i, j
            write (*, '(15x,a,*(i2,1x))') '前半部分:', (a(k), k=first, i)
            write (*, '(15x,a,*(i2,1x))') '後半結果:', (a(k), k=i + 1, last)
            exit
        end if

        !STEPX:値の入れ替え
        write (*, '(5x,a)') 'STEP:4'
        write (*, '(10x,i2,a,i2,a)') a(i), ' と ', a(j), ' を入れ替えます。'
        write (*, '(15x,a,*(i2,1x))') 'before : ', (a(k), k=1, last)
        t = a(i); a(i) = a(j); a(j) = t
        i = i + 1
        j = j - 1
        write (*, '(15x,a,*(i2,1x))') 'after  : ', (a(k), k=1, last)
    end do
    !範囲を前半部分に絞って、再度行う
    if (first < i - 1) call quicksort(a, first, i - 1)
    !範囲を後半部分に絞って、再度行う
    if (j + 1 < last) call quicksort(a, j + 1, last)

end subroutine quicksort
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2