如何返回具有重复元素的Ruby数组交集? (骰子系数中的双字母问题)

我正在尝试编写Dice的系数,但我对arrays交叉点有点问题。

def bigram(string) string.downcase! bgarray=[] bgstring="%"+string+"#" bgslength = bgstring.length 0.upto(bgslength-2) do |i| bgarray << bgstring[i,2] end return bgarray end def approx_string_match(teststring, refstring) test_bigram = bigram(teststring) #.uniq ref_bigram = bigram(refstring) #.uniq bigram_overlay = test_bigram & ref_bigram result = (2*bigram_overlay.length.to_f)/(test_bigram.length.to_f+ref_bigram.length.to_f)*100 return result end 

问题是,作为&删除重复,我得到这样的东西:

 string1="Almirante Almeida Almada" string2="Almirante Almeida Almada" puts approx_string_match(string1, string2) => 76.0% 

它应该返回100。

uniq方法指出它,但有信息丢失,这可能会在我工作的特定数据集中带来不必要的匹配。

如何获得包含所有重复项的交集?

正如Yuval F所说,你应该使用multiset 。 但是,Ruby标准库中没有multiset ,请看这里和这里 。

如果性能对您的应用程序并不重要,您仍然可以使用带有一点代码的Array来完成。

 def intersect a , b a.inject([]) do |intersect, s| index = b.index(s) unless index.nil? intersect << s b.delete_at(index) end intersect end end a= ["al","al","lc" ,"lc","ld"] b = ["al","al" ,"lc" ,"ef"] puts intersect(a ,b).inspect #["al", "al", "lc"] 

从这个链接我相信你不应该使用Ruby的集合而是使用多集合,因此每个bigram都会计算它出现的次数。 也许你可以将这个gem用于多重集合。 这应该为重复出现的双字母组织提供正确的行为。

根据@pierr的回答,我玩弄了一段时间,结果就是这样。

 a = ["al","al","lc","lc","lc","lc","ld"] b = ["al","al","al","al","al","lc","ef"] result=[] h1,h2=Hash.new(0),Hash.new(0) a.each{|x| h1[x]+=1} b.each{|x| h2[x]+=1} h1.each_pair{|key,val| result<<[key]*[val,h2[key]].min if h2[key]!=0} result.flatten 

=> ["al", "al", "lc"]

这可能是a& b的一种多重交叉,但是我没有接受它,因为我没有足够的测试来确定它。