将一定数量的项目拟合为二项分布/钟形曲线
Fit a quantity of items to a binomial distribution / bell curve
这是一类离散数学积分问题 - 我需要在固定时间段内将固定数量的项目拟合成二项分布或钟形曲线。
假设我在 T
天内运送了总共 M
个箱子,其中 n
是 t
天到达的箱子数量。我需要一种方法来计算每一天 t 的 n(t),以便
Sum ( n(t) ) 0 -> t = M
t
是整数,n(t)
是整数,
n(t)
的形状尽可能接近钟形曲线。
编辑
万一有人认为这是一个非常有价值的问题,这里是 Javascript 我根据 Yves Daoust 答案中的指针拼凑而成。
/*
See
*/
function normal(x, mean, stdDev) {
return stdNormal(( x - mean ) / stdDev);
}
function stdNormal(z) {
// Power series is not stable at these extreme tail scenarios
if (z < -6) { return 0; }
if (z > 6) { return 1; }
let j, k ;
let m = 1; // m(k) == (2**k)/factorial(k)
let b = z; // b(k) == z ** (2*k + 1)
let z2 = z * z; // cache of z squared
let z4 = z2 * z2; // cache of z to the 4th
let values = [];
// Compute the power series in groups of two terms.
// This reduces floating point errors because the series
// alternates between positive and negative.
for (k=0; k<100; k+=2) {
const a = 2*k + 1;
let item = b / (a*m);
item *= (1 - (a*z2)/((a+1)*(a+2)));
values.push(item);
m *= (4*(k+1)*(k+2));
b *= z4;
}
// Add the smallest terms to the total first that
// way we minimize the floating point errors.
let total = 0;
for (k=49; k>=0; k--) {
total += values[k];
}
// Multiply total by 1/sqrt(2*PI)
// Then add 0.5 so that stdNormal(0) === 0.5
return 0.5 + 0.3989422804014327 * total;
}
/*
Compute the cdf of the binomial distribution between 0 and T, times M.
Round all values to integers. Let m(t) the numbers so obtained.
Use n(t) = m(t+1) - m(t). In doing so, you ensure Σ n(t) = n(T) - n(0) = M.
Thanks to Yves Daoust
*/
function distributeItems(itemsToPlace = 100, steps = 7) {
const mean = Math.floor((steps - 1) / 2);
const stdDev = mean / 2.96; /// for 'standard' std dev ;-)
const m = [0]; // cdf
const n = [0]; // items
for (var step = 1; step <= steps; step++) {
m.push(Math.round(normal(step, mean, stdDev) * itemsToPlace,0));
n.push( m[step] - m[step - 1] );
}
const interimCount = n.reduce(function (sum, elt) { return sum+elt; }, 0);
const discrepancy = itemsToPlace - interimCount;
if (discrepancy !==0) {
n[n.length-1] += discrepancy;
}
return n;
}
const n = distributeItems(40,7);
console.log('Items: ',n, 'Total: ',n.reduce(function (sum, elt) { return sum+elt; }, 0))
// Result
// [ 0, 1, 5, 14, 14, 5, 1, 0 ] 40
也许不是最优解,但应该不会太远。
计算 0 和 T 之间二项分布的 cdf,乘以 M。
将所有值四舍五入为整数。让 m(t) 得到这样得到的数字。
使用 n(t) = m(t+1) - m(t)。这样做时,您可以确保 Σ n(t) = n(T) - n(0) = M.
在下图中,蓝色曲线是精确二项式 N=7,p=0.3,针对 M=20 进行了调整。橙色曲线是通过上述过程获得的。如您所见,2+5+6+4+2+1+0+0=20.
这是一类离散数学积分问题 - 我需要在固定时间段内将固定数量的项目拟合成二项分布或钟形曲线。
假设我在 T
天内运送了总共 M
个箱子,其中 n
是 t
天到达的箱子数量。我需要一种方法来计算每一天 t 的 n(t),以便
Sum ( n(t) ) 0 -> t = M
t
是整数,n(t)
是整数,n(t)
的形状尽可能接近钟形曲线。
编辑
万一有人认为这是一个非常有价值的问题,这里是 Javascript 我根据 Yves Daoust 答案中的指针拼凑而成。
/*
See
*/
function normal(x, mean, stdDev) {
return stdNormal(( x - mean ) / stdDev);
}
function stdNormal(z) {
// Power series is not stable at these extreme tail scenarios
if (z < -6) { return 0; }
if (z > 6) { return 1; }
let j, k ;
let m = 1; // m(k) == (2**k)/factorial(k)
let b = z; // b(k) == z ** (2*k + 1)
let z2 = z * z; // cache of z squared
let z4 = z2 * z2; // cache of z to the 4th
let values = [];
// Compute the power series in groups of two terms.
// This reduces floating point errors because the series
// alternates between positive and negative.
for (k=0; k<100; k+=2) {
const a = 2*k + 1;
let item = b / (a*m);
item *= (1 - (a*z2)/((a+1)*(a+2)));
values.push(item);
m *= (4*(k+1)*(k+2));
b *= z4;
}
// Add the smallest terms to the total first that
// way we minimize the floating point errors.
let total = 0;
for (k=49; k>=0; k--) {
total += values[k];
}
// Multiply total by 1/sqrt(2*PI)
// Then add 0.5 so that stdNormal(0) === 0.5
return 0.5 + 0.3989422804014327 * total;
}
/*
Compute the cdf of the binomial distribution between 0 and T, times M.
Round all values to integers. Let m(t) the numbers so obtained.
Use n(t) = m(t+1) - m(t). In doing so, you ensure Σ n(t) = n(T) - n(0) = M.
Thanks to Yves Daoust
*/
function distributeItems(itemsToPlace = 100, steps = 7) {
const mean = Math.floor((steps - 1) / 2);
const stdDev = mean / 2.96; /// for 'standard' std dev ;-)
const m = [0]; // cdf
const n = [0]; // items
for (var step = 1; step <= steps; step++) {
m.push(Math.round(normal(step, mean, stdDev) * itemsToPlace,0));
n.push( m[step] - m[step - 1] );
}
const interimCount = n.reduce(function (sum, elt) { return sum+elt; }, 0);
const discrepancy = itemsToPlace - interimCount;
if (discrepancy !==0) {
n[n.length-1] += discrepancy;
}
return n;
}
const n = distributeItems(40,7);
console.log('Items: ',n, 'Total: ',n.reduce(function (sum, elt) { return sum+elt; }, 0))
// Result
// [ 0, 1, 5, 14, 14, 5, 1, 0 ] 40
也许不是最优解,但应该不会太远。
计算 0 和 T 之间二项分布的 cdf,乘以 M。
将所有值四舍五入为整数。让 m(t) 得到这样得到的数字。
使用 n(t) = m(t+1) - m(t)。这样做时,您可以确保 Σ n(t) = n(T) - n(0) = M.
在下图中,蓝色曲线是精确二项式 N=7,p=0.3,针对 M=20 进行了调整。橙色曲线是通过上述过程获得的。如您所见,2+5+6+4+2+1+0+0=20.