如何从2D数组中减去两个子数组

时间:2018-09-26 19:15:40

标签: arrays ruby

我有一个二维坐标点数组:

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

我需要找出它们之间的距离,例如从A到B,C,D,...,从B到C,D,E ......

我不知道如何构建一个遍历所有元素的循环以及如何整合距离公式。

3 个答案:

答案 0 :(得分:1)

代码

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(又称r)和Array#combination

请注意,该方法不需要哈希中的值是两个元素的数组。它们可以是任何大小的数组。

步骤如下。

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] }

但是这会使哈希的大小增加一倍。相反,我将默认proc附加到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]])返回零,则可以将默认proc更改为以下内容。

f.default_proc = Proc.new { |g,k| k.first==k.last ? 0 : g[k.reverse] }
f[[:C, :C]]
  #=> 0
f[[:B, :A]]
  #=> 40.7922

答案 1 :(得分:1)

查找点之间距离的另一种方法是创建数组Class的补丁,然后按照Cary Swoveland的说明实施。

这是模块,distance_from只是Pitagora公式。

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)已经满足了我们的需求,我们最好不要重新发明轮子

答案 2 :(得分:0)

我将您的数组更改为散列,因为这样做更有意义。

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