質問するログイン新規登録

回答編集履歴

3

文面の微修正とコード添付

2021/01/06 10:07

投稿

swordone
swordone

スコア20675

answer CHANGED
@@ -1,9 +1,9 @@
1
1
  0以上の整数で分配するという前提で回答します。
2
2
  [重複組み合わせ](https://mathtrain.jp/tyohukuc)の考え方を利用すると、
3
3
  「分配の仕方」は「仕切りの入れ方」ととらえることができます。
4
- そこで、「仕切りを入れる場所」として、0~(分配する数+分配する個数-2)の乱数を
4
+ そこで、「仕切りを入れる場所」として、0以上(分配する数+分配する個数-1)未満の乱数を
5
5
  重複しないように(分配する個数-1)個取れば、どの分け方も等確率で得られます。
6
- 例えば、例の場合、つまり10を3つに分配する場合、10+3-2=11なので、
6
+ 例えば、例の場合、つまり10を3つに分配する場合、10+3-1=12なので、
7
7
  0~11の12個から乱数を2つ取得します。
8
8
  乱数で得られた位置に仕切りを入れることで、分配の仕方が以下のように対応します。
9
9
  (位置を示す番号は16進表記)
@@ -15,4 +15,57 @@
15
15
  重複のないようにするには、0~(分配する数+1)の配列などを作って、
16
16
  シャッフルするかフィッシャー・イェーツの方法を利用するのが確実になると思いますが、
17
17
  分配する数より分配する個数が十分少なければ、逐次乱数を取り出して重複チェックするのでも
18
- 十分実用に足りると思います。
18
+ 十分実用に足りると思います。
19
+
20
+ Javaしかできないので、Javaで実装してみました。
21
+ 仕切りが〇より多くなる場合は、逆に〇を入れる位置を考える、という考え方をしています。
22
+ ```java
23
+ import java.util.*;
24
+
25
+ public class Main {
26
+ public static void main(String[] args) {
27
+ for (int i = 0; i < 10; i++) {
28
+ System.out.println(Arrays.toString(randomSplit(10,3)));
29
+ }
30
+ }
31
+
32
+ private static int[] randomSplit(int n, int p) {
33
+ int r = p - 1;
34
+ int total = n + r;
35
+ boolean wall = total > r * 2;
36
+ if (!wall) {
37
+ r = n;
38
+ }
39
+ BitSet set = new BitSet(total);
40
+ while (set.cardinality() < r) {
41
+ set.set((int)(Math.random() * total));
42
+ }
43
+ if (!wall) {
44
+ set.flip(0, total);
45
+ }
46
+
47
+ int[] result = new int[p];
48
+ int prev = 0;
49
+ for (int i = 0; i < r; i++) {
50
+ int next = set.nextSetBit(prev);
51
+ result[i] = next - prev;
52
+ prev = next + 1;
53
+ }
54
+ result[r] = total - prev;
55
+ return result;
56
+ }
57
+ }
58
+ ```
59
+ 実行結果(10回ループしています)
60
+ ```
61
+ [6, 0, 4]
62
+ [2, 0, 8]
63
+ [1, 8, 1]
64
+ [1, 3, 6]
65
+ [7, 1, 2]
66
+ [5, 4, 1]
67
+ [0, 1, 9]
68
+ [5, 0, 5]
69
+ [2, 1, 7]
70
+ [5, 5, 0]
71
+ ```

2

乱数の範囲を修正

2021/01/06 10:06

投稿

swordone
swordone

スコア20675

answer CHANGED
@@ -1,8 +1,10 @@
1
1
  0以上の整数で分配するという前提で回答します。
2
- [重複組み合わせ](https://mathtrain.jp/tyohukuc)の考え方を利用すると、「分配の仕方」は、「仕切りの入れ方」ととらえることができます。
2
+ [重複組み合わせ](https://mathtrain.jp/tyohukuc)の考え方を利用すると、
3
+ 「分配の仕方」は「仕切りの入れ方」ととらえることができます。
3
- そこで、「仕切りを入れる場所」として、0~(分配する数+1)の乱数を重複しないように(分配する個数-1)個取れば、
4
+ そこで、「仕切りを入れる場所」として、0~(分配する数+分配する個数-2)の乱数を
4
- どの分け方も等確率で得られます。
5
+ 重複しないように(分配する個数-1)個取れば、どの分け方も等確率で得られます。
5
- 例えば、例の場合、つまり10を3つに分配する場合、0~11の12個から乱数を2つ取得します。
6
+ 例えば、例の場合、つまり10を3つに分配する場合、10+3-2=11で、
7
+ 0~11の12個から乱数を2つ取得します。
6
8
  乱数で得られた位置に仕切りを入れることで、分配の仕方が以下のように対応します。
7
9
  (位置を示す番号は16進表記)
8
10
  ```plain
@@ -12,4 +14,5 @@
12
14
  ```
13
15
  重複のないようにするには、0~(分配する数+1)の配列などを作って、
14
16
  シャッフルするかフィッシャー・イェーツの方法を利用するのが確実になると思いますが、
15
- 分配する数より分配する個数が十分少なければ、逐次乱数を取り出して重複チェックするのでも十分実用に足りると思います。
17
+ 分配する数より分配する個数が十分少なければ、逐次乱数を取り出して重複チェックするのでも
18
+ 十分実用に足りると思います。

1

仕切り表現をマークダウン

2021/01/06 09:26

投稿

swordone
swordone

スコア20675

answer CHANGED
@@ -3,8 +3,13 @@
3
3
  そこで、「仕切りを入れる場所」として、0~(分配する数+1)の乱数を重複しないように(分配する個数-1)個取れば、
4
4
  どの分け方も等確率で得られます。
5
5
  例えば、例の場合、つまり10を3つに分配する場合、0~11の12個から乱数を2つ取得します。
6
- 「3,7」が出れば、〇〇〇|〇〇〇|〇〇〇〇 「3,3,4」の分け方に、
7
- 「9,10」なば 〇〇〇〇〇〇〇〇〇||〇 「9,0,1」の方に対応します。
6
+ 乱数で得れた位置に仕切りを入れることで、配の仕が以下のように対応します。
7
+ (位置を示す番号は16進表記)
8
+ ```plain
9
+ 0 1 2 3 4 5 6 7 8 9 A B
10
+ 3,7 -> o o o | o o o | o o o o -> 3,3,4
11
+ 9,10 -> o o o o o o o o o | | o -> 9,0,1
12
+ ```
8
13
  重複のないようにするには、0~(分配する数+1)の配列などを作って、
9
14
  シャッフルするかフィッシャー・イェーツの方法を利用するのが確実になると思いますが、
10
15
  分配する数より分配する個数が十分少なければ、逐次乱数を取り出して重複チェックするのでも十分実用に足りると思います。