Excel (VBA) |
![]() ![]() |
(指定なし : 指定なし)
ダブらない乱数
投稿日時: 23/04/28 20:07:38
投稿者: がんばりやさん
|
---|---|
配列の勉強を始めました。
|
![]() |
投稿日時: 23/04/28 21:04:59
投稿者: WinArrow
|
---|---|
>Rank関数を使用すれば乱数の重複表示
|
![]() |
投稿日時: 23/04/28 21:35:57
投稿者: がんばりやさん
|
---|---|
回答ありがとうございます。
|
![]() |
投稿日時: 23/04/28 21:42:23
投稿者: simple
|
---|---|
For i = 1 To 100 ranran(i) = WorksheetFunction.Rank(ransuu(i), Range(ransuu(1, 100))) Next iこの部分についてメモしておきます。参考にしてください。 (1) Range(ransuu(1, 100)) というRangeオブジェクトの指定方法は認められません。 ・ransuuは一次元配列なのに、ransuu(1, 100)という二次元配列的な指定は不可 ・さらに、Rangeオブジェクトの指定方法としても間違いです。 (2) ワークシート関数 Rankのヘルプによれば、 第二引数は、 「数値の範囲の配列またはその範囲への参照を指定します。」とありますが、 前段の"数値の範囲の配列"というのが意味不明です。 実際に、配列で確認しましたが、「オブジェクトが必要です」というエラーになります。 二次元でも一次元でもエラーとなります。 したがって、セル範囲の参照(つまり、A1:A100といったもの)しか使えないと思います。 あなたが最初にされた方法です。仕様ではないでしょうか。 【余談】(以下、スキップ下さい) VBAの乱数ジェネレータRnd関数の周期は、16,777,216だったと思います。 (16,777,216回取り出して初めて同じものが出てきて、あとはそれを周期的に繰り返します。) 100個程度の一様乱数の数値を取り出して、 同じものが出てくることはあり得ません。 なお、ワークシートで使っている乱数RAND()は、これとは異なりさらにもっと長周期です。 作成方法は長らく開示されてきませんでしたが、最近、メルセンヌツイスタという 日本人が開発した乱数ジェネレータに変更されたはずです。 (その他のプログラム言語でもこれが標準として使われることが多かったのですが、 最近は、長周期である性質よりも、速度に重点が置かれ、別の乱数ジェネレータに 変わりつつあります。) |
![]() |
投稿日時: 23/04/29 00:34:49
投稿者: hatena
|
---|---|
ご希望のことが、
引用: であるなら、 Rank関数でできてますね。 提示の関数ではB列に出力してますが。 100個程度ならダブることはないというのはsimpleさんから回答がありますね。 シート上に出力したいなら下記のような方法もよく使われます。 A列に1から100までの連続数を出力。 B列に乱数を出力。 B列でソートする。 シートは使わずに配列で結果が欲しいということなら、1から100までの連続数を配列に格納して、それをシャッフル(ランダムな並べ替え)すればいいでしょう。 シャッフルのアルゴリズムは下記が有名です。 フィッシャー–イェーツのシャッフル - Wikipedia https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%83%E3%82%B7%E3%83%A3%E3%83%BC%E2%80%93%E3%82%A4%E3%82%A7%E3%83%BC%E3%83%84%E3%81%AE%E3%82%B7%E3%83%A3%E3%83%83%E3%83%95%E3%83%AB コード例 '配列をシャッフルする関数 Public Sub AryShuffle(ByRef MyAry) Dim i As Long, buf, LB As Long, P As Long If Not IsArray(MyAry) Then Exit Sub Randomize LB = LBound(MyAry) For i = UBound(MyAry) To LB + 1 Step -1 P = Int((i + 1) * Rnd) + LB buf = MyAry(P) MyAry(P) = MyAry(i) MyAry(i) = buf Next End Sub Sub Test() Dim ranran(1 To 100) As Long Dim i As Integer For i = 1 To 100 '1から100までの連続数を配列に格納 ranran(i) = i Next AryShuffle ranran '配列をシャッフル For i = 1 To 100 '結果をA列に出力 Cells(i, 1) = ranran(i) Next End Sub |
![]() |
投稿日時: 23/04/29 08:40:16
投稿者: がんばりやさん
|
---|---|
|
![]() |
投稿日時: 23/04/29 09:50:28
投稿者: がんばりやさん
|
---|---|
hatena さん
|
![]() |
投稿日時: 23/04/29 11:27:03
投稿者: simple
|
---|---|
補足です。
Sub test() Dim sl As Object Dim k As Long Dim j As Long Dim ary(1 To 100) As Long Set sl = CreateObject("System.Collections.SortedList") For k = 1 To 100 sl.Add Rnd(), k Next For j = 1 To 100 ary(j) = sl.getbyindex(j - 1) 'sortedListは0から始まる Next Range("A1").Resize(100, 1) = Application.Transpose(ary) End Sub(4) ちなみに、ワークシートのRAND関数にMersenne twisterを導入したのは Excel2010からでした。最近じゃなかったですな。 周期は、2の19937乗-1 (≒4.315×10の6001乗)というむやみに長い周期です。 |
![]() |
投稿日時: 23/04/29 20:34:32
投稿者: がんばりやさん
|
---|---|
|
![]() |
投稿日時: 23/04/29 20:52:39
投稿者: がんばりやさん
|
---|---|
|
![]() |
投稿日時: 23/04/30 10:08:36
投稿者: hatena
|
---|---|
引用: おおまかには、そんな感じの理解でいいと思います。 いちおうAryShuffleに処理の説明のコメントを追加しました。 '配列をシャッフルする関数 Public Sub AryShuffle(ByRef MyAry) Dim i As Long, buf, LB As Long, P As Long If Not IsArray(MyAry) Then Exit Sub Randomize LB = LBound(MyAry) '配列の先頭のインデックスを取得 '配列の最後から先頭の一つ前までループする For i = UBound(MyAry) To LB + 1 Step -1 '先頭インデックスからiまでの範囲でランダムな整数を取得 P = Int((i + 1) * Rnd) + LB '以下pとiの位置の要素を入れ替える buf = MyAry(P) MyAry(P) = MyAry(i) MyAry(i) = buf Next End Sub 全体としてやっていることの意味は、前回紹介したリンク先の「現在のアルゴリズム」に表をつかった解説があるのでそこをみてください。 https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%83%E3%82%B7%E3%83%A3%E3%83%BC%E2%80%93%E3%82%A4%E3%82%A7%E3%83%BC%E3%83%84%E3%81%AE%E3%82%B7%E3%83%A3%E3%83%83%E3%83%95%E3%83%AB#%E7%8F%BE%E5%9C%A8%E3%81%AE%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0 私自身も上記のようなコードを何もみずに一から自分で作ることはできませんが、このようなよくあるものはアルゴリズムとして昔から研究されて公開されているので、概要が理解できればあとはブラックボックスとして積極的に利用していけばいいとと思ってます。 「簡単なことは簡単にすます」というのは私も同じポリシーです。 ただ、何が簡単かというのはその人のスキルや好みによります。 私の場合、要素をランダムに並べ替える(シャッフルする)ということはよくあるので、関数をひとつ作成しておけば、あとは AryShuffle 配列 と1行記述するだけですむので、私にとっては簡単かなということです。 |
![]() |
投稿日時: 23/04/30 12:26:10
投稿者: がんばりやさん
|
---|---|
|