开发者

Calculate missing values in an array from adjacent values

Given an array

[50,30,0,0,10,0,30,60,0]

I need to replace the zeroes with calculated values to create a 'curve', so for example, between 10 and 30, the zero could be replaced with 20.

I keep thinking there must be a cool ruby way of doing this, but I cant find one. Can anyone help? The solution needs to take into account multiple adjacent zeroes, and zeroes at the start and end of the range.

Anyone any ideas开发者_Python百科?


The term you seem to be unaware of is interpolation. The Wikipedia article is a good place to start - exactly what algoithm is best suited to you depends on the exact context of your problem so we can't give you the one true answer here.


a =[50,30,0,0,10,0,30,60,0]

a.each_index{|i| a[i] = a[i-1] - ((a[i-2] - a[i-1])/2).to_i if a[i] == 0 && i > 1 }

puts a.inspect # [50, 30, 20, 15, 10, 8, 30, 60, 75]

I can't work out why the last number might be 80 in your spec however? Plus it doesn't work for the first two items in the array.


If there could not be consecutive zeroes, this unintelligible one-liner would do the trick (list is the given list of numbers):

[0, *list, 0].each_cons(3).map { |p, x, n| x == 0 ? (p + n)/2 : x }

Ruby 1.9 only, I think.


def find_consecutive_values( array, value=nil )
  raise "Need a value or block to find" unless value || block_given?
  start = last = nil
  ranges = []
  indices = array.each_with_index do |o,i|
    if ((block_given? && yield(o)) || o==value)
      start = i unless start
      last = i
    else
      ranges << (start..last) if start && last
      start = last = nil
    end
  end
  ranges << (start..last) if start && last
  ranges
end

def interpolate_zeros( array, round=false )
  result = array.dup
  find_consecutive_values( array, 0 ).each do |range|
    next unless range.first>0 && range.last<(array.length-1)
    before = result[range.first - 1]
    after  = result[range.last  + 1]
    diff   = after - before
    size   = (range.last - range.first + 2).to_f
    range.each_with_index do |i,idx|
      value = before + diff * (idx+1)/size
      value = value.round if round
      result[i] = value
    end
  end
  result
end

p interpolate_zeros( [0,50,30,0,0,10,0,30,60,0], true )
#=> [0, 50, 30, 23, 17, 10, 20, 30, 60, 0]


Just stumbled across this question. There is a ruby gem "interpolator", which just does what you want and probably tons more:

http://interpolator.rubyforge.org.

Here is a short introduction:

http://fabianosoriani.wordpress.com/2010/02/23/ruby-interpolation-with-gem-interpolator/

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜