[英]Summing Clojure Ratios is slow
我在Clojure中總結了一長串比率,例如:
(defn sum-ratios
[n]
(reduce
(fn [total ind]
(+
total
(/
(inc (rand-int 100))
(inc (rand-int 100)))))
(range 0 n)))
各種n的運行時間是:
(不太精確)替代方案是將這些值相加為雙精度:
(defn sum-doubles
[n]
(reduce
(fn [total ind]
(+
total
(double
(/
(inc (rand-int 100))
(inc (rand-int 100))))))
(range 0 n)))
此版本的運行時為:
為什么加總比率要慢得多? 我猜測它與找到Ratios的分母的最小公倍數有關,但是有沒有人知道Clojure用什么算法來比較Ratios?
讓我們來看看當你+
兩個Ratio
s時會發生什么,這是在減少的每個步驟中發生的事情。 我們從+的兩個版本開始:
([xy] (. clojure.lang.Numbers (add xy)))
這將我們帶到Numbers.add(Obj, Obj)
:
return ops(x).combine(ops(y)).add((Number)x, (Number)y);
ops
查看第一個操作數的類,並找到RatioOps
。 這導致了RatioOps.add
函數:
final public Number add(Number x, Number y){
Ratio rx = toRatio(x);
Ratio ry = toRatio(y);
Number ret = divide(ry.numerator.multiply(rx.denominator)
.add(rx.numerator.multiply(ry.denominator))
, ry.denominator.multiply(rx.denominator));
return normalizeRet(ret, x, y);
}
所以這是你的算法。 這里有五個 BigInteger
操作(三個乘法,一個加法,一個除法):
(yn*xd + xn*yd) / (xd*yd)
你可以看到如何實現多重 ; 它本身並不是微不足道的,你可以自己檢查其他人。
果然, 除法函數包括找到兩個數字之間的gcd
,以便可以減少:
static public Number divide(BigInteger n, BigInteger d){
if(d.equals(BigInteger.ZERO))
throw new ArithmeticException("Divide by zero");
BigInteger gcd = n.gcd(d);
if(gcd.equals(BigInteger.ZERO))
return BigInt.ZERO;
n = n.divide(gcd);
d = d.divide(gcd);
...
}
gcd
函數創建兩個新的MutableBigInteger
對象。
從計算上來說,它是昂貴的,正如你從上面所有的看到的那樣。 但是,不要打折額外附帶對象創建的成本(如上面的gcd
),因為我們涉及非緩存內存訪問通常會更昂貴。
double
轉換不是免費的,FWIW,因為它涉及兩個新創建的BigDecimal
的划分 。
您真的需要一個分析器來確切了解成本的位置。 但希望上面給出了一點背景。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.