Excel VBA質問箱 IV

当質問箱は、有志のボランティア精神のおかげで成り立っています。
問題が解決したら、必ずお礼をしましょうね。
本サイトの基本方針をまとめました。こちら をご一読ください。

投稿種別の選択が必要です。ご注意ください。
迷惑投稿防止のため、URLの入力を制限しています。ご了承ください。


11016 / 13646 ツリー ←次へ | 前へ→

【18624】乱数(重複なし)の発生は? ponpon 04/10/1(金) 22:13 質問[未読]
【18625】Re:乱数(重複なし)の発生は? ちゃっぴ 04/10/2(土) 0:08 回答[未読]
【18627】Re:乱数(重複なし)の発生は? ponpon 04/10/2(土) 0:58 発言[未読]
【18628】Re:乱数(重複なし)の発生は? ponpon 04/10/2(土) 1:07 発言[未読]
【18629】Re:乱数(重複なし)の発生は? ちゃっぴ 04/10/2(土) 1:38 回答[未読]
【18630】Re:乱数(重複なし)の発生は? ちゃっぴ 04/10/2(土) 1:56 回答[未読]
【18631】Re:乱数(重複なし)の発生は? ichinose 04/10/2(土) 1:59 発言[未読]
【18637】Re:乱数(重複なし)の発生は? ponpon 04/10/2(土) 15:36 お礼[未読]
【18638】Re:乱数(重複なし)の発生は? ちゃっぴ 04/10/2(土) 15:55 回答[未読]
【18640】Re:乱数(重複なし)の発生は? ponpon 04/10/2(土) 16:19 お礼[未読]
【18641】Re:乱数(重複なし)の発生は? ちゃっぴ 04/10/2(土) 16:45 回答[未読]
【18642】Re:乱数(重複なし)の発生は? ponpon 04/10/2(土) 17:43 発言[未読]
【18653】Re:乱数(重複なし)の発生は? ponpon 04/10/3(日) 9:54 お礼[未読]

【18624】乱数(重複なし)の発生は?
質問  ponpon  - 04/10/1(金) 22:13 -

引用なし
パスワード
   ▼ichinose さん:
 こんにちは。
 またまた、お世話になります。
 乱数(重複なし)の発生は?に変えて質問させていただきます。過去ログにもあったのですが、どうも理解ができなくて…
 ネット上(グーグル何かも)で調べたのですが、これっというものがなくて、(探し方が悪かったのかも)得意のsheetを使って、一度シートに書き出し、ようやく乱数(重複なし)に行き着いたのですが、どうも悔しくて…
いつもすみません。

 いろいろな方法がありそうなので、もっといろいろと知りたいと思います。

>1.配列の中身をループで調べる方法
> 変数と変数の比較だから処理は速いですよ
       ↑
  まず乱数 rndNO(1to20)を発生させて、for next で同じ数字があったら乱数を発せさせ、新しい乱数Bに追加して……ロジックが浮かばない。トホホ

>2.Application.Match関数を使う方法
> 配列にも使えます。但し、上限があります。
       ↑
 提示されたコードで見事解決することができました。いつもながら、ありがとうございました。
  Application.Match関数ですが、どういったものなのでしょうか?ヘルプをみても見つけることができませんでしたし、コードを書くときも候補として表示されませんでした。WorksheetFunction.Match関数と構文は同じように感じましたが……
 IsError関数は、一致する数字がなくエラーが出るまで でよいのですよね。
 本やヘルプで調べてみると、Blooleam型で TrueかFalseを返すのだから、=trueはいらないのでしょうか? とんちんかんの質問でしたら申し訳ありません。
 関数を基本に子どもに役立ちそうなものを考え、関数でできないところをVBAで補っています。
 ですから、必要なところを本やヘルプで調べてかじっているので、基本ができていません。いつも初歩的な質問ばかりで申し訳ありません。


>3.乱数を文字列として配列に格納する方法(Filter関数を使用する)
      ↑
 これも本とヘルプを頼りにいろいろとコードを書いたのですが……実行時エラーばかりで、情けない。
 sub tamesi()
  Dim rndNO(1 To 21) As String
  Dim intNO() As String
  Dim p As Integer
  Dim t As Integer
      t = 30
    For p = 1 To 20
      Randomize
      rndNO(p) = Int(Rnd() * t) + 1  '1からtまでの乱数の発生
      intNO = rndNO(p)
    Do While intNO = Filter(rndNO, rndNO(p)) '数字が同じ間ループ
      rndNO(p) = Int(Rnd() * t) + 1
       intNO = rndNO(p)
 
      Loop
        Debug.Print intNO
    Next



【18625】Re:乱数(重複なし)の発生は?
回答  ちゃっぴ  - 04/10/2(土) 0:08 -

引用なし
パスワード
   ユーザー定義のコレクションを使うか、Dictionaryオブジェクトを
使う方が簡単では?

エラートラップするだけですので・・・

たしか以前にこの板でコレクションを使用したサンプルを書いた
覚えがありますが・・・

いろいろ、やり方ご存知のようなので、それぞれBenchMarkを
とって見ると面白いかも知れません。

単に私が知りたいだけだったりして・・・

【18627】Re:乱数(重複なし)の発生は?
発言  ponpon  - 04/10/2(土) 0:58 -

引用なし
パスワード
   ▼ちゃっぴ さん:
 こんにちは。
 レスをありがとうございます。

>ユーザー定義のコレクションを使うか、Dictionaryオブジェクトを
                        ↑
                    何ですかこれは?ヘルプで調べてみます。
>使う方が簡単では?

>エラートラップするだけですので・・・
>
>たしか以前にこの板でコレクションを使用したサンプルを書いた
>覚えがありますが・・・
  ↑
確かに見たのですが、ユーザー定義のコレクションのことがよくわからなくて
ichinoseさんに、いろいろやり方を教えてもらっているところです。
自分では、sheetを使って、Countifを用いた方法しかできなかったものですから
本当にいろいろと方法があるものですね。どれくらいあるのでしょうか?

>いろいろ、やり方ご存知のようなので、それぞれBenchMarkを
>とって見ると面白いかも知れません。
     ↑
 ストップウォッチで計るわけではないので、これもVBAでできるのですよね?
>単に私が知りたいだけだったりして・・・
     ↑
 私も知りたいです。(早いほうがいいに決まっているから、特に初心者ですからコードがベタなもので、ほかのところが遅いので、少しでも早いほうがいいです。)

【18628】Re:乱数(重複なし)の発生は?
発言  ponpon  - 04/10/2(土) 1:07 -

引用なし
パスワード
   ▼ちゃっぴ さん:
 こんにちは。
>ユーザー定義のコレクションを使うか、Dictionaryオブジェクトを
                        ↑
         ヘルプにありませんでした。見つけられないだけかもしれません。

【18629】Re:乱数(重複なし)の発生は?
回答  ちゃっぴ  - 04/10/2(土) 1:38 -

引用なし
パスワード
   >>ユーザー定義のコレクションを使うか、Dictionaryオブジェクトを
>                        ↑
>         ヘルプにありませんでした。見つけられないだけかもしれません。

Excel2000以降ならヘルプにあると思いますが・・・
2000,2002は確認済みです。

どうしてもないようでしたら、下記サイトでWSHのヘルプDLしましょう!

Windows Script 5.6 ドキュメント ダウンロード (exe 形式; 1.67 MB)
http://www.microsoft.com/japan/msdn/scripting/default.asp

ユーザー定義のコレクションのサンプルです。
http://www.vbalab.net/vbaqa/c-board.cgi?cmd=one;no=15434;id=excel

HNが「名前なし」になってますが、書いたのは私です。

なお、BenchMarkを行なうのであればAPI GetTickCountを使用するとよいでしょう。

http://www.vbalab.net/vbaqa/c-board.cgi?cmd=one;no=14242;id=excel

【18630】Re:乱数(重複なし)の発生は?
回答  ちゃっぴ  - 04/10/2(土) 1:56 -

引用なし
パスワード
   Collectionの説明ですが、WorkSheetsコレクションを想像してください。

Index及びNameプロパティは重複不可能ですよね。
(同名シートを追加しようとするとエラーになりますよね。)

ユーザー定義Collectionもこれと同じです。
ただし、ユーザー定義CollectionはNameプロパティにあたるのが
Keyになります。
同名Keyを追加しようとすると、WorkSheetsコレクションと同じように
エラーになります。

なので、エラーを無視するようにしてやれば、最終的に
重複のない配列が出来上がります。

Dicitionaryを使用する方法も同じロジックです。

【18631】Re:乱数(重複なし)の発生は?
発言  ichinose  - 04/10/2(土) 1:59 -

引用なし
パスワード
   ponpon さん、ちゃっぴ さん
こんばんは。


>>ユーザー定義のコレクションを使うか、Dictionaryオブジェクトを
>                        ↑
>         ヘルプにありませんでした。見つけられないだけかもしれません。
私も普段は、コレクションを使う事が多いかなあ・・・。
後は、せっかくのExcel/VBAだから、フィルタですね

http://www2.jiu.ac.jp/~en/VBSdocs/390.htm
↑Dictionaryは、ここを見て下さい。

で、ご質問の件です。
1の配列をループで一つ一つ調べる方法

'=================================================================
Sub test()
  Dim rndNO(1 To 20) As Integer
  Dim intNO As Integer
  Dim p As Integer
  Dim t As Integer
  Randomize
  For p = 1 To 20
   t = 30
   intNO = Int(Rnd() * t) + 1  '1からtまでの乱数の発生

   Do Until Not ex_array(intNO, rndNO()) '同じ数字がでたら、違うまでループ
'           ↑ここでは、結果だけわかればよいので
'            Functionにしました。
   intNO = Int(Rnd() * t) + 1  '1からtまでの乱数の発生
 
    Loop
   rndNO(p) = intNO
   Next

  For i = 1 To 20
   Cells(i, 1).Value = rndNO(i)
   Next
End Sub
'==================================================================
Function ex_array(c_val As Variant, myarray As Variant) As Boolean
' 見つかると True 見つからないとFalse
  ex_array = False
  For idx = LBound(myarray) To UBound(myarray)
   If c_val = myarray(idx) Then
     ex_array = True
     Exit For
     End If
   Next idx
End Function

3の乱数を文字列として格納する方法
これは、重複し無いデータをつくる箇所をFunctionにしました。

'=====================================================================
Sub test()
  Dim wk As Variant
   wk = sp_rng(30, 20)
   For idx = LBound(wk) To UBound(wk)
    cells(idx,1).value= wk(idx)
    Next
End Sub
'=================================================================
Function sp_rng(n As Long, cnt As Long) As Variant
  Dim idx As Long
  ReDim r_array(1 To cnt) As Variant
  Dim wk1
  Randomize
  For idx = 1 To cnt
   r_array(idx) = Format("0", String(Int(WorksheetFunction.Log10(n)) + 1, "0"))
   Next
'↑ 配列を0で初期化(実際にはnの値によって例えば、n=30だと"00"で初期化)
'           つまり、桁数分0を並べる
  For idx = 1 To cnt
   wk = Int(Rnd() * n) + 1
   Do Until UBound(Filter(r_array(), Format(wk, String(Int(WorksheetFunction.Log10(n)) + 1, "0")))) < 0
'Filter関数の結果同じデータがないとUbound()は、-1になります
    wk = Int(Rnd() * n) + 1
    Loop
   r_array(idx) = Format(wk, String(Int(WorksheetFunction.Log10(n)) + 1, "0"))
   ' ↑ここもnの桁数分頭に0を付けた文字列として配列に格納
   Next idx
  For idx = LBound(r_array()) To UBound(r_array())
   r_array(idx) = Val(r_array(idx))
   Next
'  ↑このループは、文字列を数値に直しています
  sp_rng = r_array()
  Erase r_array
End Function


重複チェックをどうするか という箇所を思いついたもの
を記述しました。確認して下さい

【18637】Re:乱数(重複なし)の発生は?
お礼  ponpon  - 04/10/2(土) 15:36 -

引用なし
パスワード
   ▼ichinose さん,ちゃっぴ さん
こんにちは。二人ともありがとうございました。

>http://www2.jiu.ac.jp/~en/VBSdocs/390.htm
Dictionaryは、ここを見て下さい。
    ↑ヘルプで見つかりました。失礼しました。

>1の配列をループで一つ一つ調べる方法
>3の乱数を文字列として格納する方法
    ↑
  当たり前ですが、バッチリでした。ありがとうございました。

>重複チェックをどうするか という箇所を思いついたもの
    ↑
  重複した場所をどうするか。 ここのロジックがポイントですね。
  早くいろいろなことを思いつけるようになりたいです。

ちゃっぴ さんへ
ベンチマークやってみました。(少しは練習になるかと思って)
 1から300までの乱数を重複なしに20個発生するのを100回繰り返して
 タイムを取り、それを10回繰り返して平均をとりました。(手作業ではありませんよ。)
 マシンパワーに関係すると思いますが、
 PEN31G メモリ 256KB OS WIN2000 EXCEL2002 の条件で

 >1の配列をループで一つ一つ調べる方法
    一回の平均 35.2ms

 >2のApplication.Match関数を使う方法
    一回の平均 36.6ms

 >3の乱数を文字列として格納する方法
    一回の平均 37.6ms

 >4シートを使ってCountifを使った方法
    一回の平均 117ms

 >5ユーザー定義のコレクションを使った方法
   残念ながらセルに書き出すことができませんでした。トホホ 我ながら情けない。


Sub test4()
  Dim colNumber As New Collection
  Dim intRndNo As Integer
  Dim vntRndNo As Variant
 
  'エラートラップ
  On Error Resume Next
  Do
    intRndNo = Int(300 * Rnd) + 1
    'コレクション格納
    colNumber.Add intRndNo, "Key" & intRndNo
  Loop Until colNumber.Count = 20
  On Error GoTo 0
'  For Each vntRndNo In colNumber
   For i = 1 To 20
    Cells(i, 5).Value = vntRndNo ←ここがわからない。20個格納されている                  はずだから、choose関数? array?????
   Next
' Next vntRndNo
End Sub

 >6Dictionaryオブジェクトを使った方法
   もちろん考えることを諦めました。
以上です。

【18638】Re:乱数(重複なし)の発生は?
回答  ちゃっぴ  - 04/10/2(土) 15:55 -

引用なし
パスワード
   >    Cells(i, 5).Value = vntRndNo ←ここがわからない。20個格納されている                  はずだから、choose関数? array?????

CollectionなのでWorkSheetsと同じです。
WorkSheets(1)は、1枚目のワークシートですよね。

なので

Cells(i, 5).Value = colNumber(i) 'コレクションの中からi番目の要素をとりだす。

にしてみてください。

【18640】Re:乱数(重複なし)の発生は?
お礼  ponpon  - 04/10/2(土) 16:19 -

引用なし
パスワード
   ▼ちゃっぴ さん:
こんにちは。すみません。

>Cells(i, 5).Value = colNumber(i) 'コレクションの中からi番目の要素をとりだす。

これもやってみたのですが、実行時エラー「型が一致しません」に
成っちゃいます。どうして?どこかまちがえたかな?
  

【18641】Re:乱数(重複なし)の発生は?
回答  ちゃっぴ  - 04/10/2(土) 16:45 -

引用なし
パスワード
   >>Cells(i, 5).Value = colNumber(i) 'コレクションの中からi番目の要素をとりだす。
>
>これもやってみたのですが、実行時エラー「型が一致しません」に
>成っちゃいます。どうして?どこかまちがえたかな?

For i = 1 To 20
  Cells(i, 5).Value = colNumber(i) 
Next 

となってますか?私の環境では正常に動きますけど・・・

なお、For Each 〜 Nextを使用するとこんな感じです。
  Dim rngDest As Range
  
  Set rngDest = Cells(1, 5)      '出力開始行セット
  For Each vntRndNo In colNumber
    rngDest.Value = vntRndNo
    Set rngDest = rngDest.Offset(1) '出力行インクリメント
  Next vntRndNo

セルへの出力処理を、配列を使ってやればより高速化が望めます。
Sub test5()
  Dim colNumber As New Collection
  Dim intRndNo As Integer
  Dim intRndArray() As Integer

  'エラートラップ
  On Error Resume Next
  Do
    intRndNo = Int(300 * Rnd) + 1
    'コレクション格納
    colNumber.Add intRndNo, "Key" & intRndNo
  Loop Until colNumber.Count = 20
  On Error GoTo 0
  
  '配列数確定
  ReDim intRndArray(1 To 20, 1 To 1)
  
  'コレクションから配列に代入
  For i = 1 To 20
    intRndArray(i, 1) = colNumber(i)
  Next i
  
  'セルに一括出力
  Cells(1, 5).Resize(20, 1).Value = intRndArray
End Sub

【18642】Re:乱数(重複なし)の発生は?
発言  ponpon  - 04/10/2(土) 17:43 -

引用なし
パスワード
   ▼ちゃっぴ さん:
 こんばんは。
 ちゃんと動きました。

>For i = 1 To 20
>  Cells(i, 5).Value = colNumber(i) 
>Next            ↑
            vntRndNo(i)となってました。

    !!ベンチマーク結果発表!!

 >1の配列をループで一つ一つ調べる方法
    一回の平均 35.2ms

 >2のApplication.Match関数を使う方法
    一回の平均 36.6ms

 >3の乱数を文字列として格納する方法
    一回の平均 37.6ms

 >4シートを使ってCountifを使った方法
    一回の平均 117ms

 >5ユーザー定義のコレクションを使った方法
    一回の平均 40ms

 >6ユーザー定義のコレクション(For Each 〜 Next)を使った方法
    一回の平均 24ms

 ◎>7ユーザー定義のコレクション(セルへの出力処理に配列)を使った方法
    一回の平均 6ms
    速!速! めちゃくちゃ早かったです。びっくり!

※シートを使ってちまちまベタ菜コードを書いていると、速度を無茶苦茶犠牲にしていることがわかりました。勉強して早いコードをかけるようにがんばりたいです。
これからもまた、よろしくお願いします。
  

【18653】Re:乱数(重複なし)の発生は?
お礼  ponpon  - 04/10/3(日) 9:54 -

引用なし
パスワード
   ▼ちゃっぴ さん:
 おはようございます。
やっと理解できました。(遅いでしょ)
はじめから、下記のように説明されていたと思いますが、理解できていませんでした。

 重複不可ですから、同じものを追加しても、カウントされないのですね。
 同じものが二つも三つもできると思ってました。
したがって、エラーなしで追加していくと、最終的に必要な数の重複のない配列ができあがるわけですね。
わかるとうれしいものです。これからも使えそうです。
ありがとうございました。

>Collectionの説明ですが、WorkSheetsコレクションを想像してください。
>
>Index及びNameプロパティは重複不可能ですよね。
>(同名シートを追加しようとするとエラーになりますよね。)
>
>ユーザー定義Collectionもこれと同じです。
>ただし、ユーザー定義CollectionはNameプロパティにあたるのが
>Keyになります。
>同名Keyを追加しようとすると、WorkSheetsコレクションと同じように
>エラーになります。
>
>なので、エラーを無視するようにしてやれば、最終的に
>重複のない配列が出来上がります。
>
>Dicitionaryを使用する方法も同じロジックです。

11016 / 13646 ツリー ←次へ | 前へ→
ページ:  ┃  記事番号:
2610219
(SS)C-BOARD v3.8 is Free