Clojure 在列表中查找距离另一点最近的点
导言 假设您想确定列表中最接近另一个给定点的点。函数应该返回点本身和距离 例如,使用此数据:Clojure 在列表中查找距离另一点最近的点,clojure,clojurescript,Clojure,Clojurescript,导言 假设您想确定列表中最接近另一个给定点的点。函数应该返回点本身和距离 例如,使用此数据: (def pts [[2 4] [1 9] [9 4] [2 8]]) (def p [7 6]) 首先,需要一些助手函数: (def abs js/Math.abs) (def pow js/Math.pow) (def sqrt js/Math.sqrt) (def pow2 #(pow % 2)) (defn distance [p1 p2] (sqrt (+ (pow2 (abs (-
(def pts [[2 4] [1 9] [9 4] [2 8]])
(def p [7 6])
首先,需要一些助手函数:
(def abs js/Math.abs)
(def pow js/Math.pow)
(def sqrt js/Math.sqrt)
(def pow2 #(pow % 2))
(defn distance [p1 p2]
(sqrt (+ (pow2 (abs (- (p1 0) (p2 0))))
(pow2 (abs (- (p1 1) (p2 1)))))))
两项提案 我的第一个方法是:
(defn find-closest [p pts]
(->> (map #(vector (distance p %) %) pts)
(reduce (fn [m v]
(if (< (v 0) (m 0))
v
m)))))
(find-closest p pts)
=> [2.8284271247461903 [9 4]] ;; this is a correct result
请注意:有人有没有提示,为什么Clojure中的相同函数要慢得多
user> (time (dotimes [_ 100000] (find-closest p pts)))
"Elapsed time: 6886.850965 msecs"
user> (time (dotimes [_ 100000] (find-closest2 p pts)))
"Elapsed time: 6574.486679 msecs"
这会慢10倍多,我很难相信
问题
无论如何,因为我需要ClojureScript项目的函数,所以我的问题是:您将如何处理这个问题
find-closest2
对我来说看起来不错,但是更快的版本find-closest2
看起来有点混乱。有没有更好的办法 与所有基于微观基准做出决策的情况一样,值得使用基准库,例如,确保看到统计上显著的结果
在这种情况下,区别在于计算立即丢弃的中间惰性序列<代码>映射生成所有可能答案的序列,并为每个答案分配内存。因为您对使用中介结果不感兴趣,所以这段时间是浪费的,并且您的仅reduce版本更快
直到最近,Clojure程序有时不得不在使用map reduce filter等实现简单和可组合与不产生中间结果的快速之间做出选择。这是用修复的,所以现在您可以在不引入中间结果的情况下使用map版本,并且可以以非常通用和适应性强的方式进行
user> (import '[java.lang.Math])
nil
user> (def pow2 #(Math/pow % 2))
(defn distance [p1 p2]
(Math/sqrt (+ (pow2 (Math/abs (- (p1 0) (p2 0))))
(pow2 (Math/abs (- (p1 1) (p2 1)))))))
#'user/pow2
#'user/distance
user> (defn closer-point
([] [Long/MAX_VALUE [Long/MAX_VALUE Long/MAX_VALUE]])
([p1] p1)
([[distance1 point1 :as p1]
[distance2 point2 :as p2]]
(if (< distance1 distance2)
p1
p2)))
#'user/closer-point
user> (transduce (map #(vector (distance p %) %))
closer-point
pts)
[2.8284271247461903 [9 4]]
user>(导入“[java.lang.Math]”)
无
用户>(def pow2#(数学/pow%2))
(定义距离[p1 p2]
(数学/sqrt(+pow2(数学/abs(-(p1 0)(p2 0)))
(pow2(数学/abs(-(第11页)(第21页()()))))
#'用户/pow2
#'用户/距离
用户>(定义接近点)
([]长/最大值[长/最大值长/最大值])
([p1]p1)
([[distance1 point1:as p1]
[距离2点2:作为p2]]
(如果((转换(映射#)(向量(距离p%)%)
更近的点
(临时秘书处)
[2.8284271247461903 [9 4]]
最小键功能就是针对这个问题设计的。这是JVM版本。请注意,我们只需最小化平方距离,而不用麻烦使用Math/sqrt
计算实际距离:
(ns clj.core
(:use tupelo.core)
(:require [clojure.core :as clj]
[schema.core :as s]
[tupelo.types :as tt]
[tupelo.schema :as ts]
[criterium.core :as crit]
))
; Prismatic Schema type definitions
(s/set-fn-validation! true) ; #todo add to Schema docs
(def pts [[2 4] [1 9] [9 4] [2 8]])
(def p [7 6])
(defn square [x] (* x x))
(defn dist2 [p1 p2]
(+ (square (- (p1 0) (p2 0)))
(square (- (p1 1) (p2 1)))))
(doseq [curr-p pts]
(println "curr-p: " curr-p " -> " (dist2 p curr-p)))
(newline)
(spyx (apply min-key #(dist2 p %) pts))
(newline)
(crit/quick-bench (apply min-key #(dist2 p %) pts))
(defn -main [] )
我不会太担心代码的过早优化,只要先让它简单易懂就行了。使用内置函数几乎总是一个很好的开始(当你不需要平方根的时候,只需要最小化一个量的平方就是一个很好的开始)。请注意,我还取消了(abs…
调用,因为(square…
会自动执行该操作
以下是运行的结果:
curr-p: [2 4] -> 29
curr-p: [1 9] -> 45
curr-p: [9 4] -> 8
curr-p: [2 8] -> 29
(apply min-key (fn* [p1__8701#] (dist2 p p1__8701#)) pts) => [9 4]
WARNING: Final GC required 7.5524163302816705 % of runtime
Evaluation count : 1132842 in 6 samples of 188807 calls.
Execution time mean : 527.711887 ns
Execution time std-deviation : 3.437558 ns
Execution time lower quantile : 524.840276 ns ( 2.5%)
Execution time upper quantile : 531.911280 ns (97.5%)
Overhead used : 1.534138 ns
(ns clj.core
(:use tupelo.core)
(:require [clojure.core :as clj]
[schema.core :as s]
[tupelo.types :as tt]
[tupelo.schema :as ts]
[criterium.core :as crit]
))
; Prismatic Schema type definitions
(s/set-fn-validation! true) ; #todo add to Schema docs
(def pts [[2 4] [1 9] [9 4] [2 8]])
(def p [7 6])
(defn square [x] (* x x))
(defn dist2 [p1 p2]
(+ (square (- (p1 0) (p2 0)))
(square (- (p1 1) (p2 1)))))
(doseq [curr-p pts]
(println "curr-p: " curr-p " -> " (dist2 p curr-p)))
(newline)
(spyx (apply min-key #(dist2 p %) pts))
(newline)
(crit/quick-bench (apply min-key #(dist2 p %) pts))
(defn -main [] )
curr-p: [2 4] -> 29
curr-p: [1 9] -> 45
curr-p: [9 4] -> 8
curr-p: [2 8] -> 29
(apply min-key (fn* [p1__8701#] (dist2 p p1__8701#)) pts) => [9 4]
WARNING: Final GC required 7.5524163302816705 % of runtime
Evaluation count : 1132842 in 6 samples of 188807 calls.
Execution time mean : 527.711887 ns
Execution time std-deviation : 3.437558 ns
Execution time lower quantile : 524.840276 ns ( 2.5%)
Execution time upper quantile : 531.911280 ns (97.5%)
Overhead used : 1.534138 ns