【问题标题】:Avoid multiple loops in JS implementation of Bonferroni inequalityBonferroni不等式的JS实现中避免多重循环
【发布时间】:2020-02-18 06:53:54
【问题描述】:

我试图实现“Bonferroni 不等式”,它使用 Javascript UDF 对 GCP BigQuery 上的数据科学用例的许多独立事件的联合概率进行建模。但是我对 JS 很陌生,也不知道有什么好的做法。

要应用的公式如下:

P(U Ai) = SUM(P(Ai)) - SUM(P(Ai)*P(Aj)) + SUM(P(Ai)*P(Aj)*P(Ak) - ... i != j != k

我对该函数的输入是单个事件概率的数组:

[P(A1), P(A2), P(A3), ...] 

我本能地在行中创建了“for循环”以获得结果,但是,看到这么丑陋的代码很痛苦,所以想知道你们中是否有人知道如何以更优雅和优化的方式实现它?

这是我为 4 级 Bonferroni 不等式编写的函数:

function unionBoundProbability(probList){

  var intersection2 = 0;
  var intersection3 = 0;
  var intersection4 = 0;
  var i = 0;
  var j = 0;
  var k = 0;
  var l = 0;
  var sum = 0;
  var product = 1;

  var sum = probList.reduce((a, b) => a + b, 0);

  for(i = 0; i < probList.length; i++){
    product *= probList[i];
    for(j = i+1; j < probList.length; j++){
      intersection2 += probList[i]*probList[j];
      for(k = j+1; k < probList.length; k++){
        intersection3 += probList[i]*probList[j]*probList[k];
        for(l = k+1; l < probList.length; l++){
          intersection4 += probList[i]*probList[j]*probList[k]*probList[l];
        }
      }
    }
  }

  switch (probList.length) {
    case 0:
      return 0;
      break;
    case 1:
      return probList[0];
      break;
    case 2:
      return sum - product;
      break;
    case 3:
      return sum - intersection2 + product;
      break
    case 4:
      return sum - intersection2 + intersection3 - product;
    case 5 :
      return sum - intersection2 + intersection3 - intersection4 + product;
    default:
      return Math.max((sum - intersection2 + intersection3  - intersection4), Math.max.apply(Math, probList));
  }
}

我要做的是计算作为输入传递的所有概率的并集概率的近似值。

如果我的概率小于 5,则 switch 语句将应用精确的公式。否则,默认情况下应用 Bonferroni 近似,(因为我正在对接收信号的机会进行建模,如果估计值小于使用最佳天线的概率,那么我会保留最好的天线)。

感谢您的帮助

【问题讨论】:

  • 我建议你阅读递归并实现递归函数作为解决方案
  • 代码有效吗?你有像probList 这样的数据吗?可能还有其他缺失的信息和想要的结果?
  • 您可能希望删除 JavaScript 标记,因为这是一个不特定于 JavaScript 的一般编程问题
  • 层数是外部参数还是应该从probList派生?
  • 你好像没有在返回值中使用intersection4...

标签: javascript algorithm loops


【解决方案1】:

此示例遵循来自https://www.probabilitycourse.com/chapter6/6_2_1_union_bound_and_exten.php 的以下等式

P(⋃(i=1 => n)Ai)=∑(i=1 => n) P(Ai) − ∑(i<j) P(Ai ∩ Aj) + ∑(i<j<k) P(Ai ∩ Aj ∩ Ak) − ... +(−1)^n−1 P(⋂(i=1 => n) Ai)

我不知道您在给出的示例中包含阶乘的原因,但我没有包含阶乘,因为它们不在上述等式中。

// Recursive function to update sums of each level
function updateSums(sums, probList, maxLevel, currentLevel = 1, currentProduct = 1, lastIndex = -1) {
  // Example case: maxLevel = 4, curentLevel = 3, path = { 1: 0, 2: 1 }, currentProduct = probList[0] * probList[1]
  // Loops through all entries except 0 and 1 and adds the products to sums[2], for each entry also calculates level 4 sums

  for (let i = lastIndex + 1; i < probList.length; i++) {
    const nextProduct = currentProduct * probList[i];
    sums[currentLevel - 1] += nextProduct;

    if (currentLevel < maxLevel) {
      // get the next level product sums for current product
      updateSums(sums, probList, maxLevel, currentLevel + 1, nextProduct, i);
    }
  }
}

// Main function
function inequality(probList) {
  probList = probList.sort((a, b) => b - a).slice(0, 4);

  // Calculate maxLevel
  const maxLevel = probList.length;
  if (!maxLevel) return 0;

  // create am array of sums, each entry represents 1 level
  const sums = (new Array(maxLevel)).fill(0);
  updateSums(sums, probList, maxLevel);

  return sums.reduce((a, c, i) => {
    return a + ((i % 2) ? -1 : 1) * c;
  }, 0);
}

console.log(inequality(probList));

PS:这是用 ES6 编写的

【讨论】:

  • César 引入了阶乘,因为如果您查看他的循环,他从 idx 0 开始,因此对于 (i,j,k),他还处理 (j,i,k),(j,k, i)...(3 个元素的排列)因此 2!, 3!, 4!, ...取决于嵌套
  • 哦,明白了,他正在进行排列并将它们转换为组合。如果我们可以确保没有重复,则不需要阶乘。谢谢解释
  • 没错,我实际上通过使用上面建议的不等式 i
  • 我试过你的代码,但它只会给我 0 ......否则,我同意递归解决了重叠 for 循环问题的事实。
  • 这是一个有效的答案。当级别数增加时,我没有使用递归来故意停止该过程,但我可以将它作为函数中的额外参数传递。谢谢
【解决方案2】:

我们可以避免再次发生

A 大小为n

根据您的公式我们可以考虑

  • 我们从 A 中获取 1 个元素:C_n^1
  • 我们从 A 中获取 2 个元素:C_n^2
  • 我们从 A 中获取 3 个元素:C_n^3

我们可以简单地保留上一层的(k-1)-utuples,而不是重新计算每个k-utuple(无序元组) 例如让我们采用数组 [1,2,3,4,5]

  • 第一层:1,2,3,4,5
  • 第二层:1-2, 1-3, 1-4, 1-5, 2-3, 2-4, ..., 4-5
  • 第三层:1-2-{i}(i for 3 to 5), 1-3-{i}, ...

对于我们的例子:我们并不真正需要整个 utuple:只需要它的最后一个 idx 和它的值(它的元素的乘积)

算法就像

function bonferroni(A, nlev){
    let lv = 0;
    let tot = 0;
    //i refers to the index of the last element added to the tuple
    //s refers to its value
    //here we initialize with i=-1 just so the first while loop builds an equivalent of "A"
    let layer = [{i:-1, s:1}];
    while(lv < nlev){
        let sum = 0;
        let next = [];
        layer.forEach(utuple=>{

            for(let i = utuple.i+1; i<A.length; ++i){
                let s = utuple.s * A[i];
                sum += s;
                next.push({i, s});
            }
        })
        layer = next;
        if((lv % 2)==0){
            tot += sum;
        }else{
            tot -= sum;
        }
        lv++;
    }
    return tot;
}

详细版本是:

function bonferroniVerbose(A, nlev){
    let lv = 0;
    let tot = 0;
    //i refers to the index of the last element added to the tuple
    //s refers to its value
    //here we initialize with i=-1 just so the first while loop builds an equivalent of "A"
    let layer = [{t:[], i:-1, s:1}];
    while(lv < nlev){
        console.log('--------------layer', lv);
        let sum = 0;
        let next = [];
        layer.forEach(utuple=>{

            for(let i = utuple.i+1; i<A.length; ++i){
                let s = utuple.s * A[i];
                sum += s;
                let t = utuple.t.concat(A[i]);
                next.push({t, i, s});
                console.log('summing', t.join('*'), '->', s);
            }
        })
        layer = next;
        if((lv % 2)==0){
            tot += sum;
        }else{
            tot -= sum;
        }
        lv++;
    }
    return tot;
}
console.log(bonferroniVerbose([1,2,3,4,5], 3))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-26
    • 1970-01-01
    • 1970-01-01
    • 2021-06-21
    • 2020-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多