不同比特的随机对

Random pairs of different bits

本文关键字:随机      更新时间:2023-10-16

我有以下问题。我有一个用二进制表示的数字。我需要一种方法来随机选择其中不同的两位(即找到1和0)。除此之外,我对这个数字运行其他操作(反转序列、置换序列…)这些是我已经使用过的方法:

  • 记下所有的1和0。当我创建二进制数的二进制表示时,我存储0和1的位置。这样我就可以为一个列表选择一个索引,并从另一个列表中选择一个指数。然后我有两个不同的部分。为了运行我的其他操作,我从基本交换操作创建了这些操作,该操作在操作时更新1和0的索引。因此,我有一个第三个列表,它存储每个位的列表索引。如果一个比特是1,我知道在列表中的哪里可以找到1的所有索引(零也是如此)
  • 当执行不需要比特不同的操作时,上述方法会产生一些开销。因此,另一种方法是在需要不同比特时创建列表

有人有更好的主意吗?我需要这些操作非常快(我正在使用popcount、clz和其他二进制操作)

我觉得我没有足够的信息来正确评估权衡,但也许你会发现这个想法很有用。要在一个单词中找到随机的1(通过popcount和库采样在多个单词上找到1;通过补码找到0),首先测试popcount。如果popcount很高,则随机统一生成索引,并对其进行测试,直到找到一个索引为止。如果popcount为中等,则使用统一随机掩码进行逐位AND(但如果AND为零,则保持原始值)以减少popcount。当popcount较低时,使用clz高效地编译(小)候选列表,然后随机进行统一采样。

我认为下面的算法可能是一个非常有效的算法,可以满足您的要求。你只对数字中的每个比特迭代一次,对于每个元素,你必须生成一个随机数(不确定这有多贵,但我相信有一些优化的CPU指令可以用来获得随机数)。

想法是迭代所有的位,并以正确的概率将索引更新为您正在访问的当前索引。

从流/数组中获取元素的通用伪代码:

p = 1
e = null
for s in stream:
  with probability 1/p:
    replace e with s
  p++
return e

Java版本:

int[] getIdx(int n){
    int oneIdx  = 0;
    int zeroIdx = 0;
    int ones = 1;
    int zeros = 1;
    // this loop depends on whether you want to select all the prepended zeros
    // in a 32/64 bit representation. Alter to your liking...
    for(int i = n, j = 0; i > 0; i = i >>> 1, j++){
      if((i & 1) == 1){ // current element is 1
        if(Math.random() < 1/(float)ones){
          oneIdx = j;
        }
        ones++;
      } else{ // element is 0
        if(Math.random() < 1/(float)zeros){
          zeroIdx = j;
        }
        zeros++;
      }
    }
    return new int[]{zeroIdx,oneIdx};
  }

您可能会考虑的优化是使用int而不是float进行概率选择,这可能会稍微快一点。这是我不久前做的关于这一点的简短证明:在这里。我相信这个算法是Knuth的功劳,但记不清了。