在十亿个序列的列表中查找前N个最频繁的数字序列

时间:2020-01-30 03:42:01

标签: algorithm

假设我有以下列表列表:

x = [[1, 2, 3, 4, 5, 6, 7],  # sequence 1
     [6, 5, 10, 11],  # sequence 2
     [9, 8, 2, 3, 4, 5],  # sequence 3
     [12, 12, 6, 5],  # sequence 4
     [5, 8, 3, 4, 2],  # sequence 5
     [1, 5],  # sequence 6
     [2, 8, 8, 3, 5, 9, 1, 4, 12, 5, 6],  # sequence 7
     [7, 1, 7, 3, 4, 1, 2],  # sequence 8
     [9, 4, 12, 12, 6, 5, 1],  # sequence 9
]

本质上,对于列表中任何位置包含目标编号5(即target=5)的任何列表,长度为{{1}的最常见N=2个子序列是什么}}?

因此,条件是:

  1. 如果列表中不存在M=4,则我们将完全忽略该列表
  2. 如果列表长度小于target,则我们将完全忽略列表
  3. 如果列表的长度恰好为M,但M不在target位置,则我们将其忽略(但如果Mth位于{{ 1}}位置)
  4. 如果列表长度target长于Mth,并且L位于M位置target i = M + 1 {{1 }} i = M + 2 i=M i = L (or M position, or目标在子序列中处于最终位置

因此,使用列表列表示例,我们将计算以下子序列:

position, ...,

当然,我们想要的是频率最高的position) then we count the subsequence of length子序列。因此,按计数,wheresubseqs = [[2, 3, 4, 5], # taken from sequence 1 [2, 3, 4, 5], # taken from sequence 3 [12, 12, 6, 5], # taken from sequence 4 [8, 8, 3, 5], # taken from sequence 7 [1, 4, 12, 5], # taken from sequence 7 [12, 12, 6, 5], # taken from sequence 9 ] 是最常见的两个序列。如果N=2,则将返回所有子序列([2, 3, 4, 5]),因为并列第三个。

重要

这是超级简化的,实际上是我的实际序列表

  1. 由数十亿个正整数(1到10,000之间)组成的列表
  2. 每个列表可以短至1个元素或最长500个元素
  3. [12, 12, 6, 5]N=3可以小到1或大到100

我的问题是:

  1. 假设subseqsN始终小于100,是否有一种有效的数据结构可以进行快速查询?
  2. 是否存在用于对MN的各种组合执行这种分析的算法?我查看了后缀树,但必须滚动自己的自定义版本,才能接近我需要的版本。
  3. 对于同一数据集,我需要重复查询数据集以获取MNM(其中target,{{ 1}}和`M <= 100)。如何有效地做到这一点?

1 个答案:

答案 0 :(得分:1)

扩展我的评论。这是一个草图,您可以使用现成的后缀数组来解决此问题:

1)反转并用停止符号将列表连接起来(我在这里使用0)。

[7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 5, 6, 0, 5, 4, 3, 2, 8, 9, 0, 5, 6, 12, 12, 0, 2, 4, 3, 8, 5, 0, 5, 1, 0, 6, 5, 12, 4, 1, 9, 5, 3, 8, 8, 2, 0, 2, 1, 4, 3, 7, 1, 7, 0, 1, 5, 6, 12, 12, 4, 9]

2)构建一个suffix array

[53, 45, 24, 30, 12, 19, 33, 7, 32, 6, 47, 54, 51, 38, 44, 5, 46, 25, 16, 4, 15, 49, 27, 41, 37, 3, 14, 48, 26, 59, 29, 31, 40, 2, 13, 10, 20, 55, 35, 11, 1, 34, 21, 56, 52, 50, 0, 43, 28, 42, 17, 18, 39, 60, 9, 8, 23, 36, 58, 22, 57]

3)构建LCP array。 LCP数组将告诉您一个后缀与其后缀数组中的邻居共有多少个数字。但是,遇到停止符号时,您需要停止计数

[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 2, 1, 1, 0, 2, 1, 1, 2, 0, 1, 3, 2, 2, 1, 0, 1, 1, 1, 4, 1, 2, 4, 1, 0, 1, 2, 1, 3, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 2, 1, 2, 0]

4)查询进入时(target = 5,M = 4),您在后缀数组中搜索目标的第一个匹配项,并扫描相应的LCP数组,直到后缀的起始数目发生变化。下面是LCP数组的一部分,它与所有以5开头的后缀相对应。

[..., 1, 1, 1, 4, 1, 2, 4, 1, 0, ...]

这告诉您有两个长度为4的序列出现两次。使用索引刷过一些细节,您可以找到序列并将其还原以得到最终结果。

复杂度

  • 建立后缀数组的是O(n),其中n是所有列表和O(n)空间中元素的总数
  • 建立LCP阵列在时间和空间上也是O(n)
  • 在后缀中搜索目标数平均为O(log n)
  • 扫描相关子序列的成本与目标发生的次数成线性关系。根据您给定的参数,平均应为1/10000。

前两个步骤是离线进行的。从技术上讲,查询是O(n)(由于步骤4),但是常数很小(0.0001)。