在常量时间内保持随机项目没有重复

时间:2017-04-18 10:28:18

标签: sql postgresql rdbms

我有array我在其中取出random

[a, b, c, d ,...]

function getRandomItem(){
   // return random item from array
}

我也有一个像这样的SQL表:

category_id
random_item

然后我想将该项添加到表中。对于每个category_id,我想要多行随机项,例如:

  • 每个类别中没有重复项目(项目a无法使用category_id 1计算两次,但项目a可以位于category_id 1和category_id 2)
  • 项目数量将小于数组的长度。 (这并不是要求总是如此)。

以下是一些虚构的代码:

function persist(){
    var a = giveRandomItem();
    // $1 = a
    return execute("INSERT INTO mytable (random_item) values ($1) ON CONFLICT DO NOTHING RETURNING *", a)
}

 // usage
 var persisted;
 while(persisted === undefined){
    persisted = persist();
 }

问题在于它不是恒定的时间。我有可能连续5次点击DB,因为该项已经被持久化了。

对于每个类别,我预计最多5k项目,我的数组长度为400 000.所以概率很低。

我想找到一种恒定时间的方式,或者至少有一个sql命令可以尝试多个值,以便进一步降低概率。

用例

我能想到的一个简单用例是(它没用但很简单):

为用户提供了一个可以选择类别的界面。然后他们可以按下一个按钮,为其添加一个随机项目。 有多个用户,每个用户单独行动。因此,用户1可以将随机项添加到类别1,而用户2同时将随机项添加到类别2

修改

我最终做了这样的事情:

在应用程序级别:

shuffle(array);

function getRandomItem(seed, inc){
   let index = (seed + inc) % array.length;
   return array[index]
}

// usage:

let seed = item.category_id
let inc = category.item_count

这样我没有重复,因为我说项目的数量低于数组的长度。此外,这些项似乎是随机的,因为该类别的id用作增量开始的种子。然而,这只是起点,因此它并不是随机的,但适用于我的用例。

1 个答案:

答案 0 :(得分:2)

为了保证您不会遇到冲突(违反唯一约束),您应该改变您的方法。您应该一次生成所有5K项目(然后批量插入),而不是一次生成一个随机项目。批量插入也会大大加快速度。

如何从400K项目的数组中生成5K随机项?

一种方法是shuffle the array并采用前5K元素。然后是接下来的5K元素,依此类推。这也可以保证单独的批次不具有重复元素(直到所有400K都耗尽并且再次从数组的开头开始)。

如果您希望元素有可能在批次之间重复,则在批次之间重新排列数组。

在评论中讨论后,您似乎需要一个生成Cyclic permutations的算法。对于DB中的每个类别存储,该算法的起始种子/内部状态知道如何以这样的方式继续挑选400K数组的元素,使它们看起来是随机的,但不要重复直到该类别的所有400K元素都是拾取。