Cách xác định nếu một mảng chứa tất cả các thành phần của mảng khác


179

Được:

a1 = [5, 1, 6, 14, 2, 8]

Tôi muốn xác định xem nó có chứa tất cả các yếu tố của:

a2 = [2, 6, 15]

Trong trường hợp này kết quả là false.

Có bất kỳ phương thức Ruby / Rails tích hợp nào để xác định sự bao gồm mảng như vậy không?

Một cách để thực hiện điều này là:

a2.index{ |x| !a1.include?(x) }.nil?

Có cách nào tốt hơn, dễ đọc hơn không?


Câu trả lời được chấp nhận (phép trừ mảng) là giải pháp nhanh nhất. Tôi đã điểm chuẩn tất cả trong số họ ở đây: gist.github.com/bbugh/cbbde8b48cbb16286044f6893e1f2e5f
brainbag

Câu trả lời:


309
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]

a - b
=> [5, 1, 14, 8]

b - a
=> [15]

(b - a).empty?
=> false

61
Đây là con đường để đi. Nó có thể chỉ được rút ngắn một chút thành(a2-a1).empty?
Holger Chỉ cần

9
Điều này chỉ hoạt động cho các mảng được đặt, không phải cho các mảng có trùng lặp
Chris

3
@Chris - Bạn có thể thử sử dụng Array # uniq cho điều đó. Với ví dụ của Holger Just, nó sẽ là(a2.uniq - a1.uniq).empty?
Nick

stackoverflow.com/questions/13553822/ cấp là những gì tôi muốn nói. Mảng # duy nhất rõ ràng sẽ thất bại điều đó.
Chris

81

Có lẽ điều này dễ đọc hơn:

a2.all? { |e| a1.include?(e) }

Bạn cũng có thể sử dụng giao điểm mảng:

(a1 & a2).size == a1.size

Lưu ý rằng sizeđược sử dụng ở đây chỉ để tăng tốc, bạn cũng có thể làm (chậm hơn):

(a1 & a2) == a1

Nhưng tôi đoán đầu tiên là dễ đọc hơn. Cả 3 đều là ruby ​​trơn (không phải đường ray).


Nếu sử dụng định nghĩa a1 và a2 của OP và a1 "chứa tất cả các phần tử của" a2, tôi nghĩ rằng đây phải là _ (a1 & a2) .size == a2.size _ vì a2 là mảng nhỏ hơn, nên có tất cả các phần tử được bao gồm trong mảng lớn hơn (để có được 'true') - do đó giao điểm của hai mảng phải có cùng độ dài với mảng nhỏ hơn nếu tất cả các phần tử trong nó có mặt trong phần lớn hơn.
JosephK

57

Điều này có thể đạt được bằng cách làm

(a2 & a1) == a2

Điều này tạo ra giao điểm của cả hai mảng, trả về tất cả các phần tử a2cũng nằm trong đó a1. Nếu kết quả giống như a2, bạn có thể chắc chắn rằng bạn có tất cả các yếu tố được bao gồm a1.

Cách tiếp cận này chỉ hoạt động nếu tất cả các yếu tố a2khác nhau ở nơi đầu tiên. Nếu có đôi, cách tiếp cận này thất bại. Cái từ Tempose vẫn hoạt động sau đó, vì vậy tôi hết lòng khuyên bạn nên tiếp cận (cũng có thể nhanh hơn).


2
sử dụng lengthphương pháp này sẽ hoạt động tốt hơn nhiều
Pablo Fernandez

3
Điều này sẽ không hoạt động nếu bộ giao cắt có cùng các yếu tố theo thứ tự khác nhau. Tôi phát hiện ra điều này một cách khó khăn trong khi cố gắng trả lời câu hỏi này: stackoverflow.com/questions/12062970/ rèn sau đó nhận ra nhiều người thông minh đã làm điều đó ở đây rồi!
CubaLibre

1
@CubaLibre Thú vị. Bạn có một số dữ liệu thử nghiệm để tái tạo điều này? Trong các thử nghiệm của tôi, dường như mảng kết quả vẫn giữ thứ tự các phần tử từ mảng đầu tiên (do đó chỉnh sửa gần đây nhất cho câu trả lời của tôi). Tuy nhiên, nếu đây thực sự không phải là trường hợp, tôi muốn tìm hiểu.
Holger Chỉ cần

@HolgerJust tôi đã phạm sai lầm khi làm (a1 & a2) thay vì (a2 & a1), đó là lý do tại sao tôi thấy lỗi. Bạn đã đúng và giữ lại thứ tự từ mảng đầu tiên.
CubaLibre

10

Nếu không có phần tử trùng lặp hoặc bạn không quan tâm đến chúng, thì bạn có thể sử dụng lớp Set :

a1 = Set.new [5, 1, 6, 14, 2, 8]
a2 = Set.new [2, 6, 15]
a1.subset?(a2)
=> false

Đằng sau hậu trường này

all? { |o| set.include?(o) }

1

Bạn có thể vá lỗi lớp Array:

class Array
    def contains_all?(ary)
        ary.uniq.all? { |x| count(x) >= ary.count(x) }
    end
end

kiểm tra

irb(main):131:0> %w[a b c c].contains_all? %w[a b c]
=> true
irb(main):132:0> %w[a b c c].contains_all? %w[a b c c]
=> true
irb(main):133:0> %w[a b c c].contains_all? %w[a b c c c]
=> false
irb(main):134:0> %w[a b c c].contains_all? %w[a]
=> true
irb(main):135:0> %w[a b c c].contains_all? %w[x]
=> false
irb(main):136:0> %w[a b c c].contains_all? %w[]
=> true
irb(main):137:0> %w[a b c d].contains_all? %w[d c h]
=> false
irb(main):138:0> %w[a b c d].contains_all? %w[d b c]
=> true

Tất nhiên phương thức có thể được viết như một phương thức đơn tiêu chuẩn, vd

def contains_all?(a,b)
    b.uniq.all? { |x| a.count(x) >= b.count(x) }
end

và bạn có thể gọi nó như

contains_all?(%w[a b c c], %w[c c c])

Thật vậy, sau khi định hình, phiên bản sau nhanh hơn nhiều và mã ngắn hơn.

def contains_all?(a,b)
    b.all? { |x| a.count(x) >= b.count(x) }
end

0

Tùy thuộc vào mức độ lớn của mảng của bạn, bạn có thể xem xét một thuật toán hiệu quả O (n log n)

def equal_a(a1, a2)
  a1sorted = a1.sort
  a2sorted = a2.sort
  return false if a1.length != a2.length
  0.upto(a1.length - 1) do 
    |i| return false if a1sorted[i] != a2sorted[i]
  end
end

Sắp xếp chi phí O (n log n) và kiểm tra mỗi cặp chi phí O (n) do đó thuật toán này là O (n log n). Các thuật toán khác không thể nhanh hơn (không có triệu chứng) bằng cách sử dụng các mảng chưa được sắp xếp.


Bạn có thể làm điều đó trong O (n) với cách sắp xếp đếm.
klochner

Không, bạn không thể. Sắp xếp đếm sử dụng một vũ trụ hạn chế và Ruby không có giới hạn về cách số lượng lớn có thể nhận được.
ayckoster

Bạn có thể, bởi vì bạn thực sự không phải sắp xếp các mục - bạn chỉ cần một mục ánh xạ băm -> đếm cho cả hai mảng, sau đó lặp lại các khóa và so sánh số lượng.
klochner

Bạn có chắc chắn rằng Array # sort sử dụng sắp xếp hợp nhất?
Symate Nate

0

Hầu hết các câu trả lời dựa trên (a1 - a2) hoặc (a1 & a2) sẽ không hoạt động nếu có các phần tử trùng lặp trong một trong hai mảng. Tôi đến đây để tìm cách để xem liệu tất cả các chữ cái của một từ (được chia thành một mảng) là một phần của một tập hợp các chữ cái (ví dụ như viết nguệch ngoạc). Không có câu trả lời nào trong số này có hiệu quả, nhưng câu trả lời này có:

def contains_all?(a1, a2)
  try = a1.chars.all? do |letter|
    a1.count(letter) <= a2.count(letter)
  end
  return try
end
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.