Clojure, Haskell & Ruby vs Euler 25
bestinclass.dk
bestinclass.dk
You CAN take advantage of matrix exponentiation by using binary search.
Here are my benchmarks:
require 'matrix'
limit = 10**999
def fib(n)
(Matrix[[1,1],[1,0]]**(n-1))[0,0]
end
# search for the point where the predicate changes from true to false
def bsearch(lower,upper)
while lower < upper + 1
mid = (lower+upper)/2
if yield mid
lower = mid + 1
else
upper = mid - 1
end
end
return lower
end
require 'benchmark'
Benchmark.bm do |x|
x.report('stupid linear search') do
i=1
i += 1 while fib(i) < limit
end
x.report('linear search') do
n,a,b = 1,0,1
while b < limit
n += 1
a,b = b,a+b
end
end
x.report('binary search') do
lower = 1
# find an upper bound
upper = 1
upper *= 2 while fib(upper) < limit
bsearch(lower,upper){|n| fib(n) < limit}
end
end
Results: user system total real
stupid linear search 7.301000 0.000000 7.301000 ( 6.994000)
linear search 0.016000 0.000000 0.016000 ( 0.021000)
binary search 0.046000 0.000000 0.046000 ( 0.039000)
So normal linear search wins and is pretty fast (faster than Clojure according to his benchmarks -- assuming our machines are about as fast because the slow Ruby takes ~7sec on both).Oh and Ruby wins the loc contest:
n,a,b = 1,0,1
n,a,b = n+1,b,a+b while b<limit
Compared to second best: (def fib-seq (lazy-cat [0 1] (map + fib-seq (rest fib-seq))))
(let [limit (.pow (BigInteger/TEN) 999)]
(count (take-while #(< % limit) fib-seq)))
Fewer lines and shorter lines and clearer and simpler. (defn fib [n a b lim] (if (< b lim) (recur (inc n) b (+ a b) lim) n))
(fib 1 0 1 (.pow (BigInteger/TEN) 999))
~0.0014 on my machine.EDIT: I don't condone the article at all, it's link bait. And I think it's silly that people get up in arms about articles like this. Ruby is great. Haskell is great. Haskell and Clojure are really fast. End of story.
Another thing to consider when using phrases like "O(logn)" is that the numbers grow exponentially with n (meaning their lengths grow linearly). Many CS students are trained to think of addition, multiplication etc. as "constant time" operations but that's not true for bignums!
BTW, I assume they meant difference equations.
NO FIB!!
I won't comment on the single-expression do block or all the parens in the putStrLn expression or mention that "putStrLn . show" is just "print".
Anyway, 6 lines of Haskell is just excessive. How about two lines:
fibs = 1 : 1 : (zipWith (+) fibs $ tail fibs)
main = print . length . takeWhile (< 10^999) $ fibs
I think this is as readable as the example, and I didn't even have to line-break any expression. n,a,b = 1,0,1
n,a,b = n+1,b,a+b while b<10**999
p n
48 chars. a,b = 0,1
a,b = b,a+b while b<limit
With a counter interspersed ;)We have the shootout to answer these questions, and it doesn't include fibonacci.
public void test25() {
BigInt f1 = new BigInt(0);
BigInt f2 = new BigInt(1);
BigInt f3 = null;
int i = 2;
while(true) {
f3 = f1.add(f2);
f1 = f2;
f2 = f3;
if (f3.toString().length() >= 1000)
break;
i++;
}
res("25", 4782, i);
} problem25 := method(digits,
term := 0
n0 := BigNum with(0)
n1 := BigNum with(1)
loop(
n0 = n0 + n1
n1 = n0 - n1
if(n1 asString size >= digits, break)
term = term + 1
)
term
)
problem25(1000) println user> (time (let [limit (.pow (BigInteger/TEN) 999)]
(loop [a 0 b 1 i 1]
(if (< b limit)
(recur b (+ a b) (inc i))
i))))
"Elapsed time: 3.554351 msecs"
4782 p (Math.log(10)*999/Math.log(g=5**0.5/2+0.5)+g).ceil