2010-04-01 22 views
20

tengo esta matriz, por ejemplo (el tamaño es variable):Buscar cadena más común en una matriz

x = ["1.111", "1.122", "1.250", "1.111"] 

y tengo que encontrar el valor más común, conviene ("1.111" en este caso).

¿Hay alguna manera fácil de hacerlo?

¡Tks por adelantado!


editar # 1: Gracias a todos por las respuestas!


editar # 2: he cambiado de respuesta aceptada en base a información de Z.E.D.. ¡Gracias a todos de nuevo!

Respuesta

43

Rubí < 2,2

#!/usr/bin/ruby1.8 

def most_common_value(a) 
    a.group_by do |e| 
    e 
    end.values.max_by(&:size).first 
end 

x = ["1.111", "1.122", "1.250", "1.111"] 
p most_common_value(x) # => "1.111" 

Nota: Enumberable.max_by es nuevo con Ruby 1.9, pero se ha portado a 1.8.7

Rubí> = 2.2

Rubí 2.2 introduce el método Object#itself , con el que podemos hacer que el código sea más conciso:

def most_common_value(a) 
    a.group_by(&:itself).values.max_by(&:size).first 
end 

como un parche mono

O como Enumerable#mode:

Enumerable.class_eval do 
    def mode 
    group_by do |e| 
     e 
    end.values.max_by(&:size).first 
    end 
end 

["1.111", "1.122", "1.250", "1.111"].mode 
# => "1.111" 
+0

Estoy impresionado con la velocidad de la manera habitual en que haré esto. Buen trabajo. –

+0

@Wayne Conrad, solución uber. +1 –

+1

Aquí hay una versión más corta: x.group_by {| e | e} .values.max_by (&: size) .first # => "1.111" convirtiéndolo en un método, si lo desea, se deja como un ejercicio para el lector ;-) –

4

Puede ordenar la matriz y luego recorrerla una vez. En el bucle solo haga un seguimiento del elemento actual y la cantidad de veces que se ve. Una vez que la lista finaliza o el artículo cambia, configure max_count == count si count > max_count. Y, por supuesto, realizar un seguimiento de qué elemento tiene el max_count.

2

Puede crear un hashmap que almacene los elementos de la matriz como claves, siendo sus valores el número de veces que ese elemento aparece en la matriz.

Pseudo Código:

["1.111", "1.122", "1.250", "1.111"].each { |num| 
    count=your_hash_map.get(num) 
    if(item==nil) 
    hashmap.put(num,1) 
    else 
    hashmap.put(num,count+1) 
} 

Como ya se ha mencionado, la clasificación podría ser más rápido.

+0

¿Por qué la clasificación sería más rápida? La ordenación es O (n log n) en el mejor de los casos, mientras que esta es O (n) – Pyrolistical

+0

La corrección, la clasificación basada en la comparación es O (n log n). Hay géneros lineales, como sort sort o radix sort. EDITAR: por lo general, debe tener ciertos tipos de datos para ordenar por cubo o ordenar por radix para que sean realmente más eficientes que los géneros de comparación. Lo que recuperan a tiempo, por lo general, engullen en el espacio. FTR, el pseudo código anterior es clasificación de cubo. – saramah

2

Uso de la función valor predeterminado de hashes:

>> x = ["1.111", "1.122", "1.250", "1.111"] 
>> h = Hash.new(0) 
>> x.each{|i| h[i] += 1 } 
>> h.max{|a,b| a[1] <=> b[1] } 
["1.111", 2] 
+0

Esto fue seleccionado como la respuesta, pero mire los resultados del índice de referencia que tengo, que se muestran a continuación. –

+0

¿No sería 'nuevo. (0)' el resultado del mismo objeto para cada elemento hash? 'Hash.new {| h, k | h [k] = 0} 'en su lugar? – karatedog

5

una pasada por el hash para acumular los cargos. Use .max() para encontrar la entrada hash con el valor más grande.

 
#!/usr/bin/ruby 

a = Hash.new(0) 
["1.111", "1.122", "1.250", "1.111"].each { |num| 
    a[num] += 1 
} 

a.max{ |a,b| a[1] <=> b[1] } # => ["1.111", 2] 

o, rodar todo en una sola línea:

 
ary.inject(Hash.new(0)){ |h,i| h[i] += 1; h }.max{ |a,b| a[1] <=> b[1] } # => ["1.111", 2] 

Si sólo desea añadir el artículo devuelto.primero():

 
ary.inject(Hash.new(0)){ |h,i| h[i] += 1; h }.max{ |a,b| a[1] <=> b[1] }.first # => "1.111" 

La primera muestra que utiliza es la forma en que se llevaría a cabo por lo general en Perl. El segundo es más Ruby-ish. Ambos funcionan con versiones anteriores de Ruby. Quería compararlos, además de ver cómo la solución de Wayne podría acelerar las cosas, así que probé con referencia:

 
#!/usr/bin/env ruby 

require 'benchmark' 

ary = ["1.111", "1.122", "1.250", "1.111"] * 1000 

def most_common_value(a) 
    a.group_by { |e| e }.values.max_by { |values| values.size }.first 
end 

n = 1000 
Benchmark.bm(20) do |x| 
    x.report("Hash.new(0)") do 
    n.times do 
     a = Hash.new(0) 
     ary.each { |num| a[num] += 1 } 
     a.max{ |a,b| a[1] <=> b[1] }.first 
    end 
    end 

    x.report("inject:") do 
    n.times do 
     ary.inject(Hash.new(0)){ |h,i| h[i] += 1; h }.max{ |a,b| a[1] <=> b[1] }.first 
    end 
    end 

    x.report("most_common_value():") do 
    n.times do 
     most_common_value(ary) 
    end 
    end 
end 

aquí está el resultado:

 
          user  system  total  real 
Hash.new(0)   2.150000 0.000000 2.150000 ( 2.164180) 
inject:    2.440000 0.010000 2.450000 ( 2.451466) 
most_common_value(): 1.080000 0.000000 1.080000 ( 1.089784) 
+0

muy, muy bueno! muchas gracias por esta información ... en realidad estaba leyendo sobre 'punto de referencia' para hacer eso. gracias de nuevo. –

+0

Muestra por qué la evaluación comparativa es importante. Supuse que usar inyectar sería más rápido que recorrer la matriz con cada uno, pero la solución de Wayne redujo el tiempo a la mitad. –

+0

@ Z.E.D., Obtengo un error de sintaxis, 'inesperado tIDENTIFIER, esperando '}'' en la línea 15, 'a.max {| a, b | a [1] b [1]} .primero', cuidado en 'b ['. (Ruby 1.9.1). –

0

Se devolverá el valor más popular en la gama

x.group_by{|a| a }.sort_by{|a,b| b.size<=>a.size}.first[0] 

IE:

x = ["1.111", "1.122", "1.250", "1.111"] 
# Most popular 
x.group_by{|a| a }.sort_by{|a,b| b.size<=>a.size}.first[0] 
#=> "1.111 
# How many times 
x.group_by{|a| a }.sort_by{|a,b| b.size<=>a.size}.first[1].size 
#=> 2 
Cuestiones relacionadas