VBAで必要となる基本的なアルゴリズムについて（ソートアルゴリズム、Excel列番号の取得など）

# はじめに

VBAにおいても**基本的なアルゴリズム**を使うと色々と便利になります。

ここでは、個人的に重用している「**ソート（並べ替え）アルゴリズム**」や「**Excelの列番号取得のアルゴリズム**」などの、基本的なコードを備忘として残しておきます。

**＜目次＞**
**[1. 並べ替えのアルゴリズム（ソートアルゴリズム）](#1-並べ替えのアルゴリズムソートアルゴリズム)**
　[1-1. 前準備（共通で使用する関数）](#1-1-前準備共通で使用する関数)
　[1-2. バブルソート](#1-2-バブルソート)
　[1-3. クイックソート](#1-3-クイックソート)
　[1-4. ２次元配列のソート](#1-4-２次元配列のソート)
**[2. 進数変換のアルゴリズム](#2-進数変換のアルゴリズム)**
　[2-1. 進数変換の計算方法](#2-1-進数変換の計算方法)
　[2-2. 10進数からn進数に変換するサンプルコード](#2-2-10進数からn進数に変換するサンプルコード)
　[2-3. n進数から10進数に変換するサンプルコード](#2-3-n進数から10進数に変換するサンプルコード)
**[3. Excelの列番号を取得するアルゴリズム](#3-excelの列番号を取得するアルゴリズム)**
　[3-1. Excelの列番号を取得するアルゴリズムについての考察](#3-1-excelの列番号を取得するアルゴリズムについての考察)
　[3-2. 数字をアルファベット列番号に変換するサンプルコード](#3-2-数字をアルファベット列番号に変換するサンプルコード)
　[3-3. アルファベット列番号を数字に変換するサンプルコード](#3-3-アルファベット列番号を数字に変換するサンプルコード)

# 1. 並べ替えのアルゴリズム（ソートアルゴリズム）

ソートアルゴリズムは、データを並べ替えるためのアルゴリズムです。
ここでは、簡単に実装できる例として**バブルソート**を、処理の早い例として**クイックソート**を紹介しておきます。
あわせて、**2次元配列を対象としたソート**についても、サンプルコードと簡単な説明を書いておきます。

## 1-1. 前準備（共通で使用する関数）

先に、共通で使用する関数（プロシージャ）を書いておきます。
この記事で書くサンプルコードに使用していきますので、最初にこれらのコードを標準モジュールに記載しておいてください。

**＜スワップ関数＞**
2つの値を入れ替えるだけの関数です。

```vb
'スワップ関数（値aと値bを入替えるプローシージャ）
Sub Swap(a As Variant, b As Variant)
    Dim tmp As Variant
    tmp = a
    a = b
    b = tmp
End Sub
```

**＜配列作成関数＞**
乱数を利用して配列を作成する関数です。
配列`nums()`に、`n`個の要素（整数値）をランダムに作成します。

```vb
'乱数による配列作成プロシージャ
Sub CreateArray(nums() As Long, n As Long)
    Dim i As Long
    ReDim nums(n - 1)
    Randomize '乱数ジェネレーターを初期化（都度異なる乱数値を取得するため）
    For i = 0 To UBound(nums)
        nums(i) = Fix(Rnd * n)
    Next
End Sub
```

**＜配列出力関数＞**
1次元配列をイミディエイトウィンドウに出力する関数です。

```vb
'1次元配列の内容を出力するプロシージャ
Sub PrintArray(nums As Variant)
    Dim i As Long
    Dim buf As String
    For i = 0 To UBound(nums)
        buf = buf & nums(i) & ", "
    Next
    Debug.Print buf
End Sub
```


## 1-2. バブルソート

バブルソートは処理速度が遅いですが、アルゴリズムが単純なので、簡単に作成することができます。
データが大量でなければ、このバブルソートで十分だったりします。

### 1-2-1. バブルソートのアルゴリズム概要

**バブルソート**は、以下の図のように隣り合う要素を順に比較して、値が大きいものを後ろに移動させていくアルゴリズムです。
一巡するたびに、一番大きい値が末尾に置かれて確定していきます。
バブル（泡）がどんどん上に上っていくように、大きい値が末尾に移動して確定していくというイメージです。
![2020-11-23 125306.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/8bb41a5e-a936-9881-ee33-14e13e81f59d.png)

### 1-2-2. バブルソートのサンプルコード（数値型）

**＜ソースコード＞**



```vb
'バブルソート
Sub BubbleSort(nums() As Long)
    Dim i As Long
    Dim j As Long
    For i = UBound(nums) - 1 To 0 Step -1 '比較する範囲を1つずつ小さくしていく
        For j = 0 To i
            If nums(j) > nums(j + 1) Then
                Call Swap(nums(j), nums(j + 1)) '前方の値の方が大きければ数値を入れ替える
            End If
        Next
    Next
End Sub
```

`For i = UBound(nums) - 1 To 0 Step -1`のところで、1巡目、2巡目、3巡目と進むにつれて比較する範囲を一つずつ小さくしています。

`If nums(j) > nums(j + 1) Then`のところで数値の大小を比較して、前者の数字が大きければ`Swap関数`で入替え処理をしています。

**＜出力確認＞**

```vb
'BubbleSortの出力
Sub OutputBubbleSort()
    Dim nums() As Long
    Call CreateArray(nums, 10) '配列を作成
    Call PrintArray(nums) 'ソート前の配列を出力
    
    Call BubbleSort(nums) 'バブルソートを実行
    
    Call PrintArray(nums) 'ソート後の配列を出力
End Sub
```

```:イミディエイトウィンドウ（2行目がソート後の配列）
7, 2, 0, 9, 8, 8, 7, 6, 4, 2, 
0, 2, 2, 4, 6, 7, 7, 8, 8, 9, 
```

### 1-2-3. バブルソートのサンプルコード（Variant型）

実際にソートを使用する場合には、数値型以外の**文字列型**、**日付型**のデータを並べ替えする必要も生じます。
そういう前提を踏まえて、**Variant型**でソースコードを作成していると便利な場合もありますので、以下ソースコードを残しておきます。

**＜ソースコード＞**

```vb
'バブルソート（Variant型）
Sub BubbleSortV(arr As Variant)
    Dim i As Long
    Dim j As Long
    For i = UBound(arr) - 1 To 0 Step -1
        For j = 0 To i
            If arr(j) > arr(j + 1) Then
                Call Swap(arr(j), arr(j + 1))
            End If
        Next
    Next
End Sub
```

**＜出力確認＞**
例として、日付型のデータの並べ替えをしてみます。

```vb
Sub OutputBubbleSortV()
    Dim arr As Variant: arr = Array(#11/21/2020#, #11/4/2020#, #9/12/2020#, #10/30/2020#, #10/2/2020#)
    Call BubbleSortV(arr)
    Call PrintArray(arr)
End Sub
```

```:イミディエイトウィンドウ
2020/09/12, 2020/10/02, 2020/10/30, 2020/11/04, 2020/11/21, 
```

以上のように、日付型として並べ替えされていることが分かります。

## 1-3. クイックソート

クイックソートは、その名のとおり処理速度が速いアルゴリズムです。
取り扱うデータが大量となる場合は、こちらのアルゴリズムを使った方が良いでしょう。

### 1-3-1. クイックソートのアルゴリズム概要

**クイックソート**では、以下の図のように、配列の中で基準となる数値を一つ決めて、**基準よりも小さい値のグループ**と、**基準以上の値のグループ**に分けていきます。
各グループで更にこれを繰り返すことで、並べ替えを完了させるアルゴリズムです。
![2020-11-23 143033.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/bf935299-4c64-a159-827f-088821c5f714.png)

### 1-3-2. クイックソートのサンプルコード（数値型）

以下のサンプルコードでは、基準値として**配列の中央**を選択しています。
細かいことは、ソースコードの後で説明をします。

**＜ソースコード＞**

```vb
'クイックソート
'引数（nums=対象となる配列、s=配列の開始位置、e=配列の終了位置）
Sub QuickSort(nums() As Long, s As Long, e As Long)
    If s >= e Then Exit Sub '開始位置sが終了位置e以降になる場合は関数を抜ける
    Dim i As Long
    Dim p As Long: p = s
    
    Call Swap(nums(s), nums((s + e) \ 2)) '配列中央の値を基準値にして先頭と入れ替え
    For i = s + 1 To e
        If nums(i) < nums(s) Then 'nums(s)を基準値として数値を比較していく
            p = p + 1 '基準値より小さい数があるごとにpを1つ進める
            Call Swap(nums(i), nums(p)) 'Swap関数で入れ替えを行う。
        End If
    Next
    Call Swap(nums(s), nums(p)) 'Swap関数で入れ替え
    
    Call QuickSort(nums, s, p - 1) '基準値より小さいグループを再帰処理
    Call QuickSort(nums, p + 1, e) '基準値より大きいグループを再帰処理
End Sub
```

**＜出力確認＞**

```vb
'QuickSortの出力
Sub OutputQuickSort()
    Dim nums() As Long
    Call CreateArray(nums, 10) '配列を作成
    Call PrintArray(nums) 'ソート前の配列を出力
    
    Call QuickSort(nums, 0, UBound(nums)) 'クイックソートを実行
    
    Call PrintArray(nums) 'ソート後の配列を出力
End Sub
```

```:イミディエイトウィンドウ（2行目がソート後の配列）
6, 3, 0, 1, 4, 1, 8, 0, 1, 9, 
0, 0, 1, 1, 1, 3, 4, 6, 8, 9, 
```

**＜ソースコードの説明＞**
基本的なアルゴリズムの実装方法は、[Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%82%A4%E3%83%83%E3%82%AF%E3%82%BD%E3%83%BC%E3%83%88)に書いてあるとおりですが、実際にコードにしようとすると少々複雑なコードとなってしまいます（私にとっては）。
そのため、ここでは、こちらの論文「[(MAX上における)アルゴリズム的問題におけるユーザーインターフェースの改良](http://www.th.cs.meiji.ac.jp/assets/researches/2005/omoto/quicksort.html)」で紹介されているクイックソートの考え方をベースとしてコードを書いています。

処理の流れを図解すると、次のようになります。
![2020-12-06 142617.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/3bdfc7f1-675a-fb51-324f-15a756cb0799.png)
![2020-12-06 142756.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/428529bb-c637-731e-e840-782033b57a32.png)
以上の処理を終えた後に、更に、基準値未満の数（`s`から`p-1`までの要素）と、基準値以上の数（`p+1`から`e`までの要素）について同じ処理を再帰的に繰り返すことになります。

### 1-3-3. クイックソートのサンプルコード（Variant型）

これはもう書くまでもないかもしれません。
念のため、数値型以外（文字列型、日付型など）でも並べ替えができるように、Variant型でソースコードを作成した場合の例を記載しておきます。

```vb
'クイックソート（Variant型）
Sub QuickSortV(arr As Variant, s As Long, e As Long)
    If s >= e Then Exit Sub
    Dim i As Long
    Dim p As Long: p = s
    
    Call Swap(arr(s), arr((s + e) \ 2))
    For i = s + 1 To e
        If arr(i) < arr(s) Then
            p = p + 1
            Call Swap(arr(i), arr(p))
        End If
    Next
    Call Swap(arr(s), arr(p))
    
    Call QuickSortV(arr, s, p - 1)
    Call QuickSortV(arr, p + 1, e)
End Sub
```

## 1-4. ２次元配列のソート

実際に並べ替え処理を行いたいのは、2次元配列の場合が多いと思いますので、備忘用にサンプルコードを記載しておきます。

対象として次のようなテーブルを例にします。
どのカラム（列）でソートを掛けるかで、並ぶ順番も変わってきます。

|名前|区分|価格|個数|日付|
|---|---|---|---|---|
|にんじん|野菜|70|3|2020/11/14|
|りんご|果物|150|2|2020/11/18|
|みかん|果物|40|10|2020/10/22|
|キャベツ|野菜|180|1|2020/11/1|
|トマト|野菜|50|3|2020/11/20|
|じゃがいも|野菜|50|15|2020/11/5|
|バナナ|果物|300|2|2020/10/15|
|メロン|果物|1500|1|2020/11/8|


### 1-4-1. 2次元ソートのサンプルコード（クイックソート）

2次元配列のため、データ型は文字列型（`String型`）に固定して処理を行ってます。

工夫が必要となるのは、対象が**数値**、**日付**の場合の並べ替えです。
これは、`IsNumeric関数`で数値かどうかを、`IsDate関数`で日付かどうかを判定して、それぞれのデータ形式に応じた並べ替え処理を行うようにしています。

**＜ソースコード＞**

```vb
'2次元クイックソート
'引数（arr=対象となる配列、s=配列の開始位置、e=配列の終了位置、t=対象となる列番号）
Sub QuickSort2D(arr() As String, s As Long, e As Long, t As Long)
    If s >= e Then Exit Sub '開始位置sが終了位置e以降になる場合は関数を抜ける
    Dim i As Long, j As Long
    Dim p As Long: p = s
    Dim flg As Boolean
    Dim typ As Long
    
    If IsNumeric(arr(t, s)) Then
        typ = 0 '数値型の場合
    ElseIf IsDate(arr(t, s)) Then
        typ = 1 '日付型の場合
    Else
        typ = 2 'それ以外の場合
    End If

    For j = 0 To UBound(arr, 1)
        Call Swap(arr(j, s), arr(j, (s + e) \ 2)) 'Swap関数で入れ替え
    Next
    For i = s + 1 To e
        Select Case typ
        Case 0
            flg = Val(arr(t, i)) < Val(arr(t, s)) '数値で比較
        Case 1
            flg = CDate(arr(t, i)) < CDate(arr(t, s)) '日付で比較
        Case Else
            flg = arr(t, i) < arr(t, s) '文字列で比較
        End Select
        
        If flg Then 'arr(t, s)を基準値として数値を比較していく
            p = p + 1 '基準値より小さい数があるごとにpを1つ進める
            For j = 0 To UBound(arr, 1)
                Call Swap(arr(j, i), arr(j, p)) 'Swap関数で入れ替えを行う。
            Next
        End If
    Next
    For j = 0 To UBound(arr, 1)
        Call Swap(arr(j, s), arr(j, p)) 'Swap関数で入れ替え
    Next
    
    Call QuickSort2D(arr, s, p - 1, t) '基準値より小さいグループを再帰処理
    Call QuickSort2D(arr, p + 1, e, t) '基準値より大きいグループを再帰処理
End Sub
```

なお、上記のサンプルコードでは、最初の要素のみでデータ型を判定しています。
正確を期すならば、複数行の判定を行っても良いかもしれません。

**＜出力確認＞**
以下、出力用のコードと、イミディエイトウィンドウの表示例を示しておきます。
サンプル用の2次元配列の作成部分が、ちょっと不格好です（もっといい方法がありそうな気がします）。

```vb
'QuickSortの出力（2次元）
Sub OutputQuickSort2D()
    Dim arr() As String
    Call Create2DArray(arr) '2次元配列の作成
    
    Call QuickSort2D(arr, 0, UBound(arr, 2), 4) '列番号4（日付）を基準に2次元ソートの実行
    
    Call Print2DArray(arr) 'ソート後2次元の配列出力
End Sub

'2次元配列を作成するプロシージャ
Sub Create2DArray(arr() As String)
    Dim table As String
    table = "にんじん,野菜,70,3,2020/11/14;" & _
            "りんご,果物,150,2,2020/11/18;" & _
            "みかん,果物,40,10,2020/10/22;" & _
            "キャベツ,野菜,180,1,2020/11/1;" & _
            "トマト,野菜,50,3,2020/11/20;" & _
            "じゃがいも,野菜,50,15,2020/11/5;" & _
            "バナナ,果物,300,2,2020/10/15;" & _
            "メロン,果物,1500,1,2020/11/8"
    Dim i As Long, j As Long
    Dim tmp1 As Variant, tmp2 As Variant
    tmp1 = Split(table, ";")
    ReDim arr(UBound(Split(tmp1(0), ",")), UBound(tmp1))
    
    For i = 0 To UBound(arr, 2)
        tmp2 = Split(tmp1(i), ",")
        For j = 0 To UBound(arr, 1)
            arr(j, i) = tmp2(j)
        Next
    Next
End Sub

'2次元配列の内容を出力するプロシージャ
Sub Print2DArray(arr As Variant)
    Dim i As Long, j As Long
    Dim Output As String
    For i = 0 To UBound(arr, 2)
        For j = 0 To UBound(arr, 1)
            Output = Output & arr(j, i) & ", "
        Next
        Output = Output & vbCrLf
    Next
    Debug.Print Output
End Sub
```

```:イミディエイトウィンドウ
バナナ, 果物, 300, 2, 2020/10/15, 
みかん, 果物, 40, 10, 2020/10/22, 
キャベツ, 野菜, 180, 1, 2020/11/1, 
じゃがいも, 野菜, 50, 15, 2020/11/5, 
メロン, 果物, 1500, 1, 2020/11/8, 
にんじん, 野菜, 70, 3, 2020/11/14, 
りんご, 果物, 150, 2, 2020/11/18, 
トマト, 野菜, 50, 3, 2020/11/20, 
```

以上のように、うまく日付順にソートすることができています。
バブルソートの例は掲載しませんが、気になる方はご自身で作成してみてください。

# 2. 進数変換のアルゴリズム

次に、進数の変換に関するアルゴリズムです。
あまり使う機会は少ないかもしれませんが、この考え方は、次に紹介する「**3. Excelの列番号を取得するアルゴリズム**」でも使用しますので、先に書いておきます。

## 2-1. 進数変換の計算方法

予備知識として、簡単な形ではありますが、進数の計算方法について書いておきます。

例えば、`10`という数字を`2進数に変換`するには、次のように、数字を基数`2`で割っていくことで計算できす。

```
10 ÷ 2 = 5 余り 0   → 商の5は再度[基数2]で割る。余りの0は1の位の数字となる
 5 ÷ 2 = 2 余り 1   → 商の2は再度[基数2]で割る。余りの1は2の位の数字となる
 2 ÷ 2 = 1 余り 0   → 商の1は再度[基数2]で割る。余りの0は3の位の数字となる
 1 ÷ 2 = 0 余り 1   → 商が0なので計算終了。余りの1は4の位の数字となる

10を2進数で表示すると[1010]となる。
```

4進数であれば基数`4`で割り、16進数であれば基数`16`で割れば、同様に算出できます。

なお2進数`1010`を10進数に直すには、次のように計算します。

```
[1の位の0] × [2の0乗] = 0
[2の位の1] × [2の1乗] = 2
[3の位の0] × [2の2乗] = 0
[4の位の1] × [2の3乗] = 8

以上の結果を加算して、0 + 2 + 0 + 8 = 10となる。
```

つまり、2進数`1010`において、4の位の`1`は`1 × 2の3乗`（すなわち`8`）を表し、2の位の`1`は`1 × 2の1乗`（すなわち`2`）を表しているといことです。

これを10進数`365`で考えると、3の位の`3`は`3 × 10の2乗`（すなわち`300`）を表し、2の位の`6`は`6 × 10の1乗`（すなわち`60`）を表し、1の位の`5`は`5 × 10の0乗`（すなわち`5`）を表していることと同じです。

## 2-2. 10進数からn進数に変換するサンプルコード

以上の考え方を元にして10進数からn進数に変換するソースコードを書くと次のようになります。

**＜ソースコード＞**

```vb:10進数からn進数に変換
'整数(num)をn進数(base)に変換する
Function ConvBaseNumber(ByVal num As Long, base As Long) As String
    If base <= 0 Or base > 35 Then Exit Function 'エラー回避
    Dim rmd As String '余り（remainder）を格納
    Do While num
        rmd = num Mod base '基数で割った余りを取り出す
        num = num \ base '基数で除算
        If Val(rmd) > 9 Then rmd = Chr(55 + rmd) 'remが10以上の場合はアルファベットに変換
        ConvBaseNumber = rmd & ConvBaseNumber
    Loop
End Function
```

少々厄介に見えるのは、`If Val(rmd) > 9 Then rmd = Chr(55 + rmd)`の部分だと思います。
16進数などの場合は、10以上の数字を、`A`、`B`、`C`、`D`、`E`、`F`というようにアルファベットで表示しますので、ここでその変換処理を行っています。

例えば、`A`の文字コードは、**SHIFT_JISコード**（ASCIIコード）で`65`となります。
そして、VBAの`Chr関数`を使用して、`Chr(65)`と書くことで、文字コード`65`から文字`A`を表示することができます。
なお、`B`の文字コードは`66`、`C`の文字コードは`67`というように、文字コードが1つ加算されるごとに、アルファベットも1つずつ進むようになっています。
この考え方を使用して`Chr(55 + rmd)`の部分で、数値をアルファベットに変換しています（`rmd=10`ならば戻り値は`A`となるということです）。

**＜出力確認＞**

```vb
'ConvBaseNumberの出力
Sub OutputConvBaseNumber()
    Debug.Print ConvBaseNumber(255, 2)
    Debug.Print ConvBaseNumber(255, 16)
End Sub
```

```:イミディエイトウィンドウ
11111111
FF
```

## 2-3. n進数から10進数に変換するサンプルコード

次にn進数から10進数に変換するコードを書くと、次のようになります。

**＜ソースコード＞**

```vb
'n進数(n = base)を10進数に変換する
Function ConvDecimalNumber(strNum As String, base As Long) As Long
    If base <= 0 Or base > 35 Then Exit Function 'エラー回避
    Dim i As Long
    Dim tmpChr As String
    Dim tmpNum As Long
    For i = 0 To Len(strNum) - 1
        tmpChr = Mid(strNum, Len(strNum) - i, 1) '数字を1つずつ取り出す
        If IsNumeric(tmpChr) Then
            tmpNum = Val(tmpChr)
        Else
            tmpNum = Asc(tmpChr) - 55 'アルファベットの場合は数値に変換
        End If
        ConvDecimalNumber = ConvDecimalNumber + tmpNum * (base ^ i)
    Next
End Function
```

ここでは、先ほどの例（10進数からn進数に変換）の場合と逆に、`A`、`B`、`C`というようなアルファベットを、数値に変換する必要があります。
これは、`文字`から`文字コード`を取り出す`Asc関数`を使用することで処理をしています。
コード中の`tmpNum = Asc(tmpChr) - 55`の部分となります。

**＜出力確認＞**

```vb
'ConvDecimalNumberの出力
Sub OutputConvDecimalNumber()
    Debug.Print ConvDecimalNumber("11111111", 2)
    Debug.Print ConvDecimalNumber("AA", 16)
End Sub
```

```:イミディエイトウィンドウ
 255
 170
```

以上のように、正常に変換されています。

なお、`n進数から10進数に変換`する関数と、`10進数からn進数に変換`する関数を組み合わせれば、`n1進数からn2進数に変換`というように、自由に進数を変換する関数を作ることもできます。

# 3. Excelの列番号を取得するアルゴリズム

最後に、Excelの列番号を取得するアルゴリズムについてです。
Excel表では、1番目の列は`A`、6番目の列は`F`、28番目の列は`AB`というように、列番号をアルファベットで表示しています。
この数字とアルファベットの関係を取得するソースコードについて考えてみます。

## 3-1. Excelの列番号を取得するアルゴリズムについての考察

アルゴリズムについては、何となく、`A`を`0`、`B`を`1`というように順に数字を当てはめて、最後は`Z`が`25`となる`26進数`と考えれば良さそうです。
しかし、少し考慮しないといけないことがあります。

列番号`A`、`AA`、`AAA`を見てみると、実際は次のように数字に対応しています。

```
列番号   A =   1
列番号  AA =  27
列番号 AAA = 703

※列番号Aは0でなく1としています（Cellsプロパティに対応させるためです）
```

ここで、26進数として、`A`を`0`と置き換えると、`列番号A`は`0`、`列番号AA`は`00`、`列番号AAA`は`000`と変換されて、全て`0`という値を持つことになります。
26進数に当てはめるならば、桁数が変わる度に、また`0`からカウントするという形式と捉える必要があるわけです。

Excelの列番号について整理すると次のような形になります。

|桁数|範囲|組合せ数|開始値|終了値|
|---|---|---|---|---|
|1桁|A～Z|26通り|1(A)|26(Z)|
|2桁|AA～ZZ|26×26通り|27(AA)|702(ZZ)|
|3桁|AAA～ZZZ|26×26×26通り|703(AAA)|18278(ZZZ)|

各桁数の最初の列番号について、計算で取得してみると、次のようになります。

`列番号A`の数字については、26進数変換による`A = 0`に補正値`1`（Cellsプロパティに対応させるため）を加算することで`1`が導き出されます。
`列番号AA`の数字については、26進数変換による`AA = 0`に、`A～Zまでの組合せ数26`を加算し、最後に補正値`1`（Cellsプロパティに対応させるため）を加算することで`27`が導き出されます。
`列番号AAA`の数字については、26進数変換による`AAA = 0`に、`A～Zまでの組合せ数26`及び`AA～ZZまでの組合せ数26 × 26 = 676`を加算し、最後に補正値`1`（Cellsプロパティに対応させるため）を加算することで`703`が導き出されます。

それ以外の列番号で見てみると、例えば、`列番号AC`であれば、26進数変換による`AC = 2`に、`A～Zまでの組合せ数26`を加算し、最後に補正値`1`（Cellsプロパティに対応させるため）を加算することで`29`が導き出されます。

以下、このルールに従って、ソースコードを作成していきます。

## 3-2. 数字をアルファベット列番号に変換するサンプルコード

ます、数字から列番号に変換するコードについてです。
先に検討したルールを踏まえると、引数として渡す数値が、`1～26`の範囲か、`27～702`の範囲か、`703～18278`の範囲かを判定した上で、処理を進めていく必要があります。

**＜ソースコード＞**

```vb
'Excelの行番号（Alphabet）を取得する関数
Function Num2ExlClm(ByVal num As Long) As String
    Dim digits As Long: digits = 0
    Do While num >= 26 ^ digits
        num = num - 26 ^ digits
        digits = digits + 1
    Loop
    Dim i As Long
    For i = 0 To digits - 1
        Num2ExlClm = Chr(num Mod 26 + 65) & Num2ExlClm
        num = num \ 26
    Next
End Function
```

上記のコードをみてみると、引数として`num = 1`が渡されたら、`Do While num >= 26 ^ digits`の部分を1回通り、`num = num - 26 ^ digits`のところで、`num = 0`に変換されます（あわせて`digits = 1`となります）。
そして、`For i = 0 To digits - 1`のところで、digits（桁数）が`1`であることから1回ループして、`0 = "A"`と読み替えられて、`"A"`を戻り値として返します。

また、引数として`num = 30`が渡されたら、`Do While num >= 26 ^ digits`の部分を2回通り、`num = num - 26 ^ digits`のところで、`num `の値は1回目で`1`減算して、2回目で`26`減算して、`num = 3`に変換されます（あわせて`digits = 2`となります）。
そして、`For i = 0 To digits - 1`のところで、digits（桁数）が`2`であることから2回ループして、`3 = "AD"`と読み替えられて、`"AD"`を戻り値として返します。


**＜出力確認＞**

```vb
'Num2ExlClmの出力
Sub OutputNum2ExlClm()
    Debug.Print Num2ExlClm(5)
    Debug.Print Num2ExlClm(2000)
End Sub
```
```:イミディエイトウィンドウ
E
BXX
```

## 3-3. アルファベット列番号を数字に変換するサンプルコード

これも、先述した考え方に沿って、引数として渡す文字列が`E`というように1桁であれば加算値は`1`として、引数として渡す文字列が`AA`というように2桁であれば加算値は`26 + 1 = 27`として、引数として渡す文字列が`BXX`というように3桁であれば加算値は`26 + 26 × 26 + 1 = 703`として、これを`アルファベットを26進数として見立てた数字`と合算することで必要な数値を導き出します。

**＜ソースコード＞**

```vb
'Excelの行番号から数字を取得する関数
Function ExlClm2Num(ByVal alphabet As String) As Long
    Dim i As Long
    Dim digits As Long: digits = Len(alphabet) '文字列の長さから桁数を出す
    For i = 0 To digits - 1
        ExlClm2Num = ExlClm2Num + 26 ^ i '1つ前の桁数までの組合せ数を加算
    Next
    
    Dim tmpNum As Long
    For i = 0 To Len(alphabet) - 1
        tmpNum = Asc(Mid(alphabet, Len(alphabet) - i, 1)) - 65 'アルファベットを取り出して数値に変換
        ExlClm2Num = ExlClm2Num + tmpNum * (26 ^ i)
    Next
End Function
```
```vb
'ExlClm2Numの出力
Sub OutputExlClm2Num()
    Debug.Print ExlClm2Num("E")
    Debug.Print ExlClm2Num("BXX")
End Sub
```

**＜出力確認＞**

```:イミディエイトウィンドウ
5
2000
```

以上のように、必要な数字に変換することができました。

# さいごに

職場が変わって、過去のコードをコピペできない状況になり、ソート関数などをまた1から書いたりしていました。
そのため、この記事は、「自分自身が基本的なコードをいつでもコピペで使えるように」という意図で書いたものです。

本当は、マージソートやヒープソートのほか、ナップサック問題やダイクストラ法あたりまでは入れておきたかったのですが、そもそも使う場面が少ないし、気力も尽きてしまったことから、そのあたりはまた後日ということにします。

**2020/12/12追記**
当初のクイックソートのソースコードでは、「配列の先頭を基準値として拾う場合」と、「配列の中央を基準値として拾う場合」の２種類を掲載していました。
しかし、前者においては、対象となる配列がほぼ昇順に並んでいる場合は、計算量がO(n<sup>2</sup>)となりバブルソート並みになるため、前者を削り、後者の「配列の中央を基準値として拾う場合」のみに改めました。