【问题标题】:What elegant solution exists for this pattern? Multi-Level Searching这种模式有什么优雅的解决方案?多级搜索
【发布时间】:2011-01-29 14:45:21
【问题描述】:

假设我们有多个整数数组。您可以将每个数组视为一个级别。我们尝试从每个数组中找到一个元素序列,恰好是一个元素,然后使用相同的谓词继续下一个数组。例如,我们将v1, v2, v3 作为数组:

v1  | v2  | v3
-----------------
1   | 4   | 16
2   | 5   | 81
3   | 16  | 100
4   | 64  | 121

我可以说谓词是:next_element == previous_element^2
上述示例中的有效序列是:2 -> 4 -> 16
实际上,在这个例子中没有另一个有效的序列。 我可以写三个循环来暴力破解上面提到的例子,但是如果数组的数量是可变的,但是当然知道顺序,你将如何解决这个问题?

非常感谢提示或对设计模式的引用。我将在 C++ 中完成,但我只需要这个想法。

谢谢,

【问题讨论】:

  • 你需要的是算法,而不是模式。这将是一个问题的解决方案。
  • 这里有一个问题:您的谓词似乎可以任意复杂,因此可以同时处理 1 到 N 个数组......很难想到一个可以处理整个事情的解决方案.
  • 另外,是否有重复的可能性,如果有,您如何处理它们(您想要每个重复的 1 个解决方案,或者只喜欢一个解决方案?)

标签: c++ algorithm search


【解决方案1】:

如果您事先订购阵列,则可以更快地完成搜索。您可以从较小的数组开始,然后在每个数组上对预期数字进行二进制搜索。这将是 O(nklogM),n 是最小数组的大小,k 是数组的数量,M 是更大数组的大小

如果您使用 Hashmaps 而不是数组,这可以更快地完成。这会让你在 O(n*k) 中搜索。

如果不能使用反向函数(在较早的数组中搜索),那么您应该从第一个数组开始,并且 n = 第一个数组的大小。

为简单起见,我将从第一个数组开始

//note the 1-based arrays
for (i : 1 until allArrays[1].size()) {
  baseNumber = allArrays[1][i];
  for (j: 2 until allArrays.size()) {
    expectedNumber = function(baseNumber);
    if (!find(expectedNumber, allArrays[j]))
        break;
    baseNumber = expectedNumber;
  }
}

您可能可以进行一些空值检查并在其中添加一些布尔值以了解该序列是否存在

【讨论】:

  • 我只是在我的问题中给出了一个谓词的例子。对于我正在使用的谓词,元素实际上并没有排序,我认为它们不能为那个谓词排序。我试图想出的是,如果数组的数量是可变的,则可以找到元素序列。
  • @AraK 我的回答现在有帮助吗?
  • 非常感谢,我想这就是我要找的:)
【解决方案2】:

(设计模式适用于类和 API 设计以提高代码质量,但不适用于解决计算问题。)

视情况而定:

  1. 如果数组以随机顺序出现,并且您的空间要求有限,那么暴力破解是唯一的解决方案。 O(Nk) 时间 (k = 3),O(1) 空间。
  2. 如果谓词不可逆(例如SHA1(next_elem) xor SHA1(prev_elem) == 0x1234),那么暴力破解也是唯一的解决方案。
  3. 如果您可以使用空间,则为 v2 和 v3 创建哈希集,这样您就可以快速找到满足谓词的下一个元素。 O(N + bk) 时间,O(kN) 空间。 (b = 满足给定 prev_elem 的谓词的 next_elem 的最大数量)
  4. 如果数组是有序且有界的,您也可以使用二分查找代替哈希表来避免使用空间。 O(N (log N)k-1 + bk) 时间,O(1) 空间。

(由于递归,所有空间计数均未考虑堆栈使用情况。)

消耗最多 O(Nbk) 空间的一般方法是通过连续过滤来构建解决方案,例如

solutions = [[1], [2], ... [N]]

filterSolution (Predicate, curSols, nextElems) {
   nextSols = []
   for each curSol in curSols:
      find elem in nextElems that satisfy the Predicate
      append elem into a copy of curSol, then push into nextSols
   return nextSols
}

for each levels:
  solutions = filterSolution(Predicate, solutions, all elems in this level)
return solutions

【讨论】:

  • 我喜欢流水线的这种想法!
【解决方案3】:

如果谓词保留数组中的顺序(例如,在您的示例中,如果值都保证为非负数),您可以采用合并算法。将每个数组的键视为最终值(根据所有数组的需要多次应用谓词后得到的结果)。

如果谓词不保留顺序(或数组未按顺序开始),您可以先按结束值排序,但这样做的需要表明另一种方法可能更好(例如散列其他地方建议的表格)。

基本上,检查所有数组的下一个结束值是否相等。如果不是,请越过最低点(仅在一个数组中)并重复。如果您使所有三个都相等,这是一个(可能的)解决方案 - 在搜索下一个之前先遍历所有三个。

“可能”的解决方案,因为您可能需要进行检查 - 如果谓词函数可以将多个输入值映射到相同的输出值,您可能会遇到在某些数组中找到值的情况(但不是第一个或最后)是错误的。

编辑 - 当谓词没有将每个输入映射到唯一的输出时,可能会出现更大的问题 - 目前无法思考。基本上,合并方法可以很好地工作,但仅适用于某些类型的谓词函数。

【讨论】:

    【解决方案4】:

    我会将所有向量保留为heaps,这样在搜索元素时我可以拥有O(log n) 的复杂性。因此,对于总共 k 向量,您将得到类似 O(k * log n) 的复杂性

    【讨论】:

      【解决方案5】:

      您可以生成一个单独的索引,将索引从一个数组映射到另一个数组的索引。从索引中您可以快速查看是否存在解决方案。

      生成索引需要一种蛮力方法,但您只能这样做。如果您想改进数组搜索,请考虑使用更合适的数据结构来实现快速搜索(例如红黑树而不是数组)。

      【讨论】:

      • 之前的一位发帖人建议使用二分搜索排序数组 - 这比红黑树快得多。如果您需要在数组中间插入许多项目,我会考虑使用列表而不是数组。这确实是内存使用和速度之间的权衡。
      猜你喜欢
      • 2016-06-22
      • 1970-01-01
      • 2011-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-20
      • 2016-04-01
      • 1970-01-01
      相关资源
      最近更新 更多