如果Scala中有x(p) = x_ll + (x_ur - x_ll) * (p - p_ll) / (p_ur - p_ll)
y(q) = y_ll + (y_ur - y_ll) * (q - q_ll) / (q_ur - q_ll)
,我希望获得至少List[Int]
次出现的所有Set[Int]
的{{1}}。我可以使用Int
或thresh
,然后使用groupBy
来执行此操作。例如:
foldLeft
将提供filter
。
现在假设val thresh = 3
val myList = List(1,2,3,2,1,4,3,2,1)
myList.foldLeft(Map[Int,Int]()){case(m, i) => m + (i -> (m.getOrElse(i, 0) + 1))}.filter(_._2 >= thresh).keys
非常大。有多大,很难说,但无论如何这似乎很浪费,因为我不关心每个Set(1,2)
频率,我只关心它们是否至少List[Int]
。一旦通过Int
,就不再需要检查,只需将thresh
添加到thresh
。
问题是:我是否可以针对非常大的Int
,
a)如果我需要真实,准确的结果(没有错误的余地)
b)如果结果可以是近似值,例如通过使用一些Hashing技巧或Bloom过滤器,其中Set[Int]
可能包含一些误报,或List[Int]
>的频率Set[Int]
}不是Int
,而是thresh
中的Boolean
。
答案 0 :(得分:3)
首先,你不能比O(N)做得更好,因为你需要至少检查一次初始数组的每个元素。您当前的方法是O(N),假设IntMap
的操作实际上是恒定的。
现在您可以尝试以提高效率:
Array
而不是IntMap
(索引作为键)。另一个可能的选项是具有足够初始容量的可变HashMap
。正如我的基准测试显示它实际上有显着差异我不知道任何近似解决方案如何更快(只有你随机忽略一些元素)。否则它仍然是O(N)。
<强>更新强>
我创建了microbenchmark来测量不同实现的实际性能。对于足够大的输入和输出,Ixx关于立即向结果列表添加元素的建议不会产生显着的改进。但是,可以使用类似的方法来消除不必要的Map更新(这似乎是最昂贵的操作)。
基准测试结果(平均运行时间为1000000 elems,预热):
Authors solution:
447 ms
Ixx solution:
412 ms
Ixx solution2 (eliminated excessive map writes):
150 ms
My solution:
57 ms
我的解决方案涉及使用可变HashMap
而不是不可变IntMap
,并包括所有其他可能的优化。
Ixx的更新解决方案:
val tuple = (Map[Int, Int](), List[Int]())
val res = myList.foldLeft(tuple) {
case ((m, s), i) =>
val count = m.getOrElse(i, 0) + 1
(if (count <= 3) m + (i -> count) else m, if (count == thresh) i :: s else s)
}
我的解决方案:
val map = new mutable.HashMap[Int, Int]()
val res = new ListBuffer[Int]
myList.foreach {
i =>
val c = map.getOrElse(i, 0) + 1
if (c == thresh) {
res += i
}
if (c <= thresh) {
map(i) = c
}
}
完整的微基准源可用here。
答案 1 :(得分:1)
您可以使用foldleft
收集匹配的项目,如下所示:
val tuple = (Map[Int,Int](), List[Int]())
myList.foldLeft(tuple) {
case((m, s), i) => {
val count = (m.getOrElse(i, 0) + 1)
(m + (i -> count), if (count == thresh) i :: s else s)
}
}
我可以通过一个小清单来衡量性能提升约40%,所以它肯定是一种改进...
编辑使用List
和前置,这需要不变的时间(见注释)。
答案 2 :(得分:1)
如果通过“更有效”来表示空间效率(在列表无限的极端情况下),则会有一个名为Count Min Sketch的概率数据结构来估计其中项目的频率。然后您可以丢弃频率低于阈值的那些。
来自Algebird库的Scala实现。
答案 3 :(得分:0)
您可以使用foldLeft
逐步修改mutable.Set
示例,同时使用Seq
作为过滤器,使用withFilter
迭代withFilter
。但是,因为我使用foldLeft
我无法使用foreach
并且必须使用import scala.collection.mutable
def getItems[A](in: Seq[A], threshold: Int): Set[A] = {
val counts: mutable.Map[A, Int] = mutable.Map.empty
val result: mutable.Set[A] = mutable.Set.empty
in.withFilter(!result(_)).foreach { x =>
counts.update(x, counts.getOrElse(x, 0) + 1)
if (counts(x) >= threshold) {
result += x
}
}
result.toSet
}
和可变地图:
Seq
因此,这会丢弃第一次在withFilter
运行时已添加到结果集中的项目,因为Seq
会过滤附加函数中的map, flatMap, foreach
({ {1}})而不是返回已过滤的Seq
。
修改强>
我将我的解决方案更改为不使用Seq.count
,这是愚蠢的,正如 Aivean 正确指出的那样。
使用 Aiveans microbench我可以看到它仍然比他的方法稍慢,但仍然优于作者的第一种方法。
Authors solution
377
Ixx solution:
399
Ixx solution2 (eliminated excessive map writes):
110
Sascha Kolbergs solution:
72
Aivean solution:
54