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