快速选择一组中的位

时间:2013-03-25 23:02:40

标签: optimization language-agnostic montecarlo

我想从已知的位掩码中选择一些随机位。理想情况下,我也希望以随机顺序选择这些位,但是可以将任务拆分为稍后选择和随机播放。

数据的一些其他特征:

  • 位掩码是64位长
  • 所选位数是4,8,16或32
  • 通常会设置40到60位(总是至少一半)
  • 我需要数百个随机选择单个位掩码(结果用于统计模拟)

以下是我期望的掩码和事物的例子(选择随机4位):

mask    0111111011111011111110111111111111111101111111100111101111111111
random4 ....1...........1........1...............1......................
shuffled bit positions: 41, 16, 4, 25

在这个例子中,我不应该回到位0,因为它已经被禁用了。

这是该算法的一个已知热点,所以我想尽可能多地挤出它(对随机选择的测试只需要比我当前的随机选择实现长约2倍)。我目前的想法是填充n中的第一个char positions[64]数字,并在位掩码中设置位的位置。因此,对于上面的示例,我最终得到:[1, 2, 3, 4, 5, 6, 8, 9, ...]。然后开始选择0n之间的随机数来选择随机位位置。每次选择后,将位置设置为-1,如果再次按-1,则重复随机选择。

这非常适合选择4个数字,但在选择32个数字时经常会重复选择。

另一个想法是创建一个如上所述的位置数组,然后使用Fisher-Yates对其进行随机排列并选择第一个n位置。这需要在数组中进行更多写操作,并且总是需要生成与设置位一样多的随机数,这对于仅选择4位可能是一种过度杀伤。

有没有更快的方法来生成这些数据?我的目标是模拟的准确性,所以我真的可以在一秒钟内检查多少随机迭代。

语言并不重要,但我猜C会占据主导地位。

1 个答案:

答案 0 :(得分:1)

你不需要做一个完整的Fisher-Yates洗牌。只需在获得第一个n值后停止。您甚至可以为下一个样本重用部分洗牌的数组。这是C99中的一个例子:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// Assumes that the array a contains numbers 0..63 in any order
static void print_random_bits(uint64_t bitmask, int num_bits, int a[64]) {
    for (int i = 0, j = 63; i < num_bits; ++i, --j) {
        int r = rand() % (j + 1);
        int t = a[r];
        if (r != j) {
            a[r] = a[j];
            a[j] = t;
        }
        printf("random bit %2d: %d\n", t, bitmask & (1ULL << t) ? 1 : 0);
    }
}

int main(void) {
    int a[64];

    for (int i = 0; i < 64; ++i) {
        a[i] = i;
    }

    uint64_t bitmask = 0x5555555555555555ULL;

    for (int i = 0; i < 10; ++i) {
        print_random_bits(bitmask, 8, a);
        printf("\n");
    }

    return 0;
}