一、题目描述
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。
示例 1:
输入: 1
输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]
示例 2:
输入: 2
输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]
二、思路分析
- 题目要求:求解每种情况的概率,概率 = 每种情况出现次数/总次数,总次数很简单即可重复排列 6n,问题需要先计算结果有多少种,以及每种出现的次数。
- 每个骰子可能有六种结果,即 1-6,那么一个筛子会出现如下结果:
- 在步骤2基础上加一个筛子,会出现以下情况:
将步骤2与步骤3结果放在一块对齐如下:
发现规律如下:(博主也是辗转反侧得出正确结果,并非一次猜测就正确)
- 骰子和会落在,n - 6 * n 这个区间(区间长度 len = 6*n-n+1 = 5n + 1,加 1 的原因是含起始数字)
- 骰子数为 N,骰子和 F(n, t) 为,n - 1 个筛子,F(n-1, t - 1) + F(n-1, t - 2) + F(n-1, t-3) + F(n-1, t-4) + F(n-1, t-5) + F(n-1, t-6),注意这里有边界条件,如 F(2, 3) = F(1, 2) + F(1, 1),F(2, 7) = F(1, 1) + F(1, 2) + F(1, 3) + F(1, 4) + F(1, 5) + F(1, 6)。
核心公式
f
(
n
,
t
)
=
f
(
n
−
1
,
t
−
1
)
+
f
(
n
−
1
,
t
−
2
)
+
f
(
n
−
1
,
t
−
3
)
+
f
(
n
−
1
,
t
−
4
)
+
f
(
n
−
1
,
t
−
5
)
+
f
(
n
−
1
,
t
−
6
)
f(n, t) = f(n-1, t - 1) + f(n-1, t - 2) + f(n-1, t-3) + f(n-1, t-4) + f(n-1, t-5) + f(n-1, t-6)
f(n,t)=f(n−1,t−1)+f(n−1,t−2)+f(n−1,t−3)+f(n−1,t−4)+f(n−1,t−5)+f(n−1,t−6)
注意:此处有坑,注意边界条件,后面公式最多六个。
公式推导依据
上面展示 步骤2与步骤3结果图,可以观察到,每增加一个筛子,给现有情况都增加了六个可能性,并且这个可能性在 +1 到 +6 范围内,因此会有上文公式。
举个例子:n = 2,sum = 4 时,sum = 4 的组合可能来自于,n = 1 时的,(1 + 3,2 + 2,3+1),因此,sum = 4 的组合情况有 1 + 1 + 1 = 3 种可能。
计算出每种情况出现的次数后,概率就很简单了。
三、参考代码
class Solution {
public double[] dicesProbability(int n) {
int[] a = {1, 1, 1, 1, 1, 1};//最初形式
int num = (int)Math.pow(6, n);// 共有这些可能
int len = 5 * n + 1;
int[][] r = new int[n][len];//二维数组,对应 i,j 指的是 i个筛子,和为 j 的个数
double[] r2 = new double[len];
for(int j = 0; j < 6; j++)
r[0][j] = a[j];
for(int i = 1; i < n; i++){// 从 n 等于 1 开始逐个推导至 n
int t = 5 * i + 6;// (i+1) * 6 - (i+1-1)
for(int j = 0; j < t; j++){
int t2 = 0;
for(int k = (j - 5) > 0 ? j - 5 : 0; k <= j; k++)
t2 += r[i - 1][k];//含当前数,往前五个
r[i][j] = t2;
}
}
for(int j = 0; j < len; j++){//根据推导结果计算比例
r2[j] = 1.0 * r[n-1][j] / num;
}
return r2;
}
}