【问题标题】:Randomly sample an array with a fixed number of zeros随机采样具有固定数量零的数组
【发布时间】:2014-04-29 11:28:46
【问题描述】:

我已优化代码以随机采样包含 -1、0 和 1 的数组,概率为 1/4、1/2、1/4。好像

#define n (12)
unsigned int x,y=34353,z=57768,w=1564; //PRNG seeds
/* xorshift PRNG
 * Taken from https://en.wikipedia.org/wiki/Xorshift#Example_implementation
 * Used under CC-By-SA */
u_int32_myRand() {
    unsigned int t;
    t = x ^ (x << 11);
    x = y; y = z; z = w;
    return w = w ^ (w >> 19) ^ t ^ (t >> 8);
}

x=(int)time(NULL); //seed PRNG
unsigned int k
int F[n];
for(k=0; k<n; k++) {
      F[k]=(1-(myRand()&3))%2;  
    }

我怎样才能修改它,使它只返回恰好有 n/3 个零的数组并且仍然快速?

【问题讨论】:

  • 你能解释一下当前的算法吗?
  • @Don'tYouWorryChild myRand() 只是生成一个随机数。 1-myRand()&3 给出 -1、0、1 或 2。因此 (1-(myRand()&3))%2 根据需要给出概率 1/2 的 0 和概率 1/4 的 -1 和 1。
  • 所以对于您的答案,只需删除 %2 并将其转换为 unsigned,这将使操作 &amp;3 返回 012,因此0 现在有1/3 的概率。
  • @Don'tYouWorryChild 是的,但我需要将零的数量固定在 n/3。
  • @luk32 啊是的 {-2,-1,0,1,} 你是对的。当然 mod 2 你会得到相同的结果。我没有检查随机数生成器的质量。正如你所建议的,我认为我需要随机选择 n/3 个位置,在那里放置零,然后在其余位置放置随机选择的 -1 和 1。

标签: c performance algorithm math


【解决方案1】:

最简单的方法是用 n/3 个零填充数组的第一部分。然后根据需要添加任意数量的 1 和 -1。然后,执行Fisher-Yates shuffle 来随机化数组。

尝试“随机分布 n/3 个零”的问题在于您难以防止重叠。也就是说,如果您想在 99 个数组中放置 33 个零,则不能只选择 33 个随机索引,因为您可能会得到重复。所以你最终会在数组中得到少于 33 个零。

就性能而言,这几乎与您当前的示例一样快。它只需要对数组进行额外的传递。生成的随机数个数是一样的。

【讨论】:

    【解决方案2】:

    分两步进行:

    1. 在您的数组中随机分配 n/3 零,并将其余部分设置为 1
    2. 为其余的分配一个随机符号以获得所需的 -1/+1。

    示例代码:

    int F[n];
    // fill with 1
    for(k=0; k<n; k++) {
        F[k] = 1;
    }
    // distribute n/3 zeros
    for(k=0; k<n/3; k++) {
        // find a location which does not have a 0 yet
        int i;
        do {
            i = myRand() % n;
        } while(F[i] == 0);
        F[i] = 0;
    }
    // change remaining (non zero) to -1 with 50% probability
    for(k=0; k<n; k++) {
        if(F[k] && myRand()%2) F[k] = -1;
    }
    

    它的运行时间约为 2.4 n,但我认为你不会比这更快。

    对于 n/3 个零的情况,第二个 for 循环中的 while 循环平均执行大约 1.2 次。


    备注:

    如果成功概率足够高,则在第二个for 循环中使用的试错法效果很好。对于概率 p,您平均需要的试验次数是 1/p。

    在我们的例子中(n/3 个零),找到一个好的位置(即最后一个零)的最坏概率是 2/3,因此平均迭代 1.5 次。要找到所有 n/3 个零的位置,您平均需要大约 0.2*n 次迭代。

    平均运行时间可以计算为-log(1-a),其中a 是您想要分配的零的百分比(在您的情况下为a = 1/3)。

    更多示例:如果您想要分配 2/3*n 个零,则需要 1.1*n 次迭代。对于 0.99*n 个零,它已经是 4.6*n 次迭代。

    平均而言。在最坏的情况下,它需要永远。


    如果您需要运行时保证,您可能最好通过实施真正的采样而不重新选择,即用所有可能的索引填充容器,采样随机元素作为索引并将其从容器中删除。但这可能有大约 O(n*log(n)) 的运行时间。因此,它适用于较小的 n 或较大百分比的零。

    【讨论】:

    • 谢谢。我认为您用于分配 n/3 个零的方法是来自 eyalsch.wordpress.com/2010/04/01/random-sample 的“反复试验”。
    • 如果您的成功概率足够高,试错效果很好。我在我的问题中添加了一个评论。
    • 我发现了问题。我 = 我的兰德() % n;可以是负数!
    • 如果(F[k] && myRand()%2) F[n] = -1;看起来像一个错误。你的意思是 F[k] = -1 吗?
    • @user2179021:如果您查看为 xorshift 提供的链接,您会发现它没有正确实现。您的实现使用int,而它应该使用uint32_t(即unsigned int)。
    【解决方案3】:

    这是一个简单的算法:

    1. 将所需数量的零 (n/3) 放入输出数组中
    2. 对于剩余的地方,输入1-1,概率为1/2
    3. Shuffle the array

    但是:

    ... 数组包含 -1、0 和 1,概率为 1/4,1/2,1/4

    这是否意味着数组中恰好有n/4 1?让我们假设它没有。那么:

    • 计算数组中 1 (a) 和 -1 (b) 的数量
    • 1 的概率是a/(a+b);在上述算法中使用它而不是1/2

    注意:如果您的输入数组中只有零或根本没有零 - 不可能完全使用 n/3 零采样!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-19
      • 1970-01-01
      • 2014-11-03
      • 2015-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多