除了在Ruby 2.3.1中设置的函数

时间:2016-09-27 23:56:57

标签: ruby set divide ruby-2.3.1

以下内容来自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在不同的子集中结束。有人可以帮忙吗?谢谢。

2 个答案:

答案 0 :(得分:1)

divide只会划分block.call(a, b)&amp;&amp; block.call(b, a)。让你的se反身(即插入边缘2-1,4-3和5-2)它会起作用。或者,如果trueedge.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]接近少于而不是阈值时,de将添加到其中一个(部分构建)设置是否有&#34;路径&#34;从de到该集合的一个元素。但是,我会留下我的答案,因为任何想要解决我所解决的问题的人都可能感兴趣。

这是另一种不使用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循环。我们现在构建一个包含s1的连接集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;的音乐键),以便ab (或两者)都在集s中。如果找到此类密钥,请尝试将ab添加到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}>]