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

  • 本文关键字:数组 两个 2D 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、...

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

代码

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( 和数组#组合。

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

步骤如下。

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)

最后,对于哈希f中的每个键k,我们需要有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

查找点之间距离的另一个选项是创建一个数组类的补丁,然后实现,如 Cary Swoveland 已经显示的那样。

这是模块,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

Yu 必须将模块包含在数组类中:

Array.include ArrayAlgebraPatch

因此,您可以致电:

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

并应用于您的向量,找到点之间的距离:

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

无论如何,如果一个标准的库(如Vector(已经完成了我们需要的,我们最好不要重新发明轮子

我把你的数组改成了哈希,因为它更有意义。

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

最新更新