从大量条目中随机详尽(非重复)选择

时间:2012-07-13 09:29:05

标签: algorithm data-structures random indexing

假设我有一个存储在关系数据库中的大型(300-500k)文本文档集合。每个文档可以属于一个或多个(最多六个)类别。我需要用户能够随机选择特定类别的文档,以便永远不会重复单个实体,就像StumbleUpon的工作方式一样。

我真的没有看到使用带有大量用户和文档的慢速NOT IN查询来实现这一点的方法,所以我想我可能需要为此目的实现一些自定义数据结构。也许已经有一篇论文描述了一些可能适合我需求的算法?

目前我正在考虑以下方法:

  • 阅读数据库中的所有条目
  • 根据属于此类别的文档的ID为每个类别创建基于链接列表的索引。随机播放
  • 创建包含特定用户查看的所有条目的Bloom过滤器
  • 使用迭代器遍历索引,使用Bloom Filter随机选择项目以选择未查看的项目。

7 个答案:

答案 0 :(得分:2)

这取决于用户如何获得随机条目。

选项1:

用户正在分页某些实体并在其中几个实体后停止。例如,用户看到当前的随机实体,然后移动到下一个实体,读取它并继续几次,就是这样。 在下一次该用户(或另一个)从此类别中获取实体时,已查看的实体是明确的,您可以返回已查看的实体。

在该选项中,我建议保存已经查看过的实体id的(散列)集合,每次用户请求随机实体时,从数据库中选择它并检查是否已经存在于集合中。

因为这个集很小而且你的数据太大了,你获得已经查看过的id的可能性很小,大部分时间都需要O(1)。

选项2:

用户正在实体中进行分页,并且每次用户访问您的页面时,所查看的实体都在所有用户之间保存。 在这种情况下,您可能会使用每个类别中的所有实体并保存所有已查看的实体+检查实体是否被查看需要一些时间。

在该选项中,我将获得此主题的所有ID - 将它们随机播放并将其存储在链接列表中。当你想获得一个随机未被查看的实体时 - 只需获取列表的头部并将其删除(O(1))。

答案 1 :(得分:2)

如果您通过表格跟踪用户看到的条目...请尝试此操作。我将使用mysql,因为这是我能想到的最快的例子,但要点应该是明确的。

在'使用'的链接上......

insert into viewed (userid, url_id) values ("jj", 123)

寻找链接......

select p.url_id
from pages p left join viewed v on v.url_id = p.url_id
where v.url_id is null
order by rand()
limit 1

这会导致数据库继续执行1对1连接,并限制查询只返回用户尚未看到的一个条目。

只是一个建议。

编辑:可以进行一次操作,但不能保证网址会成功传递给用户。

答案 2 :(得分:1)

我认为对于任何给定的< user,category>对,相对于该类别中可用文档的总数,查看的文档数量非常少。

所以你可以存储索引三元组< user,category,document>指出哪些文档已被查看,然后对随机选择的文档采取乐观的方法?在绝大多数情况下,用户将读取随机选择的文档。并且您可以快速检查,因为三元组已编入索引。

答案 3 :(得分:1)

我会选择伪随机方法:

1。)确定要查看的类别中的元素数量(SELECT COUNT(*)WHERE ...)
2.)选择范围1 ...计数的随机数 3.)选择单个文档(SELECT * FROM ... WHERE [与计数时相同] ORDER BY [生成稳定顺序]。根据使用的SQL方言,有不同的子句可用于仅检索部分您想要的结果集(MySQL LIMIT子句,SQLServer TOP子句等)

如果文档的数量很大,那么为同一用户提供两次相同文档的机会几乎是微不足道的。使用上述方案,您根本不必存储任何状态信息。

答案 4 :(得分:1)

您可能需要考虑像Apache Cassandra这样的nosql解决方案。这些似乎非常适合您的需求。有许多方法可以在一个环境中设计所需的算法,在这种环境中,您可以轻松地将新列添加到表(列族)中,并且可以非常支持非常稀疏的表。

编辑:以下众多可能的解决方案之一:

  1. 为每个类别创建一个CF(列族,即表格)(在运行中创建这些非常简单)。
  2. 为属于该类别的每个文档的每个类别CF添加一行。
  3. 每当用户点击文档时,您都会添加一个带有named的列,并将该行设置为true。显然这个表格数量巨大,数百万列,可能人口稀少,但没问题,读取这个仍然是不变的时间。
  4. 现在为类别中的用户查找新文档只需从select * where == null中选择任何结果。
  5. 如果你能接受Cassandra的“最终一致”模型(即,用户永远不会获得重复文档并不是关键任务),你应该获得恒定的写入和读取时间,惊人的可扩展性等等。

答案 5 :(得分:1)

我过去通过使用Apache Lucene将关系数据库索引到面向文档的表单来解决类似的问题。这是在NoSQL服务器最近兴起之前,基本上是相同的,但它仍然是一种有效的替代方法。

您可以使用textId(关系数据库ID)字段和多值categoryId和userId字段为每个文本创建Lucene Document。适当填充categoryId字段。当用户阅读文本时,将其ID添加到userId字段。一个简单的查询将返回具有给定categoryId且没有给定userId的文档集 - 随机选择一个并显示它。

答案 6 :(得分:0)

  1. 将用户存储在Cookie或其他内容中的X选项之后。
  2. 使用用户新条件
  3. 将最后的选择返回给服务器
  4. 随机选择满足条件的文本之一,直到它不是用户的最后X选择的成员。
  5. 返回此文本选择并更新最后X选项列表。
  6. 我会尝试找到X的最佳值,但我想到的是像16的X那样的东西吗?