İki dizinin aynı içeriğe sahip olup olmadığını kontrol edin (herhangi bir sırada)


91

Ruby 1.8.6'yı Rails 1.2.3 ile kullanıyorum ve iki dizinin aynı sırada olup olmadıklarına bakılmaksızın aynı elemanlara sahip olup olmadığını belirlemem gerekiyor. Dizilerden birinin yineleme içermemesi garanti edilir (diğeri olabilir, bu durumda yanıt hayırdır).

İlk düşüncem

require 'set'
a.to_set == b.to_set

ama bunu yapmanın daha verimli veya deyimsel bir yolu olup olmadığını merak ediyordum.



Array.should = ~ another_array kontrol stackoverflow.com/questions/2978922/…
Athena

Şunları yaparak birçok karışıklıktan kurtulabilirsiniz: 1) dizilerin öğelerinin zorunlu olarak sıralanabilir olup olmadığını belirterek; ve 2) (örneğin do "iki diziler aynı unsurları olup olmadığını", derken ne demek istediğine açıklık getirmek basit bir örnek sunmak [1,2]ve [2,1,1]? aynı unsurları var)
Cary Swoveland

Ruby 2.6, differencehem çok hızlı hem de çok okunabilir bir çözüm sunan tanıttı . Daha fazla bilgi burada.
SRack

Yanıtlar:


144

Bu, aşağıdakilerin ayarlanmasını gerektirmez:

a.sort == b.sort

Dönüşüm yok mu? Öyleyse nedir .uniq.sort? Ayrıca dahili olarak ve ek uniqolarak benzerto_set.to_a.sort
Victor Moroz

Bunu, uniqs olmadan da kullandığım şeye en yakın olduğu için kabul ediyorum . Aslında dizilerden birini oluşturmaya son verdim Range#to_a, bu yüzden sadece sortdiğerini yapmak zorunda kaldım .
Taymon

11
Dizi, basitçe sıralanamayan öğeler içeriyorsa (örneğin bir karma dizisi) bu işe yaramayacaktır. sahil dhankhar'ın çözümü daha genel bir çözüm gibi görünüyor.
brad

40

iki dizi A ve B için: A ve B aynı içeriğe sahipse: (A-B).blank? and (B-A).blank?

veya sadece kontrol edebilirsiniz: ((A-B) + (B-A)).blank?

Ayrıca @ cort3z tarafından önerildiği gibi, bu çözüm als0 polimorfik diziler için çalışır, yani

 A = [1 , "string", [1,2,3]]
 B = [[1,2,3] , "string", 1]
 (A-B).blank? and (B-A).blank? => true
 # while A.uniq.sort == B.uniq.sort will throw error `ArgumentError: comparison of Fixnum with String failed` 

::::::::::: DÜZENLE :::::::::::::

Yorumlarda önerildiği gibi, yukarıdaki çözüm kopyalar için başarısız olur. Soruya göre, soruyu soran kişi kopyalarla ilgilenmediğinden bile gerekli olmasa da (dizilerini kontrol etmeden önce ayarlanacak şekilde dönüştürüyor ve kopyaları maskeliyor ve hatta baksanız bile kabul edilen yanıtı kontrol etmeden önce bir .uniq operatörü kullanıyor ve bu da kopyaları maskeliyor.). Ancak yine de, yinelenenler ilginizi çekiyorsa, yalnızca bir sayım kontrolü eklemek aynı şeyi çözecektir (soruya göre yalnızca bir dizi yinelenen öğeler içerebilir). Dolayısıyla nihai çözüm şu şekilde olacaktır: A.size == B.size and ((A-B) + (B-A)).blank?


Her iki dizi de kopya içeriyorsa bu başarısız olur. Örneğin, eğer A=[1]ve B=[1,1], her ikisi (A-B)ve (B-A)boş dönecektir. Dizi Belgelerine bakın .
jtpereyda

@dafrazzman sana tamamen katılıyorum. Geri bildiriminizi dahil etmek için cevabımı değiştirdim. Ancak soruya (veya kabul edilen cevaba) yakından bakarsanız, soran: a.to_set == b.to_set kullanıyor a.uniq.sort == b.uniq.sortve kabul edilen cevap kullanıyor ve her ikisi de ((A-B) + (B-A)).blank?A = [1] ile aynı sonucu veriyor ve B = [1,1] katılıyor mu? Sadece orijinal çözümünün üzerinde bir iyileştirme istediğinden, orijinal çözümüm hala çalışıyor :). Katılıyorum?
Sahil Dhankhar

1
Bu çözüm, birden çok türdeki nesneleri işlediği için oldukça güzel. Diyelim ki var A = [123, "test", [], some_object, nil]ve B = A#because I am lazysonra A.uniq.sorthata atacak (dizge ve Dizi karşılaştırması başarısız oldu).
Automatico

Dizi boyutuna bağlı olduğu için bu O (n) olur mu? (doğrusal)
user3007294

1
Diziler aynı boyuta sahipse ancak yinelenen öğeler aynı değilse işe yaramaz. Örneğin A = [1, 1, 2]veB = [1, 2, 2]
Boudi

23

Hız karşılaştırmaları

require 'benchmark/ips'
require 'set'

a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3, 4, 5, 6]

Benchmark.ips do |x|
  x.report('sort')   { a.sort == b.sort }  
  x.report('sort!')  { a.sort! == b.sort! }  
  x.report('to_set') { a.to_set == b.to_set }  
  x.report('minus')  { ((a - b) + (b - a)).empty? }  
end  

Warming up --------------------------------------
            sort    88.338k i/100ms
           sort!   118.207k i/100ms
          to_set    19.339k i/100ms
           minus    67.971k i/100ms
Calculating -------------------------------------
            sort      1.062M (± 0.9%) i/s -      5.389M in   5.075109s
           sort!      1.542M (± 1.2%) i/s -      7.802M in   5.061364s
          to_set    200.302k (± 2.1%) i/s -      1.006M in   5.022793s
           minus    783.106k (± 1.5%) i/s -      3.942M in   5.035311s

elemetns btw sıralaması sorthızı etkilemiyor
Morozov

Beni şaşırttı ... Set arama O (n) zaman karmaşıklığı nedeniyle set bazında karşılaştırmanın diğerlerinden daha iyi performans göstermesini bekliyordum. Böylece, iyi uygulanan herhangi bir sıralama O (n logn) gerektirir. Oysa kümelere çevrim yapmak ve değerlere bakmak genel olarak onu O (n) zamanında yapar.
Oleg Afanasyev

1
to_setO (n logn) 'nin diziyi sete dönüştürmek için gereken çabadan daha önemli olmaya başlayacağı yeterince büyük dizilerle daha iyi performans göstermeye başlamayı bekliyorum
Andrius Chamentauskas

1
Bu yararlı, ancak kendi başına bir cevap değil mi? Belki de bunu mevcut bir çözüme eklemek daha iyidir?
SRack

21

Ruby 2.6+

Ruby difference2.6'da tanıtıldı .

Bu, burada aşağıdaki gibi çok hızlı, çok okunabilir bir çözüm sağlar:

a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3, 4, 5, 6]

a.difference(b).any?
# => false
a.difference(b.reverse).any?
# => false

a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3]
a.difference(b).any?
# => true

Kıyaslamaları çalıştırmak:

a = Array.new(1000) { rand(100) }
b = Array.new(1000) { rand(100) }

Benchmark.ips do |x|
  x.report('sort')   { a.sort == b.sort }  
  x.report('sort!')  { a.sort! == b.sort! }  
  x.report('to_set') { a.to_set == b.to_set }  
  x.report('minus')  { ((a - b) + (b - a)).empty? }  
  x.report('difference') { a.difference(b).any? }
end

      sort     13.908k (± 2.6%) i/s -     69.513k in   5.001443s
     sort!     14.656k (± 3.0%) i/s -     73.736k in   5.035744s
    to_set     5.125k  (± 2.9%) i/s -     26.023k in   5.082083s
     minus     16.398k (± 2.2%) i/s -     83.181k in   5.074938s
difference     27.839k (± 5.2%) i/s -    141.048k in   5.080706s

Umarım bu birine yardımcı olur!


5
bu durumda fark kırılıyor a = [1, 2, 3] b = [1, 2, 3, 4, 5] a. fark (b). herhangi? => false
hata-404


8

Eğer umarsan [:a, :b] != [:a, :a, :b] to_setişe yaramaz. Bunun yerine frekansı kullanabilirsiniz:

class Array
  def frequency
    p = Hash.new(0)
    each{ |v| p[v] += 1 }
    p
  end
end

[:a, :b].frequency == [:a, :a, :b].frequency #=> false
[:a, :b].frequency == [:b, :a].frequency #=> true

neden sadece a.sort == b.sortfrekansı umursuyorsa?
fl00r

4
@ fl00r Ya öğeler karşılaştırılabilir değilse? ["", :b].frequency == [:b, ""].frequency #=> true
Victor Moroz

2
Ayrıca olarak fonksiyonel bir şey yapabilirsiniza.group_by{|i| i} == b.group_by{|i| i}
fl00r

7

Dizilerin eşit uzunlukta olduğunu ve hiçbir dizinin yineleme içermediğini biliyorsanız, bu da işe yarar:

( array1 & array2 ) == array1

Açıklama:& Bu durumda işletmeci hem diziler hiçbir tekrarlar da aynı içeriğe sahip IFF orijinal a1 aynıdır a1 sans bir kopyasını herhangi ürün a2 bulunmayan döndürür.

Analyis: Sıranın değişmediği göz önüne alındığında, bunun çift yineleme olarak uygulandığını tahmin ediyorum, bu kadar tutarlı bir şekilde O(n*n), özellikle büyük diziler için a1.sort == a2.sorten kötü durumda performans göstermesi gerekenden daha kötü O(n*logn).


2
Her zaman işe yaramıyor: benim için eşit olmayan a1 = [1,2,3], a2 = [2, 1, 3] a1 && a2geri dönüş[2,1,3]a1
Kalyan Raghu

@Kaylan, sadece ne zaman işe yaradığını a1==a2mı söylüyorsun ? array1Eşitliğin sağ tarafı ile değiştirilirse işe yarayabilir array2, ancak geri dönen öğelerin sırasının &garanti edildiğinden şüpheliyim .
Cary Swoveland

1
@KalyanRaghu &, diziler için küme kesişim operatörüdür &&, mantıksaldır VE - çok farklıdırlar!
Kimball

3

birleştirir &ve sizehızlı da olabilir.

require 'benchmark/ips'
require 'set'

Benchmark.ips do |x|
  x.report('sort')   { a.sort == b.sort }  
  x.report('sort!')  { a.sort! == b.sort! }  
  x.report('to_set') { a.to_set == b.to_set }  
  x.report('minus')  { ((a - b) + (b - a)).empty? }
  x.report('&.size') { a.size == b.size && (a & b).size == a.size }  
end  

Calculating -------------------------------------
                sort    896.094k (±11.4%) i/s -      4.458M in   5.056163s
               sort!      1.237M (± 4.5%) i/s -      6.261M in   5.071796s
              to_set    224.564k (± 6.3%) i/s -      1.132M in   5.064753s
               minus      2.230M (± 7.0%) i/s -     11.171M in   5.038655s
              &.size      2.829M (± 5.4%) i/s -     14.125M in   5.010414s

2

Bir yaklaşım, kopya olmadan dizi üzerinde yineleme yapmaktır.

# assume array a has no duplicates and you want to compare to b
!a.map { |n| b.include?(n) }.include?(false)

Bu bir dizi doğru döndürür. Herhangi bir yanlış gelirse, dış kısım include?doğruya dönecektir. Bu nedenle, bir eşleşme olup olmadığını belirlemek için her şeyi tersine çevirmelisiniz.


@Victor Moroz, haklısınız ve bir sıklık sayısı basitçe O (n) olacaktır.
Ron
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.