我有一个二维坐标点数组:
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 ......
我不知道如何构建一个遍历所有元素的循环以及如何整合距离公式。
答案 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