【问题标题】:How do I quickly get a weighted random instance of a Django model instance based on a weight field on that model?如何根据模型上的权重字段快速获取 Django 模型实例的加权随机实例?
【发布时间】:2020-12-05 01:43:37
【问题描述】:

我使用 Postgres 作为数据库后端,但我认为这并不重要。另外,我想仍然使用sqlite3 作为本地开发数据库,​​所以理想情况下,任何方法都适用于两者。

“加权”是指该数据库表中的某些项目比其他项目更有可能出现,基于从0+inf 的启发式值,其中0 是“永远不会被挑选” , 1 是“与任何其他实例一样被选中,2 是“被选中的可能性是任何其他实例的两倍”。

我已经 read other SO posts 讨论过拉取模型的随机实例,但据我所知,没有任何方法可以通过权重快速做到这一点。

我的模特:

  • 拥有数百万个实例。
  • 有一个weightDecimalField,可以随时更新,甚至在运行时。
  • 除了使用这种随机选择算法外,在任何地方都没有被引用(例如,它可以随时删除和重新创建而不会出现问题)。

我追求的是一种比我尝试过的解决方案更快的快速方法,或者解释为什么我尝试过的解决方案之一是我能得到的最快的。

避免 XY 问题

我想从数据库表中选择“新鲜”的内容,但仍然有机会看到一些较旧的内容。如果某些内容被浏览得太频繁或不受欢迎,则应该减少它的出现频率。理想情况下,我可以控制它的频率:“啊,所以这将是网站上其他内容的 1.5 倍。”

我尝试过的东西

根据概率滚动随机选择并重试

  • 计算模型实例的总数。例如:100。
  • 选择模型的一个随机实例。
  • 1 * weight / instances_count 掷骰子以确定是否应将物品扔回并再次进行随机选择。

这似乎很慢,而且“退回”的随机性可能永远不会终止。一般来说,这真的很笨拙,我不想使用它。把它放在第一位,因为它非常“简单”,我很可能会忽略它。

选择每个元素 ID 和权重,并使用随机权重算法选择 ID

  • SELECT 所有行。
  • 根据权重为每一行分配一个 ID 范围。
  • 动态添加所有权重。
  • 掷一个sum_of_all_weights 面的骰子。
  • 无论选择什么,根据权重选择一个 ID。

问题是,这个算法对于数百万行来说似乎很慢。这是“幼稚”的解决方案。

根据权重分配一系列 ID 并动态删除/重新创建实例

  • 每当添加某些内容或更改权重时,删除所有包含该实例唯一详细信息的实例并创建weight 更多具有相同元信息的实例。
  • 正常选择随机实例。

需要注意的是,只有基于整数的权重是可能的。此外,性能问题从SELECT 转移到INSERTDELETE 操作。

重新思考整个模型并引入“回归”价值

  • 添加throw_back_probability 字段而不是weight
  • 如果概率为0.0,它将永远不会被退回。否则,根据throw_back_probability 掷骰子并在需要时投掷。
  • 将算法限制为 3 个“回退”(或任意数量)。

这最终解决了问题,但可能仍需要更多的数据库调用,是一种间接解决方案。


确实如此,所以我确信我忽略了基于 annotation 的巧妙解决方案(或类似的)。提前感谢您的帮助!

【问题讨论】:

  • 抽样必须有多准确?
  • 不需要那么准确@DavidEisenstat,但最好是粗略的“加权”。

标签: python-3.x django algorithm performance random


【解决方案1】:

您可以将分片与您列出的任何方法结合使用。选择多个分片(最好行数/分片数显着大于log(行数),以避免高概率出现空分片),为每一行分配一个统一的随机分片ID,并将分片ID作为第一个条目主键,以便表按分片排序。要采样,请选择一个统一的随机分片,然后在分片内采样。这在分片总数不平衡的情况下是不准确的,但是如果分片足够大,那么大数定律就会发挥作用。(但是,如果分片太大,那么就会开始破坏分片的意义.)

【讨论】:

  • 不错!这应该会使事情变得更快,同时仍然允许用户在每次运行算法时获得“新鲜体验”。谢谢!
猜你喜欢
  • 2011-03-26
  • 2011-05-03
  • 2014-10-21
  • 2019-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-28
相关资源
最近更新 更多