あるプロセスで特定の整数のペアが発生する回数をカウントする必要があります (局所性に敏感なハッシュを使用して類似の画像を検出するヒューリスティック - 整数は画像を表し、以下の「隣人」は同じハッシュ値を持つ画像であるため、カウントは指定された画像のペアを接続する異なるハッシュの数)。
カウントは、(順序付けられた) ペアからカウント (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
への呼び出しで 1 回だけ発生しcollect
(これは、各呼び出しでペアが 2 回以上発生しないことを意味します)、整数は 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 コードの最適化に関する投稿があまりないように思われることを願っています。また、プロファイリングなどを行うこともできますが、私のコードは非常に醜く見えます。特にペアの展開については、明白でクリーンなアプローチがあることを願っています。]