我正在尝试编写一个 JavaScript 函数,该函数将一个整数数组和一个十进制数作为参数,并随机选择其中一个整数,如果您从长远来看使用相同的两个参数运行该函数,你得到小数作为平均值。整数数组保证顺序连续,十进制数会小于最大整数,大于最小整数。
到目前为止我试过这个:
function weightedRandomInteger(arr, target) {
if (!Array.isArray(arr) || arr.length === 0 || typeof target !== "number") {
return null;
}
const min = arr[0];
const max = arr[arr.length - 1];
if (target < min || target > max) {
return null;
}
let weights = arr.map((num) => {
const distance = Math.abs(target - num);
return distance === 0 ? 1 : 1 / distance;
});
const totalWeight = weights.reduce((a, b) => a + b);
const probabilities = weights.map((weight) => weight / totalWeight);
const random = Math.random();
let cumulativeProbability = 0;
for (let i = 0; i < arr.length; i++) {
cumulativeProbability += probabilities[i];
if (random <= cumulativeProbability) {
return arr[i];
}
}
}
当目标值正好在arr的中间时,效果非常好
例如,如果我运行:
let total = 0;
let trials = 10000
for (let i = 0; i < trials; i++) {
total += weightedRandomInteger([0, 1, 2, 3, 4, 5, 6, 7], 3.5);
}
console.log("Average is ", total/trials)
我得到“平均值是 3.4819”
但是,如果我运行:
let total = 0;
let trials = 10000
for (let i = 0; i < trials; i++) {
total += weightedRandomInteger([0, 1, 2, 3, 4, 5, 6, 7], 0.2);
}
console.log("Average is ", total/trials)
我得到“平均值是 0.9428”
如果有足够的尝试,arr 中的所有值都将在某个点返回,这是绝对必要的。即使目标离边缘很远。没想到:
weightedRandomInteger([0, 1, 2, 3, 4, 5, 6, 7], 0.2)
经常返回 7,但有时必须返回它。
哦,这让我回到了数学课。我记得泊松分布,它对具有高度可预测结果的事件进行建模。例如,在足球比赛中,1:1 是很有可能的结果,而 9:2 几乎闻所未闻。这可以用泊松建模。
所以大多数结果都接近预期值,但如果尝试足够多,它确实分布在整个范围内。这听起来像你正在寻找的东西,虽然我不确定它是否传播得足够广泛并在边缘给你足够的价值。
它从 0 到 k,其中 k 是一个正整数,但你可以移动它。
举个例子:
function poissonDist(max, avg){
const factorial = (i) => i <= 0 ? 1 : i * factorial(i-1)
return Array.from({length: max+1}, (_,i) => avg**i * Math.exp(-avg) / factorial(i) )
}
function getLookup(max, avg){
const dist = poissonDist(max, avg)
const sumProps = dist.reduce((s,p) => s+p) // should be very close to 1, not sure if needed
return dist.reduce((l,p,i) => [...l, p / sumProps + (i && l[i-1])], []) // sum of all predecessors + current
}
function getVal(min, max, avg){
const lookup = getLookup(max-min, avg-min)
const val = Math.random()
return lookup.findIndex(p => p > val) + min
}
function getAverage(min, max, avg, runs){
const res = []
for(let i = 0; i < runs; i++){
res.push(getVal(min, max, avg))
}
const sum = res.reduce((s,n) => s+n)
console.log('Frequency:', res.reduce((f,n) => ({...f, [n]: (f[n]??0)+1}), {}))
const ravg = sum/runs
return ravg
}
console.log('Average:', getAverage(2, 10, 4.5, 100))
.as-console-wrapper { max-height: 100% !important; top: 0; }
我想知道如果我的老师们读到这篇文章,他们会感到骄傲还是羞愧死