[英]Ruby - array intersection (with duplicates)
我有array(1 and 2)
。 我如何從他們那里獲得array3
?
array1 = [2,2,2,2,3,3,4,5,6,7,8,9]
array2 = [2,2,2,3,4,4,4,4,8,8,0,0,0]
array3 = [2,2,2,3,4,8]
array1 & array2
返回[2,3,4,8]
,但我需要保留重復項。
(array1 & array2).flat_map { |n| [n]*[array1.count(n), array2.count(n)].min }
#=> [2,2,2,3,4,8]
步驟:
a = array1 & array2
#=> [2, 3, 4, 8]
a
( 2
)的第一個元素傳遞到塊並分配給塊變量:
n = 2
並執行塊計算:
[2]*[array1.count(2), array2.count(2)].min
#=> [2]*[4,3].min
#=> [2]*3
#=> [2,2,2]
所以2
被映射到[2,2,2]
。 計算是其余三個要素類似a
。 當我使用flat_map
,這將返回[2,2,2,3,4,8]
。
您是否還記得Enumerable#flat_map與Enumerable#map有何不同? 假設我使用map
而不是flat_map
。 然后
a.map { |n| [n]*[array1.count(n), array2.count(n)].min }
#=> [[2, 2, 2], [3], [4], [8]]
flat_map
只不過在每個數組的前面放了一個小圖示:
[*[2, 2, 2], *[3], *[4], *[8]]
#=> [2, 2, 2, 3, 4, 8]
如果數組array1
和array2
很大並且效率是一個問題,我們可以做一些O(N)預處理:
def cnt(arr)
arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
end
cnt1 = cnt(array1)
#=> {2=>4, 3=>2, 4=>1, 5=>1, 6=>1, 7=>1, 8=>1, 9=>1}
cnt2 = cnt(array2)
#=> {2=>3, 3=>1, 4=>4, 8=>2, 0=>3}
(array1 & array2).flat_map { |n| [n]*[cnt1[n], cnt2[n]].min }
#=> [2,2,2,3,4,8]
這是一個有趣的事情。 卡里的flat_map
解決方案特別聰明。 這是使用常規舊map
並在each_with_object
幫助下的另一種each_with_object
:
array1.each_with_object(array2.dup).map{|v,t| v if (l = t.index v) && t.slice!(l) }.compact
#=> [2,2,2,3,4,8]
這里的許多復雜性都涉及用於向map
提供足夠信息以完成任務的內聯體操:
#
# we want to duplicate array2 since we'll be
# mutating it to track duplicates
# \ array1 array2
# \ value copy
# \ \ /
array1.each_with_object(array2.dup).map{|v,t| ... }
# | /
# Enumerator for array1 Iterate over
# with a copy of array2 Enumerator with map
我們可以使用each_with_object
為array1提供一個枚舉數,該枚舉數還使我們的方法鏈可以訪問array2的副本。 然后,Map可以遍歷each_with_object
枚舉器(引用array1),將每個值加載到局部變量v
並將我們的array2復制到局部變量t
。 從那里:
# map the value IF...
# / it exists in and we were able to
# / our array2 copy remove it from our copy
# / | |
map{|v,t| v if (l = t.index v) && t.slice!(l) }.compact
# | \ \ |
# array1 \ \ dump nils
# value array2 \
# copy load index position into temporary variable l
我們遍歷array1的每個值並搜索該值是否存在於array2中(通過t
)。 如果存在,則從數組2的副本中刪除該值的第一次出現,並將該值map
到結果數組。
注意, t.index(v)
t.slice!(t.index(v))
用作短路保護,以防t.slice!(t.index(v))
短路,以防在t
t.slice!(t.index(v))
中不存在該值。 我們還使用一個內聯技巧,將索引值分配給局部變量l
: (l = t.index v)
因此我們可以在后續的布爾檢查中引用l
: t.slice!(l)
。
最后,由於只要array2內不存在array1值,此方法就會映射nil值,因此我們compact
結果以刪除nil。
對於那些好奇的人,這里是到目前為止提出的解決方案的一些基准測試。 首先,這是在樣本陣列上執行100,000次操作的時鍾速度:
Cary: 1.050000 0.010000 1.060000 ( 1.061217)
Cary+: 1.580000 0.010000 1.590000 ( 1.603645)
Cam: 0.550000 0.010000 0.560000 ( 0.552062)
Mudasobwa: 2.540000 0.050000 2.590000 ( 2.610395)
Sergii: 0.660000 0.000000 0.660000 ( 0.665408)
Sahil: 1.750000 0.010000 1.760000 ( 1.769624)
#Tommy: 0.290000 0.000000 0.290000 ( 0.290114)
如果我們擴展測試數組以容納10000個具有高度相交的整數...
array1 = array2 = []
10000.times{ array1 << rand(10) }
10000.times{ array2 << rand(10) }
循環100次后,簡單循環解決方案(Sahil)開始與眾不同。 Cary的解決方案也很好,尤其是在預處理方面:
user system total real
Cary: 1.590000 0.020000 1.610000 ( 1.615798)
Cary+: 0.870000 0.010000 0.880000 ( 0.879331)
Cam: 6.680000 0.090000 6.770000 ( 6.838829)
Mudasobwa: 6.740000 0.080000 6.820000 ( 6.898394)
Sergii: 6.760000 0.100000 6.860000 ( 6.962025)
Sahil: 0.740000 0.030000 0.770000 ( 0.785975)
#Tommy: 0.430000 0.010000 0.440000 ( 0.446482)
對於大小為1/10的數組,其大小為1000個整數,並且相交度較低 ,但是...
array1 = array2 = []
1000.times{ array1 << rand(10000) }
1000.times{ array2 << rand(10000) }
當我們循環10次時,flat_map解決方案會變平……除非我們使用預處理(Cary +):
user system total real
Cary: 135.400000 0.700000 136.100000 (137.123393)
Cary+: 0.270000 0.010000 0.280000 ( 0.268255)
Cam: 0.670000 0.000000 0.670000 ( 0.676438)
Mudasobwa: 0.670000 0.010000 0.680000 ( 0.684088)
Sergii: 0.660000 0.010000 0.670000 ( 0.673881)
Sahil: 1.970000 2.130000 4.100000 ( 4.121759)
#Tommy: 0.050000 0.000000 0.050000 ( 0.045970)
這是基准測試的要點: https : //gist.github.com/camerican/139463b4bd9e0fd89424377931042ce4
array1 = [2,2,2,2,3,3,4,5,6,7,8,9]
array2 = [2,2,2,3,4,4,4,4,8,8,0,0,0]
a1, a2 = array1.dup, array2.dup # we’ll mutate them
loop.with_object([]) do |_, memo|
break memo if a1.empty? || a2.empty?
e = a2.delete_at(a2.index(a1.shift)) rescue nil
memo << e if e
end
#⇒ [2,2,2,3,4,8]
array1 = [2,2,2,2,3,3,4,5,6,7,8,9]
array2 = [2,2,2,3,4,4,4,4,8,8,0,0,0]
獲取樣本數組中每個元素的頻率:
a1_freq=Hash.new(0); a2_freq=Hash.new(0); dup_items=[];
array1.each {|a| a1_freq[a]+=1 }
array2.each {|b| a2_freq[b]+=1 }
最后,比較元素是否存在於另一個數組中。 如果是,則對兩個樣本數組中的公共元素進行最小計數。
a1_freq.each {|k,v| a2_freq[k] ? dup_items+=[k]*[v,a2_freq[k]].min : nil}
#dup_items=> [2, 2, 2, 3, 4, 8]
這有點冗長,但是假設您的意思是值在相同位置:
def combine(array1, array2)
longer_array = array1.length > array2.length ? array1 : array2
intersection = []
count = 0
longer_array.each do |item|
if array1 == longer_array
looped_array = array2
else
looped_array = array1
end
if item == looped_array[count]
intersection.push(item)
end
count +=1
end
print intersection
end
array_1 = [2,2,2,2,3,3,4,5,6,7,8,9]
array_2 = [2,2,2,3,4,4,4,4,8,8,0,0,0]
combine(array_1, array_2)
我只想指出,我不知道如何到達數組3,因為所有三個數組的索引位置3都不相同:
array_1[3] = 2
array_2[3] = 3
array_3[3] = 3
我將嘗試以這種方式達到預期的結果:
array1, array2 = [array1, array2].sort_by(&:size)
arr_copy = array2.dup
array1.each.with_object([]) do |el, arr|
index = arr_copy.find_index(el)
arr << arr_copy.slice!(index) if index
end
# => [2, 2, 2, 3, 4, 8]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.