Kiểm tra xem hai mảng có cùng nội dung (theo bất kỳ thứ tự nào)


90

Tôi đang sử dụng Ruby 1.8.6 với Rails 1.2.3 và cần xác định xem hai mảng có các phần tử giống nhau hay không, bất kể chúng có theo cùng một thứ tự hay không. Một trong các mảng được đảm bảo không chứa các bản sao (mảng còn lại có thể, trong trường hợp đó câu trả lời là không).

Suy nghĩ đầu tiên của tôi là

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

nhưng tôi đã tự hỏi liệu có một cách hiệu quả hơn hoặc thành ngữ để làm điều đó.



Hãy thử array.should = ~ another_array kiểm tra stackoverflow.com/questions/2978922/…
Athena

Bạn có thể đã tránh được nhiều nhầm lẫn bằng cách: 1) nêu rõ liệu các phần tử của mảng có nhất thiết phải sắp xếp được hay không; và 2) cung cấp một ví dụ đơn giản để làm rõ những gì bạn có nghĩa là bởi, "cho dù hai mảng có những yếu tố tương tự" (ví dụ, làm [1,2][2,1,1]có những yếu tố giống nhau)?
Cary Swoveland

Ruby 2.6 đã giới thiệu differencecung cấp một giải pháp rất nhanh và rất dễ đọc. Thêm thông tin ở đây.
SRack

Câu trả lời:


140

Điều này không yêu cầu chuyển đổi để đặt:

a.sort == b.sort

Không có chuyển đổi? .uniq.sortSau đó là gì? Bên cạnh đó uniqcũng tương tự như to_settrong nội bộ cộng thêm.to_a.sort
Victor Moroz

Chấp nhận điều này vì nó gần nhất với những gì tôi đã sử dụng, mặc dù không có uniqs. Trên thực tế, tôi đã kết thúc việc tạo một trong các mảng Range#to_a, vì vậy tôi chỉ phải tạo sortmảng còn lại.
Taymon

11
Điều này sẽ không hoạt động nếu mảng chứa các phần tử không thể được sắp xếp đơn giản (ví dụ: một mảng các băm). Giải pháp của sahil dhankhar dường như là một giải pháp tổng quát hơn.
brad

40

cho hai mảng A và B: A và B có nội dung giống nhau nếu: (A-B).blank? and (B-A).blank?

hoặc bạn chỉ có thể kiểm tra: ((A-B) + (B-A)).blank?

Cũng theo đề xuất của @ cort3z, giải pháp này als0 hoạt động cho các mảng đa hình tức là

 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` 

::::::::::: BIÊN TẬP :::::::::::::

Như được đề xuất trong các nhận xét, giải pháp trên không thành công đối với các bản sao. Mặc dù theo câu hỏi, thậm chí không bắt buộc vì người hỏi không quan tâm đến các bản sao (anh ta đang chuyển đổi các mảng của mình để đặt trước khi kiểm tra và mặt nạ trùng lặp và ngay cả khi bạn nhìn câu trả lời được chấp nhận mà anh ta đang sử dụng toán tử .uniq trước khi kiểm tra và điều đó cũng che dấu các bản sao.). Nhưng vẫn còn nếu bạn quan tâm đến các bản sao, Chỉ cần thêm một số lượng kiểm tra sẽ sửa lỗi tương tự (vì theo câu hỏi, chỉ một mảng có thể chứa các bản sao). Vì vậy, giải pháp cuối cùng sẽ là: A.size == B.size and ((A-B) + (B-A)).blank?


Điều này sẽ không thành công nếu một trong hai mảng chứa các bản sao. Ví dụ: nếu A=[1]B=[1,1], cả hai (A-B)(B-A)sẽ trả về trống. Xem Tài liệu mảng .
jtpereyda

@dafrazzman hoàn toàn đồng ý với bạn. Tôi đã sửa đổi câu trả lời của mình để kết hợp phản hồi của bạn. Nhưng nếu bạn đã xem kỹ câu hỏi (hoặc câu trả lời được chấp nhận), người hỏi đang sử dụng: a.to_set == b.to_set và câu trả lời được chấp nhận đang sử dụng a.uniq.sort == b.uniq.sortvà cả hai đều cho kết quả chính xác như ((A-B) + (B-A)).blank?đối với A = [1] và B = [1,1] đồng ý không? Vì anh ấy chỉ yêu cầu cải thiện giải pháp ban đầu của anh ấy, giải pháp ban đầu của tôi vẫn hoạt động :). đồng ý?
Sahil Dhankhar

1
Giải pháp này khá hay vì nó xử lý các đối tượng thuộc nhiều loại. Giả sử bạn có A = [123, "test", [], some_object, nil]B = A#because I am lazy, sau đó A.uniq.sortsẽ xuất hiện lỗi (so sánh chuỗi và Mảng không thành công).
Automatico

Đây sẽ là O (n) vì nó phụ thuộc vào kích thước mảng? (tuyến tính)
user3007294 27/12/16

1
Nó sẽ không hoạt động nếu các mảng có cùng kích thước nhưng các phần tử lặp lại không giống nhau. Ví dụ A = [1, 1, 2]B = [1, 2, 2]
Boudi

23

So sánh tốc độ

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

btw thứ tự của elemetns không ảnh hưởng đến sort's tốc độ
Morozov

Làm tôi ngạc nhiên ... Tôi mong đợi so sánh từng bộ sẽ tốt hơn tất cả những người khác do độ phức tạp thời gian tra cứu O (n) bộ. Vì vậy, bất kỳ sắp xếp được triển khai tốt nào sẽ yêu cầu O (n logn). Trong khi việc truyền đến các tập hợp và tìm kiếm các giá trị nói chung sẽ thực hiện trong O (n) thời gian.
Oleg Afanasyev

1
Tôi mong đợi to_setđể bắt đầu đạt vượt với mảng đủ lớn, nơi O (n logn) sẽ bắt đầu mattering hơn các nỗ lực cần thiết để mảng chuyển đổi sang bộ
Andrius Chamentauskas

1
Điều này rất hữu ích, nhưng không thực sự là một câu trả lời? Có lẽ tốt hơn nên thêm điều này vào một giải pháp hiện có?
SRack

18

Ruby 2.6+

Ruby được giới thiệu differencetrong 2.6.

Điều này đưa ra một giải pháp rất nhanh, rất dễ đọc ở đây, như sau:

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

Chạy các điểm chuẩn:

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

Hy vọng rằng sẽ giúp một ai đó!


2
trong trường hợp này sự khác biệt bị phá vỡ a = [1, 2, 3] b = [1, 2, 3, 4, 5] a.difference (b) .any? => false
error-404


8

Nếu bạn mong đợi [:a, :b] != [:a, :a, :b] to_setkhông hoạt động. Bạn có thể sử dụng tần số thay thế:

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

tại sao không chỉ a.sort == b.sortkhi anh ấy quan tâm đến tần suất?
fl00r

4
@ fl00r Điều gì sẽ xảy ra nếu các mục không thể so sánh được? ["", :b].frequency == [:b, ""].frequency #=> true
Victor Moroz

2
bạn cũng có thể làm điều gì đó chức năng nhưa.group_by{|i| i} == b.group_by{|i| i}
fl00r

7

Nếu bạn biết các mảng có độ dài bằng nhau và không có mảng nào chứa các bản sao thì điều này cũng hoạt động:

( array1 & array2 ) == array1

Giải thích: các &nhà điều hành trong trường hợp này trả về một bản sao của sans a1 không tìm thấy bất kỳ mục trong a2, mà là giống như a1 gốc khi và chỉ khi cả hai mảng có các nội dung tương tự với không có bản sao.

Analyis: Cho rằng thứ tự không thay đổi, tôi đoán điều này được thực hiện dưới dạng lặp lại kép một cách nhất quán O(n*n), đáng chú ý là tồi tệ hơn đối với các mảng lớn hơn so với a1.sort == a2.sortđiều này sẽ thực hiện với trường hợp xấu nhất O(n*logn).


2
Không hoạt động luôn: a1 = [1,2,3], a2 = [2, 1, 3] a1 && a2lợi nhuận [2,1,3]đối với tôi đó là không bằnga1
Kalyan Raghu

@Kaylan, không phải ý bạn là nó chỉ hoạt động khi a1==a2nào? Nó có thể hoạt động nếu array1ở bên phải của dấu đẳng thức được thay thế bằng array2, nhưng tôi nghi ngờ rằng thứ tự của các phần tử được trả về &được đảm bảo.
Cary Swoveland

1
@KalyanRaghu &là một toán tử giao nhau được đặt cho các mảng, &&là logic AND - chúng rất khác nhau!
Kimball

2

Một cách tiếp cận là lặp qua mảng không có bản sao

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

Điều này trả về một mảng trues. Nếu bất kỳ sai nào xuất hiện, thì bên ngoài include?sẽ trả về true. Vì vậy, bạn phải đảo ngược toàn bộ để xác định xem nó có trùng khớp hay không.


@Victor Moroz, bạn nói đúng, và tần suất đếm đơn giản sẽ là O (n).
Ron

2

kết hợp &sizecó thể quá nhanh.

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.