Как вычесть два подмассива из двумерного массива - PullRequest
0 голосов
/ 26 сентября 2018

У меня есть 2d-массив координатных точек:

array = [
  [24, 2],  # => A
  [32, 42], # => B
  [3, 11],  # => C
  [5, 9],   # => D
  [10, 5],  # => E
  [14, 2]   # => F
]

Мне нужно узнать расстояние между ними, например, от А до В, С, D, ..., от В доC, D, E, ...

Я не знаю, как построить цикл, который проходит через все элементы, и как интегрировать формулу расстояния.

Ответы [ 3 ]

0 голосов
/ 26 сентября 2018

Код

require 'matrix'

def pair_wise_distances(h)
  h.map { |k,v| [k, Vector[*v]] }.
    combination(2).
    each_with_object({}) { |((k1,v1),(k2,v2)),g|
      g[[k1,k2]] = (v2-v1).magnitude.round(4) }.
    tap { |g| g.default_proc = Proc.new { |f,k| f[k.reverse] } }
end

Пример

h = { A: [24,2], B: [32,42], C: [3,11], D: [5,9], E: [10,5], F: [14,2] }

g = pair_wise_distances(h)
  #=> {[:A, :B]=>40.7922, [:A, :C]=>22.8473, [:A, :D]=>20.2485,
  #    [:A, :E]=>14.3178, [:A, :F]=>10.0,    [:B, :C]=>42.45,
  #    [:B, :D]=>42.638,  [:B, :E]=>43.0465, [:B, :F]=>43.8634,
  #    [:C, :D]=>2.8284,  [:C, :E]=>9.2195,  [:C, :F]=>14.2127,
  #    [:D, :E]=>6.4031,  [:D, :F]=>11.4018, [:E, :F]=>5.0}
g[[:A, :B]]
  #=> 40.7922
g[[:B, :A]]
  #=> 40.7922

Пояснение

См. Vector :: [] , Vector # - , Vector # magnitude (aka r) и Array # комбинация .

Обратите внимание, что метод не требует, чтобы значения в хэше были двухэлементными массивами.Они могут быть массивов любого размера.

Шаги следующие.

f = h.map { |k,v| [k, Vector[*v]] }
  #=> [
  #     [:A, Vector[24, 2]], [:B, Vector[32, 42]], [:C, Vector[3, 11]],
  #     [:D, Vector[5, 9]], [:E, Vector[10, 5]], [:F, Vector[14, 2]]      
  #   ]
e = f.combination(2)
  #=> #<Enumerator: [
  #     [:A, Vector[24, 2]], [:B, Vector[32, 42]], [:C, Vector[3, 11]],
  #     [:D, Vector[5, 9]], [:E, Vector[10, 5]], [:F, Vector[14, 2]]
  #   ]:combination(2)>

Мы можем преобразовать e в массив, чтобы увидеть (e.size = 5*4/2 = 15) значения, которые будутгенерируется перечислителем.

e.to_a
  #=> [
  #     [[:A, Vector[24, 2]], [:B, Vector[32, 42]]],
  #     [[:A, Vector[24, 2]], [:C, Vector[3, 11]]],
  #     ...
  #     [[:A, Vector[24, 2]], [:F, Vector[14, 2]]],
  #     [[:B, Vector[32, 42]], [:C, Vector[3, 11]]],
  #     ...
  #     [[:C, Vector[3, 11]], [:D, Vector[5, 9]]],
  #     ...
  #     [[:D, Vector[5, 9]], [:E, Vector[10, 5]]],
  #     ...
  #     [[:E, Vector[10, 5]], [:F, Vector[14, 2]]]
  #   ]

Продолжение,

f = e.each_with_object({}) { |((k1,v1), (k2,v2)),g|
  g[[k1,k2]] = (v2-v1).magnitude.round(4) }
  #=> (as shown in "Example" section)

Например, вычисляется значение [:A, :B] (расстояние между :A и :B)следующим образом.

diff = Vector[32, 42] - Vector[24, 2]
  #=> Vector[8, 40]

diff.magnitude.round(4)
  #=> 40.7922

, что, как и следовало ожидать, равно

Math.sqrt(8**2 + 40**2).round(4)

Наконец, для каждого ключа k в хэше f нам нужно иметь f[k.reverse] возврат f[k].(Для ключа [:A, :B], например, необходимо вернуть значение [:B, :A], которое совпадает со значением [:A, :B]).Мы могли бы добавить «обратные» ключи:

g.keys.each { |k| g[k.reverse] = g[k] }

, но это удваивает размер хэша.Вместо этого я прикрепил процесс по умолчанию к f:

f.default_proc = Proc.new { |g,k| g[k.reverse] }

Это приводит к возвращению f[k.reverse], когда f не имеет ключа k:

g[[:A, :B]]
  #=> 40.7922
g[[:B, :A]]
  #=> 40.7922

Если мы хотим, чтобы f[[k, k]] (например, f[[:C, :C]]) возвращал ноль, мы могли бы изменить процедуру по умолчанию на следующую.

f.default_proc = Proc.new { |g,k| k.first==k.last ? 0 : g[k.reverse] }
f[[:C, :C]]
  #=> 0
f[[:B, :A]]
  #=> 40.7922
0 голосов
/ 27 сентября 2018

Еще один вариант найти расстояние между точками - создать патч для массива Class, а затем реализовать, как уже показывал Кэри Свовеланд.

Это модуль, distance_from - это просто формула Питагора.

module ArrayAlgebraPatch
  def distance_from(other)
    # raise "SIZE ERROR" if self.size != other.size
    Math.sqrt(self.zip(other).map { |e| (e[1]-e[0])**2 }.reduce(&:+))
  end
end

Ю должен включить модуль в класс Array:

Array.include ArrayAlgebraPatch

Чтобы вы могли позвонить:

[24,2].distance_from [32,42] #=> 40.792156108742276

И применить к своему вектору, найдярасстояние между точками в комбинации:

array.combination(2).map { |pt1, pt2| pt2.distance_from pt1 }

В любом случае, если стандартная библиотека (например, Vector) уже делает то, что нам нужно, нам лучше не изобретать колесо .

0 голосов
/ 26 сентября 2018

Я изменил ваш массив на хеш, так как это имело больше смысла.

class DistanceCalculator
  def initialize(x, y)
    @point_x = x
    @point_y = y
  end

  def coord_difference
    @coord_difference ||= @point_x.zip(@point_y).map { |x, y| y - x }
  end

  def distance
    Math.sqrt(coord_difference.first ** 2 + coord_difference.last ** 2)
  end
end

points = {
  A: [24,2],
  B: [32,42],
  C: [3,11],
  D: [5,9],
  E: [10,5],
  F: [14,2]
}

# example usage
puts DistanceCalculator.new(points[:A], points[:B]).distance
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...