以下内容来自Ruby 2.3.1文档,用于根据应用于原始集合中每对元素的特定条件将集合划分为一组子集。基本上,如果集合中的两个数字在彼此的1个单位内,则它们属于原始集合的子集集合中的相同子集。
require 'set'
numbers = Set[1, 3, 4, 6, 9, 10, 11]
set = numbers.divide { |i,j| (i - j).abs == 1 }
p set # => #<Set: {#<Set: {1}>,
# #<Set: {11, 9, 10}>,
# #<Set: {3, 4}>,
我认为我工作的问题可能会使用此功能。这就是问题。对于来自S的成对事物,存在n个事物的集合S和接近函数,其对于该集合中的一些对具有正值。对于未指定接近函数的值的对,可以假设这些值为0.还存在阈值参数。该程序的目的是在集合S上引入分区(集合中的一组成对的不相交和相互穷尽的子集),使得如果它们的接近函数值超过阈值参数,则两个事物属于相同的子集(反向)不一定是真的。)
此程序的输入为此格式
t&lt; -threshold参数(大于0的浮点数)
n&lt; - 要遵循的行数(整数)
Thing_i_1 Thing_j_1 proximity_ij_1(Thing_i_1和Thing_j_1是整数,proximity_ij_1是浮点数且大于0)
.... .... Thing_i_n Thing_j_n proximity_ij_n
输出是原始集合的上述成对不相交和相互穷尽的子集的集合,使得具有至少等于阈值参数的邻近函数值的两个事物落入相同的子集中。
我编写了下面的程序来实现这一目标,但它无法形成有问题的集合的子集。我的意见是
0.2
3
1 2 0.3
3 4 0.1
2 5 0.25
输出应该是{{1,2,5},{3},{4}}因为1,2应该属于同一个子集,因此应该是2.5,因为在每种情况下接近函数值都超过阈值参数(因此1和5有效地属于同一子集),3和4形成自己的子集。
require 'set'
t=gets.chomp.to_f
n=gets.chomp.to_i
edge=Struct.new(:n1,:n2)
se=Array.new
af=Array.new
sv=Set.new
for i in (0..n-1)
s=gets.chomp.split(" ")
se.insert(-1,edge.new(s[0],s[1]))
af.insert(-1,s[2].to_f)
if (sv.member? s[0])==false
sv.add(s[0])
end
if (sv.member? s[1])==false
sv.add(s[1])
end
end
c=sv.divide { |i,j| (k=se.index(edge.new(i,j)))!=nil && af[k]>=t }
p c
输出:
#<Set: {#<Set: {"5"}>, #<Set: {"2"}>, #<Set: {"1"}>, #<Set: {"3"}>, #<Set: {"4"}
>}>
除法功能似乎不起作用。我做错了吗?为什么我得到五个不相交的子集而不是预期的三个?我打印出除法块中条件的值,并且对于1,2和2,5完全正确,但是1,2和5在不同的子集中结束。有人可以帮忙吗?谢谢。
答案 0 :(得分:1)
divide
只会划分block.call(a, b)
&amp;&amp; block.call(b, a)
。让你的se
反身(即插入边缘2-1,4-3和5-2)它会起作用。或者,如果true
或edge.new(i,j)
位于edge.new(j, i)
,则会阻止您返回se
。关于类型还有一个错误:您从字符串(edge.new(s[0],s[1])
)创建边缘,但是对整数边缘(edge.new(i,j)
)进行测试,因此成员资格测试将失败。
那就是说,这是非常不好的代码。如果我要重写它,它会是这样的:
require 'set'
Edge = Struct.new(:v1, :v2, :p)
edges = {}
vertices = Set.new
t = gets.chomp.to_f
n = gets.chomp.to_i
n.times do
v1, v2, p = *gets.chomp.split
v1 = v1.to_i
v2 = v2.to_i
p = p.to_f
edge = Edge.new(v1, v2, p)
edges[[v1, v2]] = edge
vertices << v1 << v2
end
c = vertices.divide { |v1, v2|
(edge = edges[[v1, v2]] || edges[[v2, v1]]) && edge.p >= t
}
p c
# => #<Set: {#<Set: {1, 2, 5}>, #<Set: {3}>, #<Set: {4}>}>
基本上 - 使用哈希,这样你总能通过索引快速找到边缘,使用<<
将东西放到其他东西中,记住一组的整点是它不会插入同样的事情两次,对象是真实的,所以你不必明确测试!= nil
,永远不要使用for
:)
答案 1 :(得分:0)
编辑:我发现我回答了一个没有被问到的问题。我错误地认为当[d,e]
接近少于而不是阈值时,d
和e
将添加到其中一个(部分构建)设置是否有&#34;路径&#34;从d
或e
到该集合的一个元素。但是,我会留下我的答案,因为任何想要解决我所解决的问题的人都可能感兴趣。
这是另一种不使用Set#divide的方式。
<强>代码强>
require 'set'
def setify(distances, threshold)
sets = []
g = distances.dup
while (ret = g.find { |_,prox| prox >= threshold })
(n1,n2),_ = ret
s = [n1,n2].to_set
g.delete [n1,n2]
while (ret = g.find { |(n1,n2),_| s.include?(n1) || s.include?(n2) })
pair,_ = ret
s.merge pair
g.delete pair
end
sets << s
end
g.keys.flatten.each { |k| sets << [k].to_set }
sets
end
<强>实施例强>
threshold = 0.2
distances = { [1,2]=>0.3, [3,4]=>0.1, [2,5]=>0.25 }
setify(distances, threshold)
#=> [#<Set: {1, 2, 5}>, #<Set: {3}>, #<Set: {4}>]
distances = { [1,2]=>0.3, [3,4]=>0.1, [6,8]=>0.2, [2,5]=>0.25, [8,10]=>0 }
setify(distances, threshold)
#=> [#<Set: {1, 2, 5}>, #<Set: {6, 8, 10}>, #<Set: {3}>, #<Set: {4}>]
<强>解释强>
假设
threshold = 0.2
distances = { [1,2]=>0.3, [3,4]=>0.1, [6,8]=>0.2, [2,5]=>0.25, [8,10]=>0 }
然后
sets = []
g = distances.dup
#=> {[1, 2]=>0.3, [3, 4]=>0.1, [6, 8]=>0.2, [2, 5]=>0.25, [8, 10]=>0}
作为
ret = g.find { |_,prox| prox >= threshold }
#=> [[1, 2], 0.3]
是真的,我们进入(外部)while
循环。我们现在构建一个包含s
和1
的连接集2
。
(n1,n2),_ = ret
#=> [[1, 2], 0.3]
s = [n1,n2].to_set
#=> #<Set: {1, 2}>
由于已经处理了[n1,n2]
,我们必须从g
删除该密钥(因此需要g = distances.dup
,以避免变异distances
)。
g.delete [n1,n2]
#=> 0.3
现在让我们看g
。
g #=> {[3, 4]=>0.1, [6, 8]=>0.2, [2, 5]=>0.25, [8, 10]=>0}
现在在[a,b]
中查找另一个密钥g
(不是&#39; g&#39;的音乐键),以便a
或b
(或两者)都在集s
中。如果找到此类密钥,请尝试将a
和b
添加到s
,然后从[a,b]
中删除密钥g
。 (该密钥的两个元素中的至多一个将被添加到集合中。)
ret = g.find { |(n1,n2),_| s.include?(n1) || s.include?(n2) }
#=> [[2, 5], 0.25]
找到键值对,因此我们进入循环
pair,_ = ret
#=> [[2, 5], 0.25]
pair
#=> [2, 5]
s.merge pair
#=> #<Set: {1, 2, 5}>
g.delete pair
#=> 0.25
g
#=> {[3, 4]=>0.1, [6, 8]=>0.2, [8, 10]=>0}
现在再次执行while
表达式。
ret = g.find { |(n1,n2),_| s.include?(n1) || s.include?(n2) }
#=> nil
由于g
没有更多的密钥连接&#34;对于s
的元素,我们将s
添加到sets
,
sets << s
#=> [#<Set: {1, 2, 5}>]
并继续在外循环中继续。
ret = g.find { |_,prox| prox >= threshold }
#=> [[6, 8], 0.2]
我们发现另一个集合的开头至少有一对符合阈值,我们创建一个新集合并删除g
的关联键,
(n1,n2),_ = ret
#=> [[6, 8], 0.2]
n1 #=> 6
n2 #=> 8
s = [n1,n2].to_set
#=> #<Set: {6, 8}>
g.delete [n1,n2]
#=> 0.2
g #=> {[3, 4]=>0.1, [8, 10]=>0}
并着手构建该套装。
ret = g.find { |(n1,n2),_| s.include?(n1) || s.include?(n2) }
#=> [[8, 10], 0]
pair,_ = ret
#=> [[8, 10], 0]
s.merge pair
#=> #<Set: {6, 8, 10}>
g.delete pair
#=> 0
g #=> {[3, 4]=>0.1}
ret = g.find { |(n1,n2),_| s.include?(n1) || s.include?(n2) }
#=> nil
所以我们已经完成了s
集的构建。
sets << s
#=> [#<Set: {1, 2, 5}>, #<Set: {6, 8, 10}>]
再次尝试进入外循环(即,看看是否有另一组包含一对符合阈值的邻近元素)。
ret = g.find { |_,prox| prox >= threshold }
#=> nil
g
左边的键中的每个元素都必须包含它自己的集合。
b = g.keys
#=> [[3, 4]]
c = b.flatten
#=> [3, 4]
c.each { |k| sets << [k].to_set }
#=> [3, 4]
返回sets
sets
#=> [#<Set: {1, 2, 5}>, #<Set: {6, 8, 10}>, #<Set: {3}>, #<Set: {4}>]