プロが教えるわが家の防犯対策術!

エクセルで30行に0-4の間の乱数を発生させて合計数を常に100にするにはどうすればいいのでしょうか。
VBAではないのですがやりたいことはCのほうの過去ログにあったのですが
http://oshiete1.watch.impress.co.jp/qa4035587.html
見ても全くわかりませんでした。

A 回答 (4件)

>エクセルで30行に0-4の間の乱数を発生させて合計数を常に100



乱数  個数 合計
  0   0
  1   0
  2   0    0
  3  20    60
  4  10    40   0から4の数値、30個の合計が100になる為には
─────────  4の数値が最低限10個、必要
     30   100

4の出る確率を上げてみました。
Sub Test()
  Dim v(29)
  Dim i As Long, n As Long

  Randomize
  Do
    For i = 0 To 29
      n = Int(Rnd() * 8)
      If n > 4 Then n = 4
      v(i) = n
    Next
  Loop Until Application.Sum(v) = 100
  Range("A1").Resize(30).Value = Application.Transpose(v)
End Sub
    • good
    • 0
この回答へのお礼

数値を変えてもうまく動きました。教えいただきありがとうございます。簡潔なのと回答が早かったのでこちらをBAにさせていただきました。他の方も教えていただきありがとうございました。

お礼日時:2013/10/29 22:51

おもしろそうだったのでやってみました。



やってみたのは3パターン
・「test1」
 乱数でどこに、乱数で何(値 1 ~ 4)を、を求めて
 値を設定しないところは 0 のまま
・「test2」
 乱数でどこに、を求めて 1 を加算していく
 最悪無限ループに陥るかも
・「test3」
 test2 の改良バージョン

傾向としては、
test1 は、最大値がでやすい?(乱数でどこ・・・が同じなら加算していたので)
test2 / test3 は平均されやすい?(乱数の発生頻度と同じ?)

0 の出現は、 test2 / test3 では少ない様な気がします。
(乱数なので、そういうものとしても良いのかも)

While 内の実行回数の少ない順(変数 k の値)は、
 test1(100回以下) < test3(100回) < test2(100回以上)
の様な感じ(雰囲気で)

説明は省いても良いですかね

確かめられるのであれば、
  Const CNUMSUM As Long = 100 ' 合計値
  Const CROWMAX As Long = 30 ' 行数
  Const CNUMMAX As Long = 4  ' 最大値
の値を変更して、
 CROWMAX * CNUMMAX > CNUMSUM になっていればソコソコ動くと思います。


Public Sub test1()
  Const CNUMSUM As Long = 100
  Const CROWMAX As Long = 30
  Const CNUMMAX As Long = 4
  Dim iAry(1 To CROWMAX) As Long
  Dim i As Long, k As Long
  Dim iNum As Long, iR As Long

  Randomize
  For i = 1 To CROWMAX
    iAry(i) = 0
  Next

  k = 0
  iNum = CNUMSUM
  While (iNum > 0)
    k = k + 1
    i = Int(CROWMAX * Rnd()) + 1
    iR = Int((CNUMMAX) * Rnd()) + 1
    If (iAry(i) + iR > CNUMMAX) Then iR = CNUMMAX - iAry(i)
    If (iR > iNum) Then iR = iNum
    iAry(i) = iAry(i) + iR
    iNum = iNum - iR
  Wend
  Debug.Print "k = " & k

  Range("A1").Resize(CROWMAX) = _
      WorksheetFunction.Transpose(iAry)
End Sub

Public Sub test2()
  Const CNUMSUM As Long = 100
  Const CROWMAX As Long = 30
  Const CNUMMAX As Long = 4
  Dim iAry(1 To CROWMAX) As Long
  Dim i As Long, k As Long
  Dim iNum As Long

  Randomize
  For i = 1 To CROWMAX
    iAry(i) = 0
  Next

  k = 0
  iNum = CNUMSUM
  While (iNum > 0)
    k = k + 1
    i = Int(CROWMAX * Rnd()) + 1
    If (iAry(i) < CNUMMAX) Then
      iAry(i) = iAry(i) + 1
      iNum = iNum - 1
    End If
  Wend
  Debug.Print "k = " & k

  Range("B1").Resize(CROWMAX) = _
      WorksheetFunction.Transpose(iAry)
End Sub

Public Sub test3()
  Const CNUMSUM As Long = 100
  Const CROWMAX As Long = 30
  Const CNUMMAX As Long = 4
  Dim iAry(1 To CROWMAX) As Long, iPos(1 To CROWMAX) As Long
  Dim i As Long, j As Long, k As Long
  Dim iNum As Long, iHdn As Long

  Randomize
  For i = 1 To CROWMAX
    iAry(i) = 0
    iPos(i) = i
  Next

  k = 0
  iNum = CNUMSUM
  iHdn = 0
  While (iNum > 0)
    k = k + 1
    j = Int((CROWMAX - iHdn) * Rnd()) + 1
    i = iPos(j)
    iAry(i) = iAry(i) + 1
    If (iAry(i) >= CNUMMAX) Then
      iPos(j) = iPos(CROWMAX - iHdn)
      iHdn = iHdn + 1
    End If
    iNum = iNum - 1
  Wend
  Debug.Print "k = " & k

  Range("C1").Resize(CROWMAX) = _
      WorksheetFunction.Transpose(iAry)
End Sub
    • good
    • 0

「30個の乱数を並べて100でなかったらやり直し」ってのが理屈は簡単そうです。



でも、おそらく時間がかかると思うので、合計100になる状態を作っておいて(例えば10個4で20個3みたいな感じ)あとは、好みの回数だけ、ランダムな所から1引いて、ランダムな所に1加えるって作業を繰り返せば、それなりの状況が出来るのではないでしょうか?(もちろん0から1引いたり4に1足したらいけませんからその手の処理は必要です。)

その他であれば、30カ所の中のランダムな所に1を加えるという作業を100回繰り返すって方が、速度的には速いかも。(もちろん4だったら1は足しません)

お試しください。
    • good
    • 0

言語が違っても、同じやり方のプログラムは作れます。




別解:
「1~30の乱数 r を発生させ、 r行目を+1する。既に4なら4でない行になるまで乱数発生を繰り返す」を100回繰り返す
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています