アドベントカレンダー 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 件のコメント:
コメントを投稿