Finding the closest value from a matrix

I have a matrix that looks something like this

   85        90       100       125        150         175

8 1.183 1.118 1.006 0.805 0.671 0.575
10 1.847 1.744 1.57 1.256 1.047 0.897
12 2.659 2.511 … … … …
16 … … … … … …

Say, I have a value of 1.1, I want it to automatically choose the
closest bigger value i.e. 1.118 and say 8 mm diameter at 90 mm spacing.
How can this be implemented? I thought of creating hashes. but it seems
too complicated. If anyone is wondering about the formula it goes like
this.

value = 2 x area of circle / spacing

Mahadev I.:

I have a matrix that looks something like this

   85        90       100       125        150         175

8 1.183 1.118 1.006 0.805 0.671 0.575
10 1.847 1.744 1.57 1.256 1.047 0.897
12 2.659 2.511 … … … …
16 … … … … … …

Say, I have a value of 1.1, I want it to automatically choose the
closest bigger value i.e. 1.118 and say 8 mm diameter at 90 mm
spacing. How can this be implemented? I thought of creating hashes.
but it seems too complicated. If anyone is wondering about the formula
it goes like this.

value = 2 x area of circle / spacing

Are the spacings restricted to 85, 90, 100, 125, 150 and 175?

Are the diameters restricted to 8, 10, 12 and 16?

Otherwise, do you have formulæ for how the diameters and spacings grow?

Otherwise the matrix is so small that maybe
you can generate and search it on runtime:

diameters = [8, 10, 12, 16]
spacings = [85, 90, 100, 125, 150, 175]
diameters.product(spacings).map { |d, s| [0.5 * Math::PI * d ** 2 / s,
d, s] }.sort.find { |x,| x > 1.1 }

=> [1.11701072127637, 8, 90]

— Shot

closest bigger value i.e. 1.118 and say 8 mm diameter at 90 mm spacing.
How can this be implemented? I thought of creating hashes. but it seems
too complicated. If anyone is wondering about the formula it goes like
this.

value = 2 x area of circle / spacing

This sounds an awful lot like homework.

If there is a finite list of spacings, you can figure out the
ideal diameter for each one (by reversing the formula), round each up to
the closest permited diamiter, and choose the one cooresponding to
the smallest computed value (using the given formula). Look at
Array#collect, Array#find, Array#sort, and Array#first.

– MarkusQ

Hello Thanks. They are limited to those spacings and those diameters, as
they are industry standards. However there are 5 more higher spacings.
So basically you are creating a map? That’s filled with the values?
Thats quiet interesting. I tried the exact same thing, it says
“undefined method ‘product’ for for [8, 10, 12, 16]:Array”

This sounds an awful lot like homework.

If there is a finite list of spacings, you can figure out the
ideal diameter for each one (by reversing the formula), round each up to
the closest permited diamiter, and choose the one cooresponding to
the smallest computed value (using the given formula). Look at
Array#collect, Array#find, Array#sort, and Array#first.

– MarkusQ

I assure you, this is no homework. I am complete newbie, and I am
creating a plug-in to work with SketchUp. I could email it you if you
would like to see.

Mahadev I.:

They are limited to those spacings and those diameters, as they
are industry standards. However there are 5 more higher spacings.

That’s ok, then the matrix is small enough to be kept in memory
(and you can pregenerate it and only search on runtime, of course):

pregenerate

diameters = [8, 10, 12, 16]
spacings = [85, 90, 100, 125, 150, 175] # extend as needed
values = diameters.product(spacings).map { |d, s| [0.5 * Math::PI * d
** 2 / s, d, s] }.sort

search

values.find { |x,| x > 1.1 } # => [1.11701072127637, 8, 90]
values.find { |x,| x > 2 } # => [2.26194671058465, 12, 100]

So basically you are creating a map? That’s filled with the values?

I’m mapping every diameter+spacing pair to [value, diameter, spacing]
triple, then I’m sorting the triples and picking the first one with the
first element (the value) larger than the desired input.

Thats quiet interesting. I tried the exact same thing, it says
“undefined method ‘product’ for for [8, 10, 12, 16]:Array”

The above works in Ruby 1.8.7 and 1.9.1.

If you run an earlier Ruby 1.8 version, you can either use the
backports gem¹ or implement a simple variation tailored to your case:

class Array
def product other
inject([]) do |result, my|
result.concat other.map { |their| [my, their] }
end
end unless method_defined? :product
end

[:a, :b].product [:c, :d]

=> [[:a, :c], [:a, :d], [:b, :c], [:b, :d]]

¹ GitHub - marcandre/backports: The latest features of Ruby backported to older versions.

— Shot

On Nov 21, 1:04 pm, Mahadev I. [email protected] wrote:

How can this be implemented? I thought of creating hashes. but it seems
too complicated. If anyone is wondering about the formula it goes like
this.

Create a one-dimensional array where each entry records the value, row
and column number (or row and column headers).

This should be programmatically done by iterating your array

not explicitly as I’m showing it here.

Descriptor = Struct.new :row, :col, :value
a = [
Descriptor.new(0, 0, 1.183),
Descriptor.new(0, 1, 1.118),
Descriptor.new(0, 1, 1.006),

…etc.

]

Sort the array by value

a = a.sort_by{ |desc| desc.value }

Create a method that finds the closest (larger) value in the array

via a binary search: Binary search algorithm - Wikipedia

(Left as an exercise for the reader.)

Now you know what row and column it came from.

On 2009-11-21, Mahadev I. [email protected] wrote:

Hello Thanks. They are limited to those spacings and those diameters, as
they are industry standards. However there are 5 more higher spacings.
So basically you are creating a map? That’s filled with the values?
Thats quiet interesting. I tried the exact same thing, it says
“undefined method ‘product’ for for [8, 10, 12, 16]:Array”

There’s a few options.

The simplest is just to iterate through the whole thing, remembering:

  • The location of the closest larger value you’ve previously seen
  • How close that value was

For each value, if it is larger, see how close it is; if it’s closer
than
your previous best guess, it becomes your new best guess. At the end of
the process, you have the best guess.

-s

thanks for all the methods. I never realised there were so many ways to
do it!
I decided to do the 1st method. I have repeat the method a couple of
more times than shown here… Its a good way.

reinforcement = [6, 8, 10, 12, 16, 20, 25, 32, 40]
number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
diameters = [8, 10, 12, 16]
spacings = [85, 90, 100, 125, 150, 175, 200, 225, 250, 275, 300]
@shear_links = diameters.product(spacings).map { |d, s| [0.5 * Math::PI

  • d ** 2 / s, d, s] }.sort.find { |x,| x > @asw_s }
    @compression = reinforcement.product(number).map { |r, n| [0.25 *
    Math::PI * r ** 2 * n, r, n] }.sort.find { |x,|x > @comp_steel}
    @tension = reinforcement.product(number).map { |r, n| [0.25 * Math::PI *
    r ** 2 * n, r, n] }.sort.find { |x,|x > @ten_steel}

Mahadev I.:

thanks for all the methods. I never
realised there were so many ways to do it!

I decided to do the 1st method. I have repeat the method
a couple of more times than shown here… Its a good way.

reinforcement = [6, 8, 10, 12, 16, 20, 25, 32, 40]
number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
diameters = [8, 10, 12, 16]
spacings = [85, 90, 100, 125, 150, 175, 200, 225, 250, 275, 300]
@shear_links = diameters.product(spacings).map { |d, s| [0.5 * Math::PI * d ** 2 / s, d, s] }.sort.find { |x,| x > @asw_s }
@compression = reinforcement.product(number).map { |r, n| [0.25 * Math::PI * r ** 2 * n, r, n] }.sort.find { |x,|x > @comp_steel}
@tension = reinforcement.product(number).map { |r, n| [0.25 * Math::PI * r ** 2 * n, r, n] }.sort.find { |x,|x > @ten_steel}

How about some DRY-ing up of the code (and
not recomputing the arrays every time)? :slight_smile:

def values rows, cols, &formula
rows.product(cols).map do |row, col|
[formula.call(row, col), row, col]
end.sort
end

reinforcement = [6, 8, 10, 12, 16, 20, 25, 32, 40]
number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
diameters = [8, 10, 12, 16]
spacings = [85, 90, 100, 125, 150, 175, 200, 225, 250, 275, 300]

@ds ||= values(diameters, spacings) { |d, s| 0.5 * Math::PI * d ** 2
/ s }
@rn ||= values(reinforcement, number) { |r, n| 0.25 * Math::PI * r ** 2

  • n }

@shear_links = @ds.find { |x,| x > @asw_s }
@compression = @rn.find { |x,| x > @comp_steel }
@tension = @rn.find { |x,| x > @ten_steel }

— Shot