簡體   English   中英

Clojure運行非常緩慢

[英]Clojure is running extremely slow

我制作了一個簡單的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我對查找結果功能做了一些更改,性能得到了改善:

(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))))

您正在計算很多巨大的數字。 當B為500時,您在映射中的鍵為0-499。 在g為642N的查找結果中,您要計算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))))

您的算法為O(n log n),因為expt對每個n進行log n乘法。 您可以將其減少為O(n),以更實用的方式再次寫入。 我的食譜:

  1. 預先計算g ^ B: (def GpowB (math/expt g B))

  2. 迭代[1 1]以計算指數: (def exps (take B (iterate (fn [[e eb]] [(* e G) (* eB GpowB)]) [1 1])))

  3. 過濾此序列以得到解並得出結果: (map #(mod (/ H (first %)) P) (filter (fn [[e eb]] (= (mod (/H e) P) (mod eb P))) exps))

認為這是一個提示,我現在無法對其進行測試。

在更新中,您會更快,因為可以避免在地圖中為每個元素調用(res-map n)進行查找。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM