【问题标题】:Mathematically perfect way to get an evenly sampled "chain" of items获得均匀采样的项目“链”的数学上完美的方法
【发布时间】:2014-09-27 00:51:10
【问题描述】:

假设我想递归地创建一个数字列表,方法是从 0 到 9 中随机选择一个数字开始,然后(可选)在 x+1 和 9 之间随机选择另一个数字,其中 x 是最后一个被选择的数字,选择性地重复这个过程。通过这种方式,您可以获得1,3,63,4,8,92,7 等列表。这是对我真正想做的事情的简化。

出于我的目的,我不想只是从列表中挑选一堆随机数,删除重复项,然后对它们进行排序。这需要递归地完成。问题是,以我描述的方式简单地做这件事不会给所有数字一个平等的机会。它倾向于选择较大的数字而不是较小的数字。我尝试了一些方法来抵消这一点,但它太偏爱小数字了。

这是使用 Lo-Dash 的随机整数函数的 javascript:

function randomlist(index) {
    if (index > 8) return;
    var range = _.random(9 - index);
    var randex = [_.random(index, index + range)];
    if (_.random(10) < 6) randex = randex.concat(randomlist(randex[0] + 1));
    return randex
}

tally = [0,0,0,0,0,0,0,0,0,0]
for (i=0;i<1000;i++) {
    var list = randomlist(0);
    list.forEach(function(x){tally[x]+=1});
}

在这里,而不是仅仅在 x 和 9 之间选择一个随机数,然后首先选择一个随机的较小范围,然后在其中选择一个随机数。我认为它可以解决问题,但是当我运行上面的代码时,tally 最终看起来像[278, 262, 224, 189, 217, 180, 185, 179, 156, 61],显然更倾向于较小的数字。如果我调整递归的机会,那么平衡就会发生一些变化。我希望可能有一些公式允许可调整的递归机会并设法生成对所有数字进行均匀采样的列表。

【问题讨论】:

  • 您是否错过了大数比小数多的事实,这就是为什么随机数函数倾向于更频繁地选择大数的原因?也许您应该将所有数字范围视为具有相同的位数——小的数字只是以 0 开头,例如 053 和 008。
  • 为什么不选择数字之间的距离,然后通过扫描计算数字呢?通过扫描,我的意思是 [2,4,1,5,2] 变为 [2,6,7,12,14]。
  • 你的概率分布是多少?您只想要前 10 个数字的均匀抽样随机子集吗?然后在每个递归步骤中,要么采用特定的项目,要么不采用。
  • @vernonner3voltazim,我不认为你理解我的问题,因为你的回答对我来说没有意义。
  • _.random(9 - index) 索引等于 0 有相同的机会选择 1 或任何其他数字。但整体1出现的机会较少,因为它只能出现在第一个单元格中。因此,您应该将_.random(9 - index) 替换为根据数组的最大大小赋予1 更多权重的函数。您可以通过在Math 上询问来找到每个数字的确切权重。

标签: javascript recursion random-sample


【解决方案1】:

根据您的描述,符合该描述的“列表”数量有限。那么,假设你创建了一个包含所有这些的数组,然后简单地从数组中随机选择一些东西?这里:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>test-page</title>
 <script type="text/javascript">
//<!--

var lists, und, tmp, dv, pkd, i, q,r,s,t,u,v,w,x,y,z;

function begin()
{ lists=[];
  for(i=0,q=0; q<10; q++)
    for(r=q+1;r<10;r++)
    { lists[i++]=[q,r];
      for(s=r+1;s<10;s++)
      { lists[i++]=[q,r,s];
        for(t=s+1;t<10;t++)
        { lists[i++]=[q,r,s,t];
          for(u=t+1;u<10;u++)
          { lists[i++]=[q,r,s,t,u];
            for(v=u+1;v<10;v++)
            { lists[i++]=[q,r,s,t,u,v];
              for(w=v+1;w<10;w++)
              { lists[i++]=[q,r,s,t,u,v,w];
                for(x=w+1;x<10;x++)
                { lists[i++]=[q,r,s,t,u,v,w,x];
                  for(y=x+1;y<10;y++)
                  { lists[i++]=[q,r,s,t,u,v,w,x,y];
                    for(z=y+1;z<10;z++)
                      lists[i++]=[q,r,s,t,u,v,w,x,y,z];
    } } } } } } } }
  for(j=0; j<i; j++)
  { tmp=document.createElement("span");
    tmp.innerHTML=lists[j]+"<br />";
    document.body.appendChild(tmp);  
  }  
  tmp=document.createElement("span");
  tmp.innerHTML="<br />Total: " + i + " lists. <br />";  //1013
  document.body.appendChild(tmp);
  pkd=[];
  i=0;  
  dv=document.getElementById("pks");
  return;
}

function Pick()
{ if(lists==und)
    return;
  q=Math.floor(Math.random()*1013);
  for(r=0; r<i; r++)
    if(pkd[r]==q)  //check this array for previously-picked list
      break;
  if(r==i)         //not previously picked?
  { pkd[i++]=q;    //add to array
    tmp=document.createElement("span");
    tmp.innerHTML=lists[q]+"<br />";
    dv.appendChild(tmp);  //display this list
  }
  else
    Pick();  //try again to pick an unpicked list
    //DON'T click the button more than 1013 times!
  return;
}

 // -->
 </script>
</head>
<body>
<input type="button" value="create lists" onclick="begin();" />&nbsp; &nbsp;
<input type="button" value="random pick" onclick="Pick();" /><br />
<br />
<div id="pks">
</div>
<br />
</body>
</html> 

【讨论】:

  • 聪明,当然它会产生完美均匀的采样。唯一的问题是它往往会产生太多的长列表。在我的描述中,我提到每个新数字在重复出现时都有一个(可调整的)百分比机会添加到列表中。因此,如果概率为 50%,那么您将获得 2 数字列表的数量是 1 数字列表的一半,以及 3 数字列表的四分之一等等。
  • 但实际上我实现了一个与您提出的类似的解决方案。也就是说,我可以在递归过程开始时生成一个排序列表,然后在每次递归时从列表中弹出元素。因为我确实需要递归,但这些数字实际上不必在递归中间生成。但是当我问这个问题时,我没有意识到这一点。
猜你喜欢
  • 2012-09-18
  • 2017-07-12
  • 1970-01-01
  • 2021-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-18
  • 2014-07-26
相关资源
最近更新 更多