アドベントカレンダー 2日目
問題点:
動的配列でReDimする前に一番最後の要素を
調べようとするとエラーとなるので
どうするの?
って話ですけど、
一番てっとりばやいのは、配列の要素を管理する変数を作って
追加する時にインクリメントしてあげればよいわけですが
別関数に処理をまかせ、特定の条件の時のみ配列に追加する
となると、いちいち配列と、配列の要素数を管理する変数を
毎回引き渡すとなるとちょっと美しくないですよね。
そんなわけで、配列の要素数を管理する変数なしで行うとしたら
SGN関数をつかって、配列の宣言がされてなかったら0が返ってくる修正を利用して処理を分岐するか
On Error Resume Nextを使ってUboundで最終要素にアクセスしてエラーがあれば0でReDimするって
方法あたりが考えられますよね。
たとえば、こんな感じ。
以下は、AddEven関数内で5回ループし、偶数だったら配列に追加する処理です。
では、せっかくなんでどのやり方が一番最速か調べてみます。
純粋に配列の初期要素問題がどう影響するかだけを見たいので
乱数やら、偶数の時だけ配列に追加とかは除外します。
単純に10万回ループさせて値を追加し続けてみます。
1.SGN関数を使う
2.On Error Resume Nextを使う
3.要素数管理用の変数を使う(引数に渡す)
→IF分を減らした方が早いんじゃないかという疑惑から
4.要素数管理用の変数を使う(引数で渡さない:グローバル宣言)
→IF分なくしても、引数を増やすと時間がかかるんじゃないかという疑惑から。
ソースコードは最後に掲載
一応、SGNを使った場合が一番早いようですが、
10万回ループで0.1秒差なので、どれを使ったもかわらないですね。
ソースコード:
問題点:
動的配列でReDimする前に一番最後の要素を
調べようとするとエラーとなるので
どうするの?
って話ですけど、
一番てっとりばやいのは、配列の要素を管理する変数を作って
追加する時にインクリメントしてあげればよいわけですが
別関数に処理をまかせ、特定の条件の時のみ配列に追加する
となると、いちいち配列と、配列の要素数を管理する変数を
毎回引き渡すとなるとちょっと美しくないですよね。
そんなわけで、配列の要素数を管理する変数なしで行うとしたら
SGN関数をつかって、配列の宣言がされてなかったら0が返ってくる修正を利用して処理を分岐するか
On Error Resume Nextを使ってUboundで最終要素にアクセスしてエラーがあれば0でReDimするって
方法あたりが考えられますよね。
たとえば、こんな感じ。
以下は、AddEven関数内で5回ループし、偶数だったら配列に追加する処理です。
- Option Explicit
- Sub SampleCode()
- Dim EvenNumber() As String
- 'SGN関数を使った場合
- Call AddEvenNumber1(EvenNumber)
- '配列の初期化
- Erase EvenNumber
- 'On Error Resume Nextを使った場合
- Call AddEvenNumber2(EvenNumber)
- End Sub
- Sub AddEvenNumber1(ByRef EvenNumber() As String)
- Dim E As Long
- Dim i As Integer
- Dim RandomNumber As Integer
- Randomize
- For i = 1 To 5
- '1~100の乱数を発生
- RandomNumber = Int(Rnd() * 100) + 1
- '奇数の時は次のループへ
- If RandomNumber Mod 2 = 1 Then GoTo NextForLoop
- If Sgn(EvenNumber) = 0 Then
- E = 0
- Else
- E = UBound(EvenNumber) + 1
- End If
- ReDim Preserve EvenNumber(E)
- EvenNumber(E) = RandomNumber
- NextForLoop:
- Next
- End Sub
- Sub AddEvenNumber2(ByRef EvenNumber() As String)
- Dim i As Integer
- Dim RandomNumber As Integer
- Randomize
- For i = 1 To 5
- '1~100の乱数を発生
- RandomNumber = Int(Rnd() * 100) + 1
- '奇数の時は次のループへ
- If RandomNumber Mod 2 = 1 Then GoTo NextForLoop
- On Error Resume Next
- ReDim Preserve EvenNumber(UBound(EvenNumber) + 1)
- If Err.Number <> 0 Then ReDim Preserve EvenNumber(0)
- On Error GoTo 0
- EvenNumber(UBound(EvenNumber)) = RandomNumber
- NextForLoop:
- Next
- End Sub
では、せっかくなんでどのやり方が一番最速か調べてみます。
純粋に配列の初期要素問題がどう影響するかだけを見たいので
乱数やら、偶数の時だけ配列に追加とかは除外します。
単純に10万回ループさせて値を追加し続けてみます。
1.SGN関数を使う
2.On Error Resume Nextを使う
3.要素数管理用の変数を使う(引数に渡す)
→IF分を減らした方が早いんじゃないかという疑惑から
4.要素数管理用の変数を使う(引数で渡さない:グローバル宣言)
→IF分なくしても、引数を増やすと時間がかかるんじゃないかという疑惑から。
ソースコードは最後に掲載
1.SGN | 2.Error | 3.要素数管理 | 4.要素数管理 | |
1回目 | 1.047 | 0.812 | 0.703 | 0.734 |
2回目 | 0.734 | 0.687 | 0.750 | 0.735 |
3回目 | 0.609 | 0.656 | 0.703 | 0.796 |
4回目 | 0.719 | 0.766 | 0.703 | 0.703 |
5回目 | 0.562 | 0.718 | 0.875 | 0.828 |
6回目 | 0.563 | 0.797 | 0.735 | 0.750 |
7回目 | 0.578 | 0.750 | 0.750 | 0.750 |
8回目 | 0.547 | 0.797 | 0.953 | 0.703 |
9回目 | 0.594 | 0.812 | 0.781 | 0.656 |
10回目 | 0.516 | 0.859 | 0.844 | 0.750 |
平均 | 0.6469 | 0.7654 | 0.7797 | 0.7405 |
一応、SGNを使った場合が一番早いようですが、
10万回ループで0.1秒差なので、どれを使ったもかわらないですね。
ソースコード:
- Option Explicit
- Dim EE As Long
- Declare Function GetTickCount Lib "kernel32" () As Long
- Sub SampleCode()
- Dim BaseTime As Long
- Dim E As Long: E = -1
- Dim EvenNumber() As String
- '基準タイム
- BaseTime = GetTickCount
- 'SGN関数を使った場合
- Call AddArray1(EvenNumber)
- Debug.Print "SGN :" & (GetTickCount - BaseTime) / 1000
- '配列の初期化
- Erase EvenNumber
- '基準タイム
- BaseTime = GetTickCount
- 'On Error Resume Nextを使った場合
- Call AddArray2(EvenNumber)
- Debug.Print "Err :" & (GetTickCount - BaseTime) / 1000
- '配列の初期化
- Erase EvenNumber
- '基準タイム
- BaseTime = GetTickCount
- 'On Error Resume Nextを使った場合
- Call AddArray3(EvenNumber, E)
- Debug.Print "要素:" & (GetTickCount - BaseTime) / 1000
- '配列の初期化
- Erase EvenNumber
- EE = -1
- '基準タイム
- BaseTime = GetTickCount
- 'On Error Resume Nextを使った場合
- Call AddArray4(EvenNumber)
- Debug.Print "要素:" & (GetTickCount - BaseTime) / 1000
- Debug.Print "---"
- End Sub
- Sub AddArray1(ByRef EvenNumber() As String)
- Dim E As Long
- Dim i As Long
- Dim RandomNumber As Integer
- For i = 1 To 100000
- If Sgn(EvenNumber) = 0 Then
- E = 0
- Else
- E = UBound(EvenNumber) + 1
- End If
- ReDim Preserve EvenNumber(E)
- EvenNumber(E) = i
- NextForLoop:
- Next
- End Sub
- Sub AddArray2(ByRef EvenNumber() As String)
- Dim i As Long
- Dim RandomNumber As Integer
- For i = 1 To 100000
- On Error Resume Next
- ReDim Preserve EvenNumber(UBound(EvenNumber) + 1)
- If Err.Number <> 0 Then ReDim Preserve EvenNumber(0)
- On Error GoTo 0
- EvenNumber(UBound(EvenNumber)) = i
- NextForLoop:
- Next
- End Sub
- Sub AddArray3(ByRef EvenNumber() As String, ByRef E As Long)
- Dim i As Long
- Dim RandomNumber As Integer
- For i = 1 To 100000
- E = E + 1
- ReDim Preserve EvenNumber(E)
- EvenNumber(E) = i
- NextForLoop:
- Next
- End Sub
- Sub AddArray4(ByRef EvenNumber() As String)
- Dim i As Long
- Dim RandomNumber As Integer
- For i = 1 To 100000
- EE = EE + 1
- ReDim Preserve EvenNumber(EE)
- EvenNumber(EE) = i
- NextForLoop:
- Next
- End Sub
0 件のコメント:
コメントを投稿