【问题标题】:Algorithm to group strings from list of lists从列表列表中对字符串进行分组的算法
【发布时间】:2017-09-05 17:03:26
【问题描述】:

我有几个对象,每个对象都包含一个字符串列表。

Object 1
["one", "two", "three", "four"]
Object 2
["one", "two", "five", "seven", "eight"]
Object 3
["one", "two", "three", "four", "five", "seven"]
Object 4
["one", "two", "nine", "ten"]

如您所见,字符串是重复的,我的任务是将这些字符串分组并在对象中引用这些组,而不是字符串本身。输出应如下所示:

group1
["one", "two"]
group2
["three", "four"]
group3
["five", "seven"]
group4
["eight"]
group5
["nine", "ten"]

对象将包含对组的引用

Object 1
["group1", "group2"]
Object 2
["group1", "group3", "group4"]
Object 3
["group1", "group2", "group3"]
Object 4
["group1", "group5"]

谢天谢地,这个分组是基于一组静态数据,每个对象由大约 20 个这样的字符串组成。

如果一个字符串出现在两个组中并不重要,但这很好,所以,除非创建这样的组非常容易,否则这不是一个目标。

我以字符串为例简化了我的问题,但在我的文档中,它们是一遍又一遍重复的 JSON 对象。将它们分组并引用这些组的目标,以便不会一次又一次地重复相同的对象。所以,可以说,目标是减少重复。

我知道这是某种 clustering problem,但我无法将这些字符串形式化为 (x,y) 平面中的点。 我该如何处理?这个问题有名字吗?

编辑:经过一番思考,我认为我应该在所有这些列表中找到不相交的字符串集并从那里开始工作。联合查找算法似乎很合适。

【问题讨论】:

  • 分组是如何完成的?您使用什么分组标准?
  • 分组没有规则。任何字符串都可以分组,但目的是避免重复一堆字符串,而是引用它们的组。比如说,如果我让这 7 个字符串在 4 个对象中重复,而不是写 28 个字符串,我会提到 4 个对象中的一组。 @user3437460
  • 我想说聚类分析不是您在这里需要的,因为它主要是一种在 n 维空间中定义类别的统计方法。您正在处理明确定义的群体(似乎),而不是回答有关距离或最佳群体定义的问题
  • @Jim Dennis 我知道这很难推测,但从 OP 试图找到一个精确有序的子序列以进行序列匹配,特别是匹配任何 [c n,..,cn+m] 的一些 [c0,..,cj] 是准确的等于集合 [f0,..,fm]。如果我们将 m 绑定到一个常数(正如 OP 在示例中所做的那样),那么将一个列表与另一个列表进行匹配将在二次时间内完成。
  • @LucasKot-Zaniewski:我同意。如果我们只想搜索有界的序列列表...例如 5、4、3、2 最长胜...那么问题就变得容易处理了。

标签: string algorithm


【解决方案1】:

我在我的一个程序中为$JOB 执行了一个非常相似的计算,只要我保持通用,就可以安全地分享。

我打算用 python3 来写这个,而且我是从一种非常不同的语言翻译的,所以可能会有一些失误,而且肯定有一些地方可以发现明显的速度提升:

def findGroups(llStrs):
  # Input should be a list of lists of strings
  all_things = frozenset.union(frozenset(), *(map(frozenset, llStrs)))
  all_singletons = frozenset.union(frozenset(), *(frozenset(x) for x in llStrs if len(x) == 1))
  working = [all_things - all_singletons]
  for lStr in llStrs:
    setLStr = frozenset(lStr)
    working2 = []
    for w in working:
      if (w & setLStr) and not (w <= setLStr):
        a = w & setLStr
        b = w - setLStr
        if len(a) > 1:
          working2.append(a)
        if len(b) > 1:
          working2.append(b)
      else:
        working2.append(w)
    working = working2
  final_working_union = frozenset.union(*working)
  return working + [frozenset({x}) for x in (all_things - final_working_union)]

现在,这可能不是您绝对想要的 - 每个组都是不相交的,对于您的使用,如果有时组重叠会更好。但是,这大致就是我在项目中对事物进行分组的方式。

【讨论】:

  • 感谢伪代码。我也使用 python,所以,我会尝试让这个脚本工作并理解它在做什么。
【解决方案2】:
p = ["one", "two", "three", "four"]

q = ["one", "two", "five", "seven", "eight"]

r = ["one", "two", "three", "four", "five", "seven"]

s = ["one", "two", "nine", "ten"]

p, q, r, s = map(set, [p, q, r, s])
u = p | q | r | s
groups = {}
elements = [p, q, r, s]
index_map = {i:set() for i, _ in enumerate(elements)}
for i in u:
    count = 0
    current = []
    for k, j in enumerate(elements):
        if {i}.issubset(j):
            count += hash(count + 1 + k)
            current += [k]
    if not count in groups:
        groups[count] = [i]
    else:
        groups[count] += [i]
    for t in current:
        index_map[t] |= {count}
print groups
print index_map

上面代码背后的想法是检查一个元素在不同集合中重复的次数,基于此,该元素在字典中具有唯一键。因此,所有集合中的每个元素都将属于相同的键或在 N 个集合中重复的元素将具有唯一键。一旦我们有了唯一的键,我们必须将原始数据与新数据映射,为此我将跟踪元素索引,这些索引在不同的集合中具有当前元素。最后我执行集合并集。

【讨论】:

    【解决方案3】:

    这是一个 Python 示例。我们定义一个组类来携带一个列表和一个别名。然后我们循环遍历我们想要压缩的对象列表,直到没有更多的替换是可能的。请注意,我对组列表进行了排序,以便较长的匹配优先于较短的匹配。

    union-find 算法在这里对您没有帮助,因为它用于在将图形(父子映射)作为输入时查看两个节点是否在同一个集合中,然后合并两个集合(如果它们是)不是。您可以找到更省时的方法来判断 两个 元素是否是同一集合的成员(例如由单个元素键控并带有指向其所在组的指针的字典,本质上是扁平化不相交集看起来像),但我不明白这将如何帮助您解决您描述的问题。您没有正确的输入,也不需要上述算法的实现输出,所以我看不出在这种情况下它对您有什么用处。

    您所描述的是匹配有序子集 [cn,..,cn+m] 一些集合 [c0,..,cj sub>] 完全等于 已定义 组 [f0,..,fm] (压缩不会如果我们最多可以对两个元素进行分组,则非常有效,这就是为什么我们将组长度概括为m)。据我所知,使用不相交集结构并没有真正的好处。

    最后,如果您的目标是通过选择频繁重复子序列的最佳替换组来最小化内存占用,我建议您查看数据压缩主题。

    class group:
        def __init__(self,stringList, name):
            self.list = stringList
            self.alias = name
    
    class object1:
        def __init__(self,stringList):
            self.list = stringList
    
    g1 = group(["one", "two"], "group1")
    g2 = group(["five", "seven"], "group2")
    g3 = group(["seven"], "group3")
    g4 = group(["three", "five", "seven"], "group4")
    o = object1(["one", "two", "three", "five", "seven", "seven", "eight", "nine"])
    group_list = [g1, g2, g3, g4]
    group_list.sort(key=lambda x: len(x.list), reverse=True)
    
    def match_elems(match_group, sub_list, index):
        for i in range(index, min(index + len(match_group.list), len(sub_list))):
            if match_group.list[i-index] != sub_list[i]:
                return False
        return True
    
    def compress(obj, groups):
        for g in group_list:
            n = 0
            while n < len(obj.list):
                if match_elems(g, obj.list, n):
                    obj.list = obj.list[:n] + [g.alias] + obj.list[n:]
                    obj.list = obj.list[:n+1] + obj.list[n+len(g.list)+1:]
                    print(obj.list)
                n=n+1
    
    compress(o, group_list)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-18
      • 2018-11-28
      • 2017-09-26
      • 2020-10-05
      • 1970-01-01
      • 1970-01-01
      • 2021-07-08
      相关资源
      最近更新 更多