【问题标题】:Find Top N Most Frequent Sequence of Numbers in List of a Billion Sequences在十亿序列列表中查找前 N 个最频繁的数字序列
【发布时间】:2020-05-16 05:25:17
【问题描述】:

假设我有以下列表:

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)的任何列表,最常观察到的长度为M=4 的顶级N=2 子序列是什么?

所以,条件是:

  1. 如果列表中不存在target,那么我们将完全忽略该列表
  2. 如果列表长度小于M,那么我们将完全忽略该列表
  3. 如果列表的长度正好是Mtarget 不在Mth 位置,那么我们将忽略它(但如果targetMth 位置,我们就计算它)
  4. 如果列表长度LM 长并且targeti=M 位置(ori=M+1position, ori=M+2position, ...,i= Lposition) then we count the subsequence of lengthMwheretarget`在子序列的最后位置

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

subseqs = [[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][12, 12, 6, 5] 是计数最多的两个最频繁的序列。如果 N=3 则所有子序列 (subseqs) 将被返回,因为第三个存在平局。

重要

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

  1. 由数十亿个正整数列表(1 到 10,000 之间)组成
  2. 每个列表可以短至 1 个元素,也可以长达 500 个元素
  3. NM 可以小到 1 也可以大到 100

我的问题是:

  1. 假设NM 总是小于100,是否存在允许快速查询的有效数据结构?
  2. 是否有已知算法可以对NM 的各种组合执行这种分析?我查看了后缀树,但我必须推出自己的自定义版本才能接近我需要的。
  3. 对于同一个数据集,我需要反复查询该数据集的各种值或targetNM 的不同组合(其中target <= 10,000N <= 100 和`M

【问题讨论】:

  • 我认为您进入了错误的 StackExchange 站点。
  • 对于现实世界的问题来说,这是一个合理的数据科学问题
  • 我认为在反转和连接您的列表后,您可以使用自定义的suffix array。当您的目标号码出现时,您基本上为所有以您的目标号码开头的后缀构建一个LCP-Array(考虑到停止符号)。如果您期望多个查询具有相同的目标编号和不同的 M,那么您也可以预先计算 LCP-Array。
  • 如何计算两个或多个满足条件的目标?
  • @Neil 这将是“全部”。这正是序列 7 中发生的情况。但是,这也取决于N 是什么,因为我们感兴趣的是Ntarget 的最频繁子序列。和长度M

标签: algorithm


【解决方案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)。

【讨论】:

  • 我正在使用 Python。那么,您认为我必须推出自己的自定义后缀数组实现吗?此外,为什么我们需要反转和连接序列?
  • 后缀数组可以是标准的,但您可能希望采用 LCP 数组。由于您要查找长度为 M 且以目标编号结尾的所有子序列,但后缀数组只能帮助您找到以目标编号开头的子序列,因此您必须还原序列。您还可以为每个序列构建一个后缀数组,但串联会更有效。
  • 如果我有十亿个长度在 10 到 100 之间的列表,空间和时间复杂度是否会成为问题?
  • 由于我的输入已经是整数而不是字母,字典排序在这里是如何发挥作用的?现在,列表是通过获取唯一的“状态”并为其分配下一个可用整数值来生成的。为了获得更好的性能,我应该关心如何分配这些整数值吗?现在,我在数据中遇到的第一个状态自动分配给整数值1,第二个观察到的状态分配给2,依此类推。但是,这些状态可能非常罕见。或许,这些状态应该按照观察的频率来分配?
  • 您对LCP的理解是正确的。现在,当扫描相关部分(步骤 4)并寻找 LCP 条目大于 M 的连续子序列(LCP)时。这些序列的长度 + 1 告诉您关联序列 (长度为 M 的)出现在原始数据中。
猜你喜欢
  • 2020-05-13
  • 2022-06-15
  • 1970-01-01
  • 2015-10-08
  • 1970-01-01
  • 2010-09-16
  • 2021-12-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多