Como determinar se uma matriz contém todos os elementos de outra matriz

179

Dado:

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

Gostaria de determinar se ele contém todos os elementos de:

a2 = [2, 6, 15]

Nesse caso, o resultado é false.

Existe algum método Ruby / Rails interno para identificar essa inclusão de matriz?

Uma maneira de implementar isso é:

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

Existe uma maneira melhor e mais legível?

Misha Moroshko
fonte
A resposta aceita (subtração de matriz) é a solução mais rápida. Eu aferido todos eles aqui: gist.github.com/bbugh/cbbde8b48cbb16286044f6893e1f2e5f
brainbag

Respostas:

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
Geo
fonte
61
Este é o caminho a percorrer. Pode ser um pouco reduzido para(a2-a1).empty?
Holger Apenas
9
Isso só funciona para matrizes que são conjuntos, e não para arrays com duplicatas
Chris
3
@ Chris - Você pode tentar usar a Matriz # uniq para isso. Com o exemplo de Holger Just, seria(a2.uniq - a1.uniq).empty?
Nick
stackoverflow.com/questions/13553822/… é o que eu quis dizer. A matriz # unique falhará explicitamente nisso.
31412 Chris
81

Talvez seja mais fácil ler isso:

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

Você também pode usar a interseção da matriz:

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

Observe que sizeé usado aqui apenas para velocidade, você também pode fazer (mais devagar):

(a1 & a2) == a1

Mas acho que o primeiro é mais legível. Estes 3 são simples rubi (não trilhos).

Pablo Fernandez
fonte
Se estiver usando a definição de OP de a1 e a2 e a1 "contendo todos os elementos de" a2, acho que deve ser _ (a1 & a2) .size == a2.size _, já que a2 é a matriz menor, que deve ter todos elementos incluídos na matriz maior (para obter 'true') - portanto, a interseção das duas matrizes deve ter o mesmo comprimento que a matriz menor, se todos os elementos estiverem presentes na matriz maior.
JosephK
57

Isso pode ser alcançado fazendo

(a2 & a1) == a2

Isso cria a interseção de ambas as matrizes, retornando todos os elementos dos a2quais também estão a1. Se o resultado for o mesmo a2, você pode ter certeza de que todos os elementos estão incluídos a1.

Essa abordagem só funciona se todos os elementos em a2forem diferentes um do outro em primeiro lugar. Se houver duplas, essa abordagem falhará. O de Tempos ainda funciona, então eu recomendo sinceramente sua abordagem (também é provavelmente mais rápida).

Holger Just
fonte
2
usando o lengthmétodo terá um desempenho muito melhor
Pablo Fernandez
3
Isso não funcionará se o conjunto de interseção tiver os mesmos elementos em uma ordem diferente. Descobri isso da maneira mais difícil, ao tentar responder a essa pergunta: stackoverflow.com/questions/12062970/… depois percebi que muitas pessoas inteligentes já haviam feito isso aqui!
cubalibre
1
@CubaLibre Interesting. Você tem alguns dados de teste para reproduzir isso? Nos meus testes, parecia que a matriz resultante retém a ordem dos elementos da primeira matriz (daí a minha edição mais recente na minha resposta). No entanto, se esse não for realmente o caso, eu gostaria de aprender.
precisa
@HolgerJust Eu cometi o erro de fazer (a1 e a2) em vez de (a2 e a1), e é por isso que eu estava vendo o erro. Você está certo sobre e reter o pedido da primeira matriz.
cubalibre
10

Se não houver elementos duplicados ou você não se importar com eles, poderá usar a classe Set :

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

Nos bastidores isso usa

all? { |o| set.include?(o) }
Confusão
fonte
1

Você pode aplicar um patch de macaco à classe Array:

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

teste

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

Obviamente, o método pode ser escrito como um método padrão sozinho, por exemplo

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

e você pode invocá-lo como

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

De fato, após a criação de perfil, a versão a seguir é muito mais rápida e o código é mais curto.

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

Dependendo do tamanho de suas matrizes, você pode considerar um algoritmo eficiente 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

A classificação dos custos O (n log n) e a verificação de cada par custa O (n), portanto, esse algoritmo é O (n log n). Os outros algoritmos não podem ser mais rápidos (assintoticamente) usando matrizes não classificadas.

ayckoster
fonte
Você pode fazer isso em O (n) com uma classificação de contagem.
precisa saber é o seguinte
Não, você não pode. A classificação de contagem usa um universo limitado e Ruby não tem uma limitação sobre o tamanho dos números.
ayckoster 12/09
Você pode, porque na verdade não precisa classificar os itens - você só precisa de um item de mapeamento de hash -> contar para as duas matrizes, iterar sobre as chaves e comparar as contagens.
klochner
Tem certeza de que a matriz # # usa a classificação de mesclagem?
Nate Symer
0

A maioria das respostas com base em (a1 - a2) ou (a1 e a2) não funcionaria se houver elementos duplicados em qualquer matriz. Cheguei aqui procurando uma maneira de ver se todas as letras de uma palavra (divididas em uma matriz) faziam parte de um conjunto de letras (por exemplo, scrabble). Nenhuma dessas respostas funcionou, mas esta funciona:

def contains_all?(a1, a2)
  try = a1.chars.all? do |letter|
    a1.count(letter) <= a2.count(letter)
  end
  return try
end
Charles Breton
fonte