我需要计算特定整数对在某些过程中出现的次数(启发式检测使用局部敏感散列的相似图像 - 整数表示图像,下面的“邻居”是具有相同散列值的图像,因此计数表示有多少不同的哈希连接给定的图像对。)
计数存储为从(有序)对到计数(下面matches
)的地图。
输入(下面的nbrs-list
)是一个被认为是邻居的整数列表的列表,其中应该计算内部列表中的每个不同(有序)对(“邻居”)。因此,例如,如果nbrs-list
为[[1,2,3],[10,8,9]]
,则对为[1,2],[1,3],[2,3],[8,9],[8,10],[9,10]
。
多次调用例程collect
; matches
参数累积结果,nbrs-list
在每次调用时都是新的。邻居的最小数量(内部列表的大小)是1,最大的是~1000。 nbrs-list
中的每个整数在collect
的任何调用中只出现一次(这意味着,在每次调用时,没有一对发生多次)并且整数覆盖范围0 - 1,000,000(因此nbrs-list
的大小小于1,000,000 - 因为每个值只出现一次,有时它们出现在组中 - 但通常大于100,000 - 因为大多数图像没有邻居)。
我已经从更大的代码块中提取了例程,因此它们可能包含很少的编辑错误,抱歉。
(defn- flatten-1
[list]
(apply concat list))
(defn- pairs
([nbrs]
(let [nbrs (seq nbrs)]
(if nbrs (pairs (rest nbrs) (first nbrs) (rest nbrs)))))
([nbrs a bs]
(lazy-seq
(let [bs (seq bs)]
(if bs
(let [b (first bs)]
(cons (if (> a b) [[b a] 1] [[a b] 1]) (pairs nbrs a (rest bs))))
(pairs nbrs))))))
(defn- pairs-map
[nbrs]
(println (count nbrs))
(let [pairs-list (flatten-1 (pairs nbrs))]
(apply hash-map pairs-list)))
(defn- collect
[matches nbrs-list]
(let [to-add (map pairs-map nbrs-list)]
(merge-with + matches (apply (partial merge-with +) to-add))))
因此,上面的代码将每组邻居扩展为有序对;创建从对到1
的映射;然后使用添加值组合地图。
我希望这样跑得更快。我没有看到如何避免对的O(n ^ 2)扩展,但我想我至少可以通过避免这么多中间结构来减少不断的开销。与此同时,我希望代码相当紧凑和可读......
哦,现在我超过了“GC开销限制”。因此,减少内存使用/流失也是一个优先事项:o)
[也许这太具体了?我希望这些课程是一般的,并且似乎没有很多关于优化“真正的”clojure代码的帖子。此外,我可以简介等,但我的代码看起来很难看,我希望有一个明显的,更清晰的方法 - 特别是对扩展。]
答案 0 :(得分:3)
我想你想要每对发生的频率?
尝试功能frequencies。它使用引擎盖下的瞬态,这应该避免GC开销。
答案 1 :(得分:1)
(我希望我没有误解你的问题)
如果您只想计算列表中的对,那么[[1,2,3],[8,9,10]]是
(defn count-nbr-pairs [n]
(/ (* n (dec n))
2))
(defn count-pairs [nbrs-list]
(apply + (map #(count-nbr-pairs (count %)) nbrs-list)))
(count-pairs [[1 2 3 4] [8 9 10]])
; => 9
这当然假设您不需要删除重复的对。
答案 2 :(得分:1)
=>(require '[clojure.math.combinatorics :as c])
=>(map #(c/combinations % 2) [[1 2 3] [8 9 10]])
(((1 2) (1 3) (2 3)) ((8 9) (8 10) (9 10)))
这是一个非常小的图书馆,请查看source
性能方面,您正在查看以下数字,以了解1M
下1K唯一值的使用情况=> (time (doseq
[i (c/combinations (take 1000 (shuffle (range 1000000))) 2)]))
"Elapsed time: 270.99675 msecs"
这包括生成目标集,在我的机器上大约需要100毫秒。
答案 3 :(得分:0)
上述建议似乎有所帮助,但还不够。我终于得到了不错的表现:
将对包装成long
值。这是有效的,因为MAX_LONG > 1e12
和long
个实例具有可比性(因此与long[2]
不同,可以像哈希键一样工作)。与[n1 n2]
相比,这对降低内存使用有显着影响。
使用TLongByteHashMap原始类型哈希映射并对其进行变更。
使用不可变数据结构时,使用嵌套的doseq
循环(或嵌套的for
循环)处理对代码。
改善我的位置敏感哈希。问题的一大部分是它太弱了,所以找到太多的邻居 - 如果一百万张图片的邻居受到限制,那么你会得到一百万对,这会消耗太多的内存......
内循环现在看起来像:
(doseq [i (range 1 (count nbrs))]
(doseq [j (range i)]
(let [pair (pack size (nth nbrs i) (nth nbrs j))]
(if-let [value (.get matches pair)]
(.put matches pair (byte (inc value)))
(.put matches pair (byte 0))))))
,其中
(defn- pack
[size n1 n2]
(+ (* size n1) n2))
(defn- unpack
[size pair]
[(int (/ pair size)) (mod pair size)])