回答編集履歴

3

繰り返し二乗法

2020/08/03 22:48

投稿

Penpen7
Penpen7

スコア698

test CHANGED
@@ -14,6 +14,8 @@
14
14
 
15
15
  指数の足し算を行ったあと、実際に掛け算を行って、有理数(powplus/powminus)を求めてやります。
16
16
 
17
+ (なお、掛け算時に累乗を計算する必要がありますが、これは繰り返し二乗法を使って高速化します。)
18
+
17
19
  ここまでは整数同士の掛け算割り算なので誤差はありませんが、最後に割り算を行い小数に直します。
18
20
 
19
21
  (試作のためpythonですが, 流れさえわかれば, javaでも組めると思います。javaでも書こうと思いましたが、pythonで力尽きました)

2

pythonによるテスト

2020/08/03 22:48

投稿

Penpen7
Penpen7

スコア698

test CHANGED
@@ -5,3 +5,227 @@
5
5
 
6
6
 
7
7
  二項分布にはほかの分布で近似されることが知られています。正規分布(np>5 np(1-p)>5のとき) あるいはポアソン分布(nが大きくpが十分に小さい場合)で近似できるため、こころへんの性質をつかうといいのではないでしょうか。
8
+
9
+
10
+
11
+ 追記) 愚直に計算を行うのではなく、前処理で素因数分解を行うことで高速(マイクロ秒くらい)に解けることを確認しました。
12
+
13
+ まずは、エラトステネスの篩で素数列挙を行い, 通常の整数では素数で順番に割り算してどこまで割れるか試していき、階乗ではルジャンドルの定理を使って、素因数分解を行います。
14
+
15
+ 指数の足し算を行ったあと、実際に掛け算を行って、有理数(powplus/powminus)を求めてやります。
16
+
17
+ ここまでは整数同士の掛け算割り算なので誤差はありませんが、最後に割り算を行い小数に直します。
18
+
19
+ (試作のためpythonですが, 流れさえわかれば, javaでも組めると思います。javaでも書こうと思いましたが、pythonで力尽きました)
20
+
21
+
22
+
23
+ ```python
24
+
25
+ from decimal import *
26
+
27
+ # 素数列挙(エラトステネスの篩)
28
+
29
+ def eratosthenes(n):
30
+
31
+ primeNumbers = []
32
+
33
+ isprime = [True]*(n+1)
34
+
35
+ for i in range(2, n+1):
36
+
37
+ if(isprime[i]):
38
+
39
+ primeNumbers.append(i)
40
+
41
+ temp = i
42
+
43
+ while(temp <= n):
44
+
45
+ isprime[temp] = False
46
+
47
+ temp += i
48
+
49
+ return primeNumbers
50
+
51
+
52
+
53
+ # n!を素因数分解(ルジャンドルの定理)
54
+
55
+ def primeFactorizeKaijo(n, primeNumbers):
56
+
57
+ assert(n <= max_int)
58
+
59
+ res = {}
60
+
61
+ for i in primeNumbers:
62
+
63
+ if(i > n):
64
+
65
+ break
66
+
67
+ temp = i
68
+
69
+ restemp = 0
70
+
71
+ while(temp <= n):
72
+
73
+ restemp += int(n/temp)
74
+
75
+ temp *= i
76
+
77
+ res[i] = restemp
78
+
79
+ return res
80
+
81
+
82
+
83
+ # nを素因数分解する
84
+
85
+ def primeFactorize(n, primeNumbers):
86
+
87
+ assert(n <= max_int)
88
+
89
+ res = {}
90
+
91
+ temp = n
92
+
93
+ for i in primeNumbers:
94
+
95
+ if(i > temp):
96
+
97
+ break
98
+
99
+ while(temp % i == 0):
100
+
101
+ if(i in res):
102
+
103
+ res[i] += 1
104
+
105
+ else:
106
+
107
+ res[i] = 1
108
+
109
+ temp /= i
110
+
111
+ return res
112
+
113
+ def calculatePowMul(ans, fact, power):
114
+
115
+ for k, v in fact.items():
116
+
117
+ ans[k] += v*power
118
+
119
+
120
+
121
+ # nCk * p^k * q^{n-k}
122
+
123
+ n = 10000
124
+
125
+ k = 5000
126
+
127
+
128
+
129
+ # p = [pの分子, pの分母]
130
+
131
+ p = [1, 65535]
132
+
133
+ max_int = max([n, k, p[0], p[1]])
134
+
135
+
136
+
137
+ # 素因数分解
138
+
139
+ primeNumbers = eratosthenes(max_int)
140
+
141
+ pbunbo_fact = primeFactorize(p[1], primeNumbers)
142
+
143
+ pbunshi_fact = primeFactorize(p[0], primeNumbers)
144
+
145
+ qbunshi_fact = primeFactorize(p[1]-p[0], primeNumbers)
146
+
147
+ nkaijo_fact = primeFactorizeKaijo(n, primeNumbers)
148
+
149
+ kkaijo_fact = primeFactorizeKaijo(k, primeNumbers)
150
+
151
+ nmkkaijo_fact = primeFactorizeKaijo(n-k, primeNumbers)
152
+
153
+
154
+
155
+ # 指数同士の足し算をおこない、掛け算する
156
+
157
+ ans = [0]*(max_int+1)
158
+
159
+ calculatePowMul(ans, pbunbo_fact, -n)
160
+
161
+ calculatePowMul(ans, pbunshi_fact, k)
162
+
163
+ calculatePowMul(ans, qbunshi_fact, n-k)
164
+
165
+ calculatePowMul(ans, nkaijo_fact, 1)
166
+
167
+ calculatePowMul(ans, kkaijo_fact, -1)
168
+
169
+ calculatePowMul(ans, nmkkaijo_fact, -1)
170
+
171
+
172
+
173
+ # 指数の正負で分ける(0は入れない)
174
+
175
+ plus = {}
176
+
177
+ minus = {}
178
+
179
+ for i in primeNumbers:
180
+
181
+ if(ans[i] > 0):
182
+
183
+ plus[i] = ans[i]
184
+
185
+ if(ans[i] < 0):
186
+
187
+ minus[i] = abs(ans[i])
188
+
189
+
190
+
191
+ # 実際に掛け算を行う(
192
+
193
+ powplus = 1
194
+
195
+ for i in plus:
196
+
197
+ powplus *= pow(i, plus[i])
198
+
199
+ powminus = 1
200
+
201
+ for i in minus:
202
+
203
+ powminus *= pow(i, minus[i])
204
+
205
+
206
+
207
+ # 有効桁数を50桁とする
208
+
209
+ getcontext().prec = 50
210
+
211
+
212
+
213
+ # 実際に割って小数にする
214
+
215
+ print(Decimal(powplus)/Decimal(powminus))
216
+
217
+ ```
218
+
219
+
220
+
221
+ ```text
222
+
223
+ CPU times: user 3 µs, sys: 0 ns, total: 3 µs
224
+
225
+ Wall time: 5.25 µs
226
+
227
+ 6.3420873578683450119531499686198773734249989811395E-21075
228
+
229
+ ```
230
+
231
+ ほぼ無視できるような確率ですね。

1

修正

2020/08/03 22:32

投稿

Penpen7
Penpen7

スコア698

test CHANGED
@@ -4,4 +4,4 @@
4
4
 
5
5
 
6
6
 
7
- nが十分に大き場合、正規分布(np>5 np(1-p)>5) あるいはポアソン分布(nが大きくpが十分に小さい場合)づくため、こころへんの性質をつかうといいのではないでしょうか。
7
+ 二項はほかの分布で近似されることが知られてます。正規分布(np>5 np(1-p)>5のとき) あるいはポアソン分布(nが大きくpが十分に小さい場合)似できるため、こころへんの性質をつかうといいのではないでしょうか。