Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-31 Thread David Powell
As an aside:

Fingertrees are an interesting way to keep a collection that can
efficiently compute means over its values, or a window of its values.

https://gist.github.com/672592

-- 
Dave

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-30 Thread Sean Corfield
On Fri, Mar 30, 2012 at 11:01 AM, Larry Travis  wrote:
> user=> (time (dotimes [i 10] (average1 mill-float-numbs)))
> "Elapsed time: 526.674 msecs"
>
> user=> (time (dotimes [i 10] (average2 mill-float-numbs)))
> "Elapsed time: 646.608 msecs"
>
> user=> (time (dotimes [i 10] (average3 mill-float-numbs)))
> "Elapsed time: 405.484 msecs"
>
> user=> (time (dotimes [i 10] (average4 mill-float-numbs)))
> "Elapsed time: 394.31 msecs"

I can understand the first one being "slow" but I'm a bit surprised
about the loop being the slowest of the four options. Can someone shed
some light on that?

Nice to see the accumulating reduce being faster since that's how I've
settled in to solving this kind of problem in our code, without really
wondering about performance (it's "fast enough" and I think it's the
more elegant solution).
-- 
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-30 Thread simon.T
Hi  Rob,

Appreciate it, I like the code and explanation, great!

Simon

On Thursday, March 29, 2012 6:28:48 PM UTC+8, Rob Nagle wrote:
>
> You can reduce in one pass with a function that tracks both the sum 
> and the count. 
>
> (defn avg [coll] 
>   (apply / (reduce (fn [[sum n] x] [(+ sum x) (inc n)]) [0 0] coll))) 
>
> This reduce function is somewhat unusual in that its arguments have 
> different forms. As a result, this one does require the initial-value 
> argument be used. It's set to [0 0] indicating the sum and count both 
> start at 0. The function then "consumes" the numbers in coll one at a 
> time, producing the running sum and count each time. Then we just 
> apply / to divide the sum by the count. 
>
> On Mar 28, 9:18 pm, "simon.T"  wrote: 
> > The obvious way is like the following, which traverse the sequence 2 
> times. 
> > Wondering what will be the efficient way... 
> > 
> > (defn avg [coll] 
> >   (/ (reduce + coll) (count coll)))

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-30 Thread Stephen Compall
On Thu, 2012-03-29 at 23:31 -0700, Benjamin Peter wrote:
> the sequence would have been traversed completely by the reduce call
> and therefore clojure could know it's size and provide a constant time
> count.
> 
> Could this be implemented?

Yes.  You could probably implement it yourself, as a wrapper sequence
type, though this wouldn't make all other sequence types automatically
countable.

> Is it?

It won't be, in general, because you would have to "hold onto the head"
until you got to the end, or go through a costly half-broken weak
reference dance.

It is worth considering that even for some uncounted sequences, the cost
of a second traversal for "count" may be less than the bookkeeping cost
of keeping a count as you traverse once.

-- 
Stephen Compall
^aCollection allSatisfy: [:each|aCondition]: less is better

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-30 Thread Larry Travis
I too think this is interesting because because it serves to illustrate 
some important general aspects of Clojure with a very simple problem.


I wrote four Clojure programs contrasting different ways of solving the 
problem, and then timed the application of each ten times to a 
million-item sequence /mill-float-numbs/ of floating-point random 
numbers.  Here are the interesting results:


(defn average1
  [seq1]
  (/ (reduce + seq1) (count seq1)))

(defn average2
  [seq1]
  (loop [remaining (rest seq1)
cnt 1
accum (first seq1)]
(if (empty? remaining)
  (/ accum cnt)
  (recur (rest remaining)
(inc cnt)
(+ (first remaining) accum)

(defn average3
  [seq1]
  (letfn [(count-add
 [ [cnt accum] numb]
 [(inc cnt) (+ accum numb)] ) ]
(let [result-couple (reduce count-add [0 0] seq1)]
  (/ (result-couple 1) (result-couple 0)

(defn average4
  [seq1]
(let [result-couple (reduce
  (fn [ [cnt accum] numb]
[(inc cnt) (+ accum numb)] )
  [0 0]
  seq1)]
  (/ (result-couple 1) (result-couple 0

user=> (time (dotimes [i 10] (average1 /mill-float-numbs/)))
"Elapsed time: 526.674 msecs"

user=> (time (dotimes [i 10] (average2 /mill-float-numbs/)))
"Elapsed time: 646.608 msecs"

user=> (time (dotimes [i 10] (average3 /mill-float-numbs/)))
"Elapsed time: 405.484 msecs"

user=> (time (dotimes [i 10] (average4 /mill-float-numbs/)))
"Elapsed time: 394.31 msecs"

  --Larry

On 3/30/12 1:31 AM, Benjamin Peter wrote:

I think this topic is interesting. My guess would be, that the
sequence would have been traversed completely by the reduce call and
therefore clojure could know it's size and provide a constant time
count.

Could this be implemented? Is it?


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-30 Thread Benjamin Peter
Hi,

On Mar 29, 10:43 pm, Alan Malloy  wrote:
> "Very likely" strikes me as a huge overstatement here. Most sequences
> that you want to average won't be source-code literals, they'll be
> lazy sequences, and those aren't counted.

I think this topic is interesting. My guess would be, that the
sequence would have been traversed completely by the reduce call and
therefore clojure could know it's size and provide a constant time
count.

Could this be implemented? Is it?


regards,

Benjamin

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-29 Thread David Cabana
> "Very likely" strikes me as a huge overstatement here. Most sequences
> that you want to average won't be source-code literals, they'll be
> lazy sequences, and those aren't counted

Point taken about lazy sequences. But the above was not intended to
suggest the sequence needs to be source code literal to satisfy
'counted?', rather that vectors, lists, maps, and sets do so.  That
covers a fair bit of ground.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-29 Thread Alan Malloy
On Mar 29, 10:18 am, David Cabana  wrote:
> On Thu, Mar 29, 2012 at 12:18 AM, simon.T  wrote:
> > The obvious way is like the following, which traverse the sequence 2 times.
> > ...
>
> The obvious way does not necessarily traverse the sequence twice.  If
> a sequence S satisfies the 'counted?' predicate, (count S) takes
> constant time. In particular
> user=> (counted? [:a :b :c])
> true
>
> user=> (counted? '(:a :b :c))
> true
>
> user=> (counted? {:a 1 :b 2 :c 3})
> true
>
> user=> (counted? #{:a :b :c})
> true
>
> The examples are stolen 
> from:http://clojuredocs.org/clojure_core/clojure.core/counted_q
>
> So it is very likely that (/ (reduce + coll) (count coll)) will not
> traverse 'coll' twice, and the natural way is the preferred way.
> 

"Very likely" strikes me as a huge overstatement here. Most sequences
that you want to average won't be source-code literals, they'll be
lazy sequences, and those aren't counted.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-29 Thread David Cabana
On Thu, Mar 29, 2012 at 12:18 AM, simon.T  wrote:
> The obvious way is like the following, which traverse the sequence 2 times.
> ...

The obvious way does not necessarily traverse the sequence twice.  If
a sequence S satisfies the 'counted?' predicate, (count S) takes
constant time. In particular
user=> (counted? [:a :b :c])
true

user=> (counted? '(:a :b :c))
true

user=> (counted? {:a 1 :b 2 :c 3})
true

user=> (counted? #{:a :b :c})
true

The examples are stolen from:
http://clojuredocs.org/clojure_core/clojure.core/counted_q

So it is very likely that (/ (reduce + coll) (count coll)) will not
traverse 'coll' twice, and the natural way is the preferred way.


-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-29 Thread Rob Nagle
You can reduce in one pass with a function that tracks both the sum
and the count.

(defn avg [coll]
  (apply / (reduce (fn [[sum n] x] [(+ sum x) (inc n)]) [0 0] coll)))

This reduce function is somewhat unusual in that its arguments have
different forms. As a result, this one does require the initial-value
argument be used. It's set to [0 0] indicating the sum and count both
start at 0. The function then "consumes" the numbers in coll one at a
time, producing the running sum and count each time. Then we just
apply / to divide the sum by the count.

On Mar 28, 9:18 pm, "simon.T"  wrote:
> The obvious way is like the following, which traverse the sequence 2 times.
> Wondering what will be the efficient way...
>
> (defn avg [coll]
>   (/ (reduce + coll) (count coll)))

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-29 Thread Rick Beerendonk
> The obvious way is like the following, which traverse the sequence 2 times.
> Wondering what will be the efficient way...

(defn avg [coll]
  (loop [c coll tot 0 cnt 0]
(if (empty? c)
  (/ tot cnt)
  (recur (rest c) (+ tot (first c)) (inc cnt)

This will loop only once.
It happens to be faster, but the difference is not explained by (count
coll) elimination only. Maybe reduce is a factor as well.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en


Re: What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-29 Thread Linus Ericsson
or to increase a counter while reducing it, a function like inc+ returning
{:sum sum :count count} and then take the sum/counter, which is the mean.

The problem is possible to state as a clean map-reduce problem with only
one traversing of the data. It's also possible to remove items form the
mean operation (ie, the problem is associative).

/Linus

2012/3/29 simon.T 

> The obvious way is like the following, which traverse the sequence 2 times.
> Wondering what will be the efficient way...
>
> (defn avg [coll]
>   (/ (reduce + coll) (count coll)))
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

What's the efficient functional way to computing the average of a sequence of numbers?

2012-03-29 Thread simon.T
The obvious way is like the following, which traverse the sequence 2 times.
Wondering what will be the efficient way...

(defn avg [coll]
  (/ (reduce + coll) (count coll)))

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en