我做了一个简单的Clojure程序,即使迭代次数等于500,它运行速度也很慢。有谁能告诉我代码有什么问题?
(ns calc.core
(:require [clojure.math.numeric-tower :as math]))
(def ^:const B 500)
(defn build-map [p h g]
(into {} (for [n (range 0 B)] [n (mod (/ h (math/expt g n)) p)])))
(defn find-result [res-map p g]
(for [n (range 0 B)
:when (= (mod (math/expt g (* B n)) p) (res-map n))]
(get res-map n)))
(defn calculate [p h g]
(find-result (build-map p h g) p g))
(def ^:const P 130N)
(def ^:const G 642N)
(def ^:const H 323N)
(defn -main []
(do
(println "Starting calculation...")
(println (calculate P H G))
(println "done")))
更新#1 我稍微更改了 find-result 功能并提高了性能:
(defn find-result [res-map p g]
(for [[k v] res-map
:when (= (mod (math/expt g (* B k)) p) v)]
v))
为什么这段代码运行得更快?我的代码还可以进行哪些其他改进,以便它运行得更快(B = 1024时运行时间仍然很慢)?
更新#2
尝试了所有的建议,但Clojure版本似乎永远都在运行。例如,用Java编写的这个版本运行得非常快:https://gist.github.com/kernelmode/e943f155edad50c01955
这是我的代码的更新版本:
(ns crypto5.core
(:require [clojure.math.numeric-tower :as math]))
(def ^:const B (math/expt 2N 10N))
(def ^:const P 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084171N)
(def ^:const G 11717829880366207009516117596335367088558084999998952205599979459063929499736583746670572176471460312928594829675428279466566527115212748467589894601965568N)
(def ^:const H 3239475104050450443565264378728065788649097520952449527834792452971981976143292558073856937958553180532878928001494706097394108577585732452307673444020333N)
(def ^:const R (/ 1 G))
(def GpowB (math/expt G B))
(def exps (take B (iterate (fn [[e eb]] [(* e G) (* eb GpowB)]) [1 1])))
(defn -main []
(println (map #(mod (/ H (first %)) P) (filter (fn [[e eb]] (= (mod (/ H e) P) (mod eb P))) exps))))
答案 0 :(得分:4)
你正在计算很多巨大的数字。如果B为500,则地图中的键为0-499。在g为642N的查找结果中,你用最大的数据计算数学/ expt 500次(math / expt 642N 249500)。仅这一个计算需要花费很长时间才能计算,而且当k = 499时就是这样。如果您指定了您尝试解决的确切问题,那将会很有帮助。看起来更像是算法问题。
<强>更新强>
我在这里创建了一个Clojure解决方案:Clojure solution gist。据我所知,它应该与Java版本相同(更好地检查它确定)。它也可以在大约30秒内运行,而且它的体积小得多,而且可读性也更高。具有讽刺意味的是,我曾经很难阅读Clojure,现在它正在阅读Java,我遇到了麻烦:)。您遇到的最大性能问题是不使用modPow。事实证明,即使在Java中做pow,然后mod比使用modPow慢很多。哦,Clojure BigInt与BigInteger不同,它并不容易。所以我使用BigInteger和Java互操作。有时您需要获得最佳性能。希望这会有所帮助。
更新#2代码
(ns calc.core)
(def start (biginteger 1))
(def b (.pow (biginteger 2) 20))
(def g (biginteger 11717829880366207009516117596335367088558084999998952205599979459063929499736583746670572176471460312928594829675428279466566527115212748467589894601965568))
(def p (biginteger 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084171))
(def h (biginteger 3239475104050450443565264378728065788649097520952449527834792452971981976143292558073856937958553180532878928001494706097394108577585732452307673444020333))
(defn build-left-table [start end p h g]
(into {}
(pmap (fn [n] [(.mod(.multiply h (.modInverse (.modPow g (biginteger n) p) p)) p) (biginteger n)])
(range start (inc end)))))
(defn find-collision [table b p g]
(let [base (.modPow g b p)]
(loop [i 0
v (biginteger 1)]
(when (< i b)
(do
(if (contains? table v)
(let [percentage (double (/ (* i 100) b))]
(println (str "Collision after " percentage "%"))
[i (table v)])
(recur (inc i) (.mod (.multiply v base) p))))))))
(time (find-collision (build-left-table start b p h g) b p g))
更新#3
如果你只是想找到这个值,这是一个更好的类似Clojure的版本。以上基本上是一个java重写:
(defn find-collision [table b p g]
(let [base (.modPow g b p)
f (iterate (fn [x] (.mod (.multiply x base) p)) (biginteger 1))]
(some (fn [x] (table x)) (take b f))))
答案 1 :(得分:1)
你的算法是O(n log n),因为expt对每个n进行log n乘法。您可以将其减少为O(n)以更实用的方式再次写入。我的食谱:
预先计算g ^ B:(def GpowB (math/expt g B))
迭代[1 1]
计算指数:(def exps (take B (iterate (fn [[e eb]] [(* e G) (* eB GpowB)]) [1 1])))
为解决方案过滤此序列并获取结果:(map #(mod (/ H (first %)) P) (filter (fn [[e eb]] (= (mod (/H e) P) (mod eb P))) exps))
考虑一下提示,我现在无法测试它。
在您的更新中,您会更快,因为您避免在地图中查找,为每个元素调用(res-map n)
。