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

回答編集履歴

4

マークダウン恵助@ぷ

2025/01/26 06:50

投稿

swordone
swordone

スコア20675

answer CHANGED
@@ -1,48 +1,48 @@
1
- 常用対数で計算するなら、全部常用対数で計算すればいいのでは?
1
+ 常用対数で計算するなら、全部常用対数で計算すればいいのでは?
2
- 階乗が絡んでくると、掛け算ではちょっと数が大きくなればすぐオーバーフローしますよ。
2
+ 階乗が絡んでくると、掛け算ではちょっと数が大きくなればすぐオーバーフローしますよ。
3
-
3
+
4
- 条件を満たす投票パターンは「同じものを含む順列」の公式から
4
+ 条件を満たす投票パターンは「同じものを含む順列」の公式から
5
- N!/(r1!*r2!*...*rM!)通りとなるので、
5
+ N!/(r1!\*r2!\*...\*rM!)通りとなるので、
6
- これを全投票パターンのM^N通りで割れば確率pになります。
6
+ これを全投票パターンのM^N通りで割れば確率pになります。
7
- p≧10^(-x) ⇔ log10(p)≧-x ⇔ -log10(p)≦xなので、
7
+ p≧10^(-x) ⇔ log10(p)≧-x ⇔ -log10(p)≦xなので、
8
- pの常用対数を取って、-1倍して天井関数を取れば、条件を満たす最小の整数xが求められます。
8
+ pの常用対数を取って、-1倍して天井関数を取れば、条件を満たす最小の整数xが求められます。
9
- pの常用対数の計算において、対数の性質から
9
+ pの常用対数の計算において、対数の性質から
10
- log10(p)=log10(N!)-{log10(r1!)+log10(r2!)+...+log10(rM!)}-N*log10(M)
10
+ log10(p)=log10(N!)-{log10(r1!)+log10(r2!)+...+log10(rM!)}-N\*log10(M)
11
- であり、階乗の定義と対数の性質から自然数nに対し、
11
+ であり、階乗の定義と対数の性質から自然数nに対し、
12
- log10(n!)=log10(1)+log10(2)+...+log10(n)
12
+ log10(n!)=log10(1)+log10(2)+...+log10(n)
13
- なので、掛け算を使うことなく計算が可能です。
13
+ なので、掛け算を使うことなく計算が可能です。
14
- log10(N!)はループで計算することになりますが、その過程でN以下のすべての自然数nに対するlog10(n!)の値が出てくることになるので、それを配列に保存しておけば、log10(ri!)に再利用できます。
14
+ log10(N!)はループで計算することになりますが、その過程でN以下のすべての自然数nに対するlog10(n!)の値が出てくることになるので、それを配列に保存しておけば、log10(ri!)に再利用できます。
15
- なお、0!=1に注意してください。
15
+ なお、0!=1に注意してください。
16
-
16
+
17
- Cのコードは書いたことがないのですが、たぶんこんな感じ?
17
+ Cのコードは書いたことがないのですが、たぶんこんな感じ?
18
- ポインタなども理解が十分でないため、見様見真似で書いてます。
18
+ ポインタなども理解が十分でないため、見様見真似で書いてます。
19
- ```C
19
+ ```C
20
- int main(void){
20
+ int main(void){
21
- int N,M;
21
+ int N,M;
22
- double *logs;
22
+ double *logs;
23
-
23
+
24
- scanf("%d %d",&N,&M);
24
+ scanf("%d %d",&N,&M);
25
-
25
+
26
- logs=(double *)malloc(sizeof(double)*(N+1));
26
+ logs=(double *)malloc(sizeof(double)*(N+1));
27
- logs[0]=logs[1]=0.0; //log10(0!)=log10(1!)=0
27
+ logs[0]=logs[1]=0.0; //log10(0!)=log10(1!)=0
28
-
28
+
29
- double LOG10 = 0.0;
29
+ double LOG10 = 0.0;
30
-
30
+
31
- for(int i=2;i<=N;i++){ //log10(1)=0なのでスキップして2から
31
+ for(int i=2;i<=N;i++){ //log10(1)=0なのでスキップして2から
32
- LOG10+=log10(i);
32
+ LOG10+=log10(i);
33
- logs[i] = LOG10;
33
+ logs[i] = LOG10;
34
- }
34
+ }
35
-
35
+
36
- int r;
36
+ int r;
37
- for(int i=0;i<M;i++) {
37
+ for(int i=0;i<M;i++) {
38
- scanf("%d",&r);
38
+ scanf("%d",&r);
39
- LOG10 -= logs[r];
39
+ LOG10 -= logs[r];
40
- }
40
+ }
41
-
41
+
42
- LOG10 -= N*log10(M);
42
+ LOG10 -= N*log10(M);
43
- int ans = (int)ceil(-LOG10);
43
+ int ans = (int)ceil(-LOG10);
44
-
44
+
45
- printf("%d\n",ans);
45
+ printf("%d\n",ans);
46
- return 0;
46
+ return 0;
47
- }
47
+ }
48
48
  ```

3

コメント受け修正

2020/08/23 05:44

投稿

swordone
swordone

スコア20675

answer CHANGED
@@ -23,7 +23,7 @@
23
23
 
24
24
  scanf("%d %d",&N,&M);
25
25
 
26
- logs=(double *)malloc(sizeof(double)*N);
26
+ logs=(double *)malloc(sizeof(double)*(N+1));
27
27
  logs[0]=logs[1]=0.0; //log10(0!)=log10(1!)=0
28
28
 
29
29
  double LOG10 = 0.0;
@@ -39,7 +39,7 @@
39
39
  LOG10 -= logs[r];
40
40
  }
41
41
 
42
- LOG10 -= -N*log10(M);
42
+ LOG10 -= N*log10(M);
43
43
  int ans = (int)ceil(-LOG10);
44
44
 
45
45
  printf("%d\n",ans);

2

修正

2020/08/23 05:44

投稿

swordone
swordone

スコア20675

answer CHANGED
@@ -24,6 +24,7 @@
24
24
  scanf("%d %d",&N,&M);
25
25
 
26
26
  logs=(double *)malloc(sizeof(double)*N);
27
+ logs[0]=logs[1]=0.0; //log10(0!)=log10(1!)=0
27
28
 
28
29
  double LOG10 = 0.0;
29
30
 
@@ -35,12 +36,11 @@
35
36
  int r;
36
37
  for(int i=0;i<M;i++) {
37
38
  scanf("%d",&r);
38
- if (r <= 1) continue; //rが0か1なら、log10(r!)=0なのでスキップ
39
39
  LOG10 -= logs[r];
40
40
  }
41
41
 
42
42
  LOG10 -= -N*log10(M);
43
- int ans = ceil(-LOG10);
43
+ int ans = (int)ceil(-LOG10);
44
44
 
45
45
  printf("%d\n",ans);
46
46
  return 0;

1

対数計算の具体的戦略

2020/08/23 04:05

投稿

swordone
swordone

スコア20675

answer CHANGED
@@ -1,2 +1,48 @@
1
1
  常用対数で計算するなら、全部常用対数で計算すればいいのでは?
2
- 階乗が絡んでくると、掛け算ではちょっと数が大きくなればすぐオーバーフローしますよ。
2
+ 階乗が絡んでくると、掛け算ではちょっと数が大きくなればすぐオーバーフローしますよ。
3
+
4
+ 条件を満たす投票パターンは「同じものを含む順列」の公式から
5
+ N!/(r1!*r2!*...*rM!)通りとなるので、
6
+ これを全投票パターンのM^N通りで割れば確率pになります。
7
+ p≧10^(-x) ⇔ log10(p)≧-x ⇔ -log10(p)≦xなので、
8
+ pの常用対数を取って、-1倍して天井関数を取れば、条件を満たす最小の整数xが求められます。
9
+ pの常用対数の計算において、対数の性質から
10
+ log10(p)=log10(N!)-{log10(r1!)+log10(r2!)+...+log10(rM!)}-N*log10(M)
11
+ であり、階乗の定義と対数の性質から自然数nに対し、
12
+ log10(n!)=log10(1)+log10(2)+...+log10(n)
13
+ なので、掛け算を使うことなく計算が可能です。
14
+ log10(N!)はループで計算することになりますが、その過程でN以下のすべての自然数nに対するlog10(n!)の値が出てくることになるので、それを配列に保存しておけば、log10(ri!)に再利用できます。
15
+ なお、0!=1に注意してください。
16
+
17
+ Cのコードは書いたことがないのですが、たぶんこんな感じ?
18
+ ポインタなども理解が十分でないため、見様見真似で書いてます。
19
+ ```C
20
+ int main(void){
21
+ int N,M;
22
+ double *logs;
23
+
24
+ scanf("%d %d",&N,&M);
25
+
26
+ logs=(double *)malloc(sizeof(double)*N);
27
+
28
+ double LOG10 = 0.0;
29
+
30
+ for(int i=2;i<=N;i++){ //log10(1)=0なのでスキップして2から
31
+ LOG10+=log10(i);
32
+ logs[i] = LOG10;
33
+ }
34
+
35
+ int r;
36
+ for(int i=0;i<M;i++) {
37
+ scanf("%d",&r);
38
+ if (r <= 1) continue; //rが0か1なら、log10(r!)=0なのでスキップ
39
+ LOG10 -= logs[r];
40
+ }
41
+
42
+ LOG10 -= -N*log10(M);
43
+ int ans = ceil(-LOG10);
44
+
45
+ printf("%d\n",ans);
46
+ return 0;
47
+ }
48
+ ```