fc2ブログ

Excelじゆうちょう

Excelのお絵描きツール『りっぷ2(りっぷつぅ)』のサポートページ、まずは「はじめに」をご覧ください。 [NewEntry] [Admin]

記事更新カレンダー

02 « 2024-03 « 04
- - - - - 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31 - - - - - -

やたらに多いカテゴリ

比較的新しい記事

新しいコメント

ありがたいブログ拍手

拍手コメント一覧(拍手はしない)

さみしいトラックバック

申し訳ないプロフィール

申し訳ない

管理人  [ 申し訳ない ]

pxivもやってます
リンクの一番上からのぞきに来てください
※閲覧にはユーザー登録が必要です

RSSってなんぞ?

広告は消せないらしい

        2010-10-07       R、G、Bの値の取り出し

24ビットカラーは、RGB値で表すことができます。

定数名  |R  |G  |B  |色
━━━━━┿━━┿━━┿━━┿━━━━
vbBlack  |  0|  0|  0|黒
vbWhite  | 255| 255| 255|
vbRed   | 255|  0|  0|
vbGreen  |  0| 255|  0|
vbBlu   |  0|  0| 255|
vbMagenta | 255|  0| 255|マゼンタ
vbYellow | 255| 255|  0|
vbCyan  |  0| 255| 255|シアン

透明度のない色ならこれで一通りの表現が可能で、Excelの塗りつぶしの色でも使われています。
このRGB値は16進数6桁で成り立っており、上位の位から2桁ずつ青成分(B)、緑成分(G)、赤成分(R)となっています。
オレンジを例にとるなら、RGB値は0080FFで、10進数に直すと33023です。
また、この色は赤成分(R)が255、緑成分(G)が128、青成分(B)が0です。

今回は、このRGB値から各色成分を取り出す方法を考えます。
取り上げるのはふたつ、\演算子とMod演算子を使う方法、\演算子とAnd演算子を使う方法です。


'【コードウインドウ】
Private Sub testMod() '\演算子とMod演算子を使う方法
Dim R_G_B_ As Long, R_ As Long, G_ As Long, B_ As Long
R_G_B_ = vbWhite '白
R_ = R_G_B_ Mod 256 '赤成分
G_ = (R_G_B_ \ 256) Mod 256 '緑成分
B_ = (R_G_B_ \ 65536) Mod 256 '青成分
MsgBox "白のRGB値:R=" & R_ & "、G=" & G_ & "、B=" & B_
End Sub

Private Sub testAnd() '\演算子とAnd演算子を使う方法
Dim R_G_B_ As Long, R_ As Long, G_ As Long, B_ As Long
R_G_B_ = vbWhite '白
R_ = R_G_B_ And vbRed '赤成分
G_ = (R_G_B_ And vbGreen) \ 256 '緑成分
B_ = (R_G_B_ And vbBlue) \ 65536 '青成分
MsgBox "白のRGB値:R=" & R_ & "、G=" & G_ & "、B=" & B_
End Sub


↓結果は両方同じ↓
白のRGB値

変数R_G_B_に好きな色のRGB値を代入すれば、各色成分を分析できます。

さて、それでは本題です。
上の方法ではどちらを使うのがいいコードなのでしょうか?
演算子が違うだけで、行数もコードの構造もほぼ同じですね。
処理速度を比べましょう!


'【コードウインドウ】
Private Declare Function GetTickCount Lib "kernel32" () As Long '時間計測用API

Private Sub testModSpeed() '\演算子とMod演算子を使う方法
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long
Dim R_G_B_ As Long, R_ As Long, G_ As Long, B_ As Long
R_G_B_ = vbWhite
Debug.Print "↓\演算子とMod演算子を使う方法↓"
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 100000000
    R_ = R_G_B_ Mod 256
    G_ = (R_G_B_ \ 256) Mod 256
    B_ = (R_G_B_ \ 65536) Mod 256
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
End Sub

Private Sub testAndSpeed() '\演算子とAnd演算子を使う方法
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long
Dim R_G_B_ As Long, R_ As Long, G_ As Long, B_ As Long
R_G_B_ = vbWhite
Debug.Print "↓\演算子とAnd演算子を使う方法↓"
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 100000000
    R_ = R_G_B_ And vbRed
    G_ = (R_G_B_ And vbGreen) \ 256
    B_ = (R_G_B_ And vbBlue) \ 65536
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
End Sub

【イミディエイトウインドウ】
↓\演算子とMod演算子を使う方法↓
1回目 6146 ミリ秒
2回目 6053 ミリ秒
3回目 6053 ミリ秒
4回目 6053 ミリ秒
↓\演算子とAnd演算子を使う方法↓
1回目 5522 ミリ秒
2回目 5491 ミリ秒
3回目 5476 ミリ秒
4回目 5476 ミリ秒


後者の方が約1割早いという結果になりました。
スポンサーサイト



        2010-09-09       Select Caseは短絡評価

Select Caseステートメントは、ある値に対してCase条件の比較を行い、Trueとなればその下のコードを実行します。
If Then ElseステートメントのElseIfと似た動作をします。
例えば、以下のようなコードがあったとします。


'【コードウインドウ】
Private Sub testSelectCase0()
Dim Str_E As String
Str_E = "E"
Select Case Str_E
Case "A"
  '処理A
Case "B"
  '処理B
Case "C"
  '処理C
Case "D"
  '処理D
Case "E"
  '処理E

Case Else
  '処理Else
End Select
End Sub


文字列変数Case_Eを比較対象にして、上から順番に"A"、"B"、"C"…と判定していきます。
そして、一致した場合は(ここではCase "E")その下の処理を実行し、Select Caseステートメントを終了します。
もし、いずれのCase条件にも一致しなければ、Case Elseの下の処理Elseが実行されます。
さらに、Case条件には複数の条件を指定できます。


'【コードウインドウ】
Private Sub testSelectCase1()
Dim Str_E As String
Str_E = "E"
Select Case Str_E
Case "A", "B", "C", "D", "E"
  '処理ABCDE

End Select
End Sub


この場合は、Case_Eが"A"または"B"または"C"…"E"の場合に処理ABCDEが実行されます。
そうでなければ、Case Elseが省略されてますので何も実行されません。

さて、今回のテーマはtestSelectCase1のように複数指定したCase条件は短絡評価されるということです。
まず、"A"を比較して一致しなければ"B"、"C"…と順番に続き、一致した時点でその後の比較をせずに(上の例では最後まで比較してしまいます)処理に移ります。

ということは、よくヒットする条件ほど先頭へ並べた方が比較回数が少なくて済みますね。
処理速度の向上につながります。
では、どれくらい違いがあるのか比べてみましょう。
3パターンで実験してみました。


'【コードウインドウ】
Private Declare Function GetTickCount Lib "kernel32" () As Long '時間計測用API

Private Sub testSelectCase2()
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long
Dim Str_E As String
Str_E = "E"
Debug.Print "↓testSelectCase2の結果↓"
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 50000000
    Select Case Str_E
    Case "A"
      '処理A
    Case "B"
      '処理B
    Case "C"
      '処理C
    Case "D"
      '処理D
    Case "E"
      '処理E

    End Select
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
End Sub

Private Sub testSelectCase3()
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long
Dim Str_E As String
Str_E = "E"
Debug.Print "↓testSelectCase3の結果↓"
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 50000000
    Select Case Str_E
    Case "A", "B", "C", "D", "E"
      '処理ABCDE
    Case "F"
      '処理F
    Case "G"
      '処理G
    Case "H"
      '処理H
    Case "I"
      '処理I
    End Select
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
End Sub

Private Sub testSelectCase4()
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long
Dim Str_E As String
Str_E = "E"
Debug.Print "↓testSelectCase4の結果↓"
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 50000000
    Select Case Str_E
    Case "E"
      '処理E

    Case "A"
      '処理A
    Case "B"
      '処理B
    Case "C"
      '処理C
    Case "D"
      '処理D
    End Select
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
End Sub

【イミディエイトウインドウ】
↓testSelectCase2の結果↓
1回目 20592 ミリ秒
2回目 20592 ミリ秒
3回目 20514 ミリ秒
4回目 20515 ミリ秒
↓testSelectCase3の結果↓
1回目 20062 ミリ秒
2回目 20014 ミリ秒
3回目 20062 ミリ秒
4回目 20031 ミリ秒
↓testSelectCase4の結果↓
1回目 5975 ミリ秒
2回目 5897 ミリ秒
3回目 5897 ミリ秒
4回目 5896 ミリ秒


testSelectCase4が、明らかに高速でした。
やはり、短絡評価されているようです。
If Then Elseステートメントを使ったOr演算子の代用が、Select Caseステートメントで100%可能なわけではありませんが、有効な手段のひとつではあります。

        2010-07-23       短絡評価できないAnd、Or

今回のテーマは、ExcelVBAのAnd演算子とOr演算子についてです。

A And Bは、AとBが共にTrueの時のみTrueを返します。

A And B  ┃   B
     ┃───┬───
     ┃False |True
━━━━━╋━━━┿━━━
 |False ┃False |False
A ├───╂───┼───
 |True ┃False |True

A Or Bは、AとBが共にFalseの時のみFalseを返します。

A Or B  ┃   B
     ┃───┬───
     ┃False |True
━━━━━╋━━━┿━━━
 |FalseFalse |True
A ├───╂───┼───
 |True ┃True |True

ここら辺はOKですね、ささっと進めます。
このAnd演算子とOr演算子は、Aの評価が決まれば、Bの評価をしなくても結果が決まってしまう場合があります。
Andの場合、AがFalseならBの評価が何であっても、結果はFalseになります。
Orの場合、AがTrueならBの評価が何であっても、結果はTrueになります。
上の表を見ればわかりますね、このようなAの状態次第で結果が決まってしまう評価方法を「短絡評価」と呼びます。

これが意外と重要なんです。
例えば、Bの評価が何かの重要な処理をした結果だったとした場合、短絡評価ならBが実行されない可能性があります。
ですが、裏を返せばBが実行されないとすれば、それだけ処理時間が短縮できます。

それでは、ExcelVBAではどうなっているんでしょうか?

残念ながら、短絡評価はサポートしていません。
どうやら、短絡評価をする演算子自体がないようです。
(たぶん…少なくとも私は知らないです、あったら教えてください)
以下のコードで実際に確認してみましょう。


'【コードウインドウ】
Private Sub testAnd() 'これを実行
Debug.Print FalseTrue(False) And FalseTrue(True) 'False And Trueで、Falseを表示
End Sub

Private Function FalseTrue(Bol As Boolean) As Boolean '引数の評価をそのまま返す関数
Debug.Print Bol '引数の評価を表示
FalseTrue = Bol '引数の評価を返す
End Function

【イミディエイトウインドウ】
False
True
False


A And BのAにあたるFalseTrue(False)がFalseなのに、BにあたるFalseTrue(True)が実行されてます。
同じことはA Or Bにも言えます。
あえて確認はしませんが、気になる方は上のコードをちょこっと修正すれば実験できます。

最後に、処理速度の違いも見ておきましょう。
短絡評価の演算子がありませんので、Ifステートメントの入れ子で代用しています。


'【コードウインドウ】
Private Declare Function GetTickCount Lib "kernel32" () As Long '時間計測用API

Private Function FalseTrue(Bol As Boolean) As Boolean '引数の評価をそのまま返す関数
FalseTrue = Bol '引数の評価を返す
End Function

Private Sub testAnd1() 'AとBを両方評価
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long
Debug.Print "↓testAnd1の結果↓"
For For0 = 1 To 4
  Tck0 = GetTickCount
    For For1 = 1 To 100000000
    If FalseTrue(False) And FalseTrue(True) Then
      Stop 'ここは通過しない
    End If
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
End Sub

Private Sub testAnd2() '短絡評価
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long
Debug.Print "↓testAnd2の結果↓"
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 100000000
    If FalseTrue(False) Then
      If FalseTrue(True) Then 'ここは通過しない
        Stop 'ここも通過しない
      End If
    End If
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
End Sub

【イミディエイトウインドウ】
↓testAnd1の結果↓
1回目 21981 ミリ秒
2回目 21871 ミリ秒
3回目 21856 ミリ秒
4回目 21871 ミリ秒
↓testAnd2の結果↓
1回目 10889 ミリ秒
2回目 10764 ミリ秒
3回目 10764 ミリ秒
4回目 10764 ミリ秒


効果てきめん、およそ半分の時間になりました。

そういえば、りっぷ2(りっぷつぅ)でもAnd演算子やOr演算子をいっぱい使ってたなぁ…
コード自体は問題ないはずですし、可読性下がるんで、当面は今のままでいいや。

        2010-03-27       速度比較、整数を取得

割り算の結果を整数で取得すること、よくありますよね?
私はよくあります、特にりっぷ2(りっぷつぅ)で。

その方法もいろいろありますが、どれを使えば早く処理されるのでしょうか。
そこで、Long型変数に 3 ÷ 4 の結果を代入する場合について比較してみました。
以下の4つを例にします。

(1)そのまま代入
小数部は丸められます。

(2)CLng関数
小数部は丸められます。

(3)Int関数、Fix関数
小数部は切り捨てられます。

(4)\演算子
小数部は切り捨てられます。

小数部の扱いについて、丸めと切り捨ての違いは考慮しないことにします。
Int関数、Fix関数は引数が負の値の場合で動作が異なりますが、代表してInt関数で比較します。
それでは、結果を示します。


'【コードウインドウ】
Private Sub test() '(1)そのまま代入
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long, a As Long
a = 1
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 100000000
    a = 3 / 4
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
Debug.Print "test、a = " & a
End Sub

'【イミディエイトウインドウ】
1回目 4602 ミリ秒
2回目 4602 ミリ秒
3回目 4602 ミリ秒
4回目 4617 ミリ秒
test、a = 1

'【コードウインドウ】
Private Sub testCLng() '(2)CLng関数
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long, a As Long
a = 1
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 100000000
    a = CLng(3 / 4)
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
Debug.Print "testCLng、a = " & a
End Sub

'【イミディエイトウインドウ】
1回目 4587 ミリ秒
2回目 4586 ミリ秒
3回目 4602 ミリ秒
4回目 4587 ミリ秒
testCLng、a = 1

'【コードウインドウ】
Private Sub testInt() '(3)Int関数
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long, a As Long
a = 1
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 100000000
    a = Int(3 / 4)
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
Debug.Print "testInt、a = " & a
End Sub

'【イミディエイトウインドウ】
1回目 6786 ミリ秒
2回目 6802 ミリ秒
3回目 6786 ミリ秒
4回目 6786 ミリ秒
testInt、a = 0

'【コードウインドウ】
Private Sub testYen() '(4)\演算子
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long, a As Long
a = 1
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 100000000
    a = 3 \ 4
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
Debug.Print "testYen、a = " & a
End Sub

'【イミディエイトウインドウ】
1回目 1684 ミリ秒
2回目 1685 ミリ秒
3回目 1700 ミリ秒
4回目 1685 ミリ秒
testYen、a = 0


(4)の\演算子が飛び抜けて早いことがわかります。
当然ですね、他のものは一度割り算で小数の値を計算してから、それを変数に代入するために変換しています。
\演算子は割り算の商だけを返しますので、二度手間がないわけですね。
もし小数部の丸めを気にしなくていいのなら、ぜひとも\演算子を使いたいものです。

で、この結果はりっぷ2(りっぷつぅ)に反映させていただきました。
恥ずかしい話likePの時には\演算子を知りませんでしたので、数ある改良点の中で一番のスピードアップに貢献してくれました。

【追記】
(1)と(2)の結果自体は大差ありませんが、変数のデータ型を意識するという意味で明示的にCLng関数を使用した方がいいでしょう。

(1)と(2)の小数部を丸めるとは、四捨五入ではありません。
CLng関数のヘルプにあるように、0.5は近い方の偶数に丸められます。

ヘルプより引用
 小数部分がちょうど0.5の時、CInt関数およびCLng関数は常にもっとも近い偶数に値を丸めます。
 たとえば0.5を0に、1.5を2にそれぞれ丸めます。

うっかりしそうな設定です、お気をつけください。

        2009-09-10       VBA速度比較、ひな形

以下のプロシージャを使って速度比較をすることにします。

ここでは、10万回繰り返した合計時間を4セット分イミディエイトウインドウに表示するようにしていますが、繰り返しの回数は実験対象によって変更していく予定です。(まあ、実行してストレスのない範囲内で適当に)
もちろん、PCのスペックによって結果は大きく変わってきますので、あくまで比較としてみてください。


'【コードウインドウ】
Private Declare Function GetTickCount Lib "kernel32" () As Long '時間計測用API

Private Sub test0()
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long
For For0 = 1 To 4
  Tck0 = GetTickCount
  For For1 = 1 To 100000
    'ここで実験します
  Next For1
  Tck1 = GetTickCount
  Debug.Print For0 & "回目 " & Tck1 - Tck0 & " ミリ秒"
Next For0
End Sub


※お詫び
昨日はCDblと[]の比較をすると書きましたが、次回に先送りします。
すでに手前では検証しているんですが、その途中で調べておかないといけないことが出てきてしまい、少しお時間をいただきます。
結果は後日、まとめて記事にする予定です。

上記のプロシージャはコメントの部分を書き換えればすぐに実験できますので、気軽に試してみてください。

 | HOME |