【问题标题】:persist random items with no duplicates in constant time在恒定时间内保留没有重复的随机项目
【发布时间】:2017-04-19 21:21:35
【问题描述】:

我有一个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 被用作增量开始的种子。然而,这只是起点,因此它并不是真正随机的,但适用于我的用例。

【问题讨论】:

    标签: sql postgresql rdbms


    【解决方案1】:

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

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

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

    如果您希望元素有机会在批次之间重复,则在批次之间重新洗牌。


    在 cmets 中讨论后,您似乎需要一个生成 Cyclic permutations 的算法。对于数据库中的每个类别存储该算法的起始种子/内部状态,以了解如何继续选取 400K 数组的元素,使其看起来是随机的,但在选取该类别的所有 400K 元素之前不要重复。

    【讨论】:

    • 我相信这行不通。我在我的问题中添加了一个用例以更好地反映问题。您的答案不起作用,因为它假设我可以一次生成所有内容。我不能,项目是通过用户输入生成的。如果这有意义的话。
    • 如果您坚持逐个插入元素,那么您应该更改getRandomItem() 函数的实现,使其返回非重复元素。一种方法是再次打乱 400K 数组,每次调用 getRandomItem() 时,它都会返回数组中的下一项。所以,主要思想是控制随机性的产生。按随机顺序预先生成 400K 数组,然后在需要随机项时按顺序读取。在您遍历所有 400K 元素之前,它将保证没有冲突。
    • 它已经做到了。问题是类别的数量是未知的。用户 1 可以发布类别 1 中的项目。然后随机数组中的索引递增。一段时间过去了,而其他用户在其他类别上发布(并且随机数组中的索引增加)。然后过了一段时间,另一个用户将一个项目发布到类别 1。问题是在此期间其他类别中发布了 400 000 个项目,我们又回到了第 1 格。你看到问题了吗?不保证索引位于与所述类别不重复的位置。
    • @Ced,是的,如果有 400K 插入,那么元素将开始重复。如果您只有少数类别,那么您可以为每个类别预先生成一个打乱的数组。如果您知道每个类别最多有 5K 个元素,那么您可以为每个类别仅预生成 5K 个项目(不是全部 400K),将它们保存在帮助表中的某个位置并根据需要进行检索。主要思想仍然成立——不要把随机性留给机会,控制它。
    • 我实际上并没有几个类别。控制随机性是我发这篇文章的原因。
    猜你喜欢
    • 1970-01-01
    • 2016-01-22
    • 1970-01-01
    • 1970-01-01
    • 2018-07-02
    • 1970-01-01
    • 1970-01-01
    • 2018-01-27
    • 2015-04-07
    相关资源
    最近更新 更多