-1

According as the number of elements in a set of numbers is odd or even, median of that set is defined respectively as the middle value or the average of the two middle values in the list that results when the set is sorted.

Below is code for calculating the "running" median of a list of numbers. "Running" median is a dynamic median which is re-calculated with the appearance of a new number as the list is scanned for all numbers that have appeared thus far. Input is an integer n followed by a list of n integers, and output should be the "running" median of the list as the list is scanned. For example,

3
4
1
5

should yield

4
2.5
4

because 4 is the median of [4], 2.5 ((1+4)/2)is the median of [4,1] and 4 again is the median of [4,1,5].

My program works correctly, but it times out on a certain test on very large inputs. I suspect that this copying step is the problem.

a=(a[0,(k=a.index(a.bsearch{|x|x>=t}))].push(t) + a[k,a.length-k])

But I am not sure because this copy is meant to be a shallow copy as far as I know. Also, I am not doing a regular insert anywhere, which would involved shifting elements and thus result in slowing down the code, into the array that contains the numbers.

n=gets.chomp.to_i
a=[]
n.times do
    t=gets.chomp.to_i

    a==[]||(t<=a.first) ? a.unshift(t): t>=a.last ? a.push(t) : a=(a[0,(k=a.index(a.bsearch{|x|x>=t}))].push(t) + a[k,a.length-k])


    p (l=a.count)%2==0 ? ((a[l/2] + a[l/2-1])/2.0).round(1):a[(l-1)/2].round(1)
end

Can anybody point out where the problem could be? Thank you.

Here is a less obfuscated version.

n=gets.chomp.to_i
a=[]
n.times do
    t=gets.chomp.to_i


    if a==[]||(t<=a.first) 
        a.unshift(t)
    else 
        k=a.index(a.bsearch{|x|x>=t})
        if k.nil? == true
            k=a.length
        end
        a=a[0,k].push(t)+ a[k,a.length-k]
    end
    p (l=a.count)%2==0 ? ((a[l/2] + a[l/2-1])/2.0).round(1):a[(l-1)/2].round(1)
end
user17144
  • 428
  • 3
  • 18
  • I just wanted to point out that an efficient online algorithm for median is actually an active area of research at the moment, with the rise of big data and computational statistics. Your problem is not *exactly* an online problem, but can probably be solved by an efficient online algorithm such as [binmedian](http://www.stat.cmu.edu/~ryantibs/median/). – Jörg W Mittag Oct 03 '16 at 01:24
  • Is there any way to move this to code review from here? Thank you, all. – user17144 Oct 03 '16 at 05:10
  • @Jörg W Mittag Thank you. There is apparently a method using heaps that is supposed to be faster. I am looking into it. – user17144 Oct 03 '16 at 05:16

1 Answers1

0

I think...

a=(a[0,(k=a.index(a.bsearch{|x|x>=t}))].push(t) + a[k,a.length-k])

...because it's creating a new array every time, is likely an expensive operation as the array gets bigger.

Better might actually be something that mutates the original array.

a.insert((a.index{|x|x>t} || -1), t)

It also handles the edge cases of less than first or greater than last, so you can remove those tests. Also works on first pass (empty array a)

SteveTurczyn
  • 36,057
  • 6
  • 41
  • 53
  • This is really clever code, but it still times out on an array which is 100000 elements long. When you use insert, Ruby shifts at least part of the rest of the elements unless the inserted element is at the end. For example, when you insert at the beginning there are a.length shifts. 0th element is shifted to 1st place, 1st element shifted to the 2nd place, and so on. That is why I broke up the array at the right place into two pieces, put in t, and then re-joined the pieces. But it is still too slow. The interesting thing is that insort in Python which, I think, uses insert does not time out. – user17144 Oct 02 '16 at 22:49
  • What you say regarding the creation of the arrays make sense. Can I insert without shifting any elements? – user17144 Oct 02 '16 at 22:58