一个 JavaScript 函数,它从数组中选择一个随机整数,目标是在多次运行中达到指定的小数平均值

问题描述 投票:0回答:1

我正在尝试编写一个 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,但有时必须返回它。

javascript statistics weighted-average
1个回答
2
投票

哦,这让我回到了数学课。我记得泊松分布,它对具有高度可预测结果的事件进行建模。例如,在足球比赛中,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; }

我想知道如果我的老师们读到这篇文章,他们会感到骄傲还是羞愧死

© www.soinside.com 2019 - 2024. All rights reserved.