2010-04-01 14 views
20

mam tę tablicę, na przykład (rozmiar jest zmienna):Znajdź najczęściej ciąg w tablicy

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

i muszę znaleźć wartość najbardziej Commom ("1.111" w tym przypadku).

Czy istnieje prosty sposób na zrobienie tego?

Tks z góry!


EDYCJA # 1: Dziękuję wszystkim za odpowiedzi!


EDIT # 2: Zmieniłem zaakceptowane odpowiedź na podstawie informacji Z.E.D. użytkownika. Dziękuję wszystkim jeszcze raz!

Odpowiedz

43

Ruby < 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" 

Uwaga: Enumberable.max_by jest nowy z Ruby 1.9, ale zostało przeniesione do 1.8.7

Ruby> = 2,2

Ruby 2.2 wprowadza metodę Object#itself , dzięki czemu możemy uczynić kod bardziej zwięzłym:

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

jako plaster małpa

Albo jak 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

Jestem pod wrażeniem przyspieszenia w zwykły sposób, w jaki to zrobię. Dobra robota. –

+0

@Wayne Conrad, rozwiązanie uber. +1 –

+1

Oto krótsza wersja: x.group_by {| e | e} .values.max_by (&: size) .pierwszy # => "1.111" Włączenie go do metody, jeśli jest to pożądane, pozostawia się czytelnikowi ;-) –

4

Możesz posortować tablicę, a następnie przeliczyć ją raz. W pętli wystarczy śledzić bieżący element i liczbę wyświetleń. Po zakończeniu listy lub zmianie pozycji ustaw max_count == count, jeśli count > max_count. I oczywiście śledzić, który element ma max_count.

2

Można utworzyć hashmap, która przechowuje elementy tablicy jako klucze z ich wartościami określającymi ile razy ten element pojawia się w tablicy.

pseudokod:

["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) 
} 

Jak już wspomniano, sortowania może być szybciej.

+0

Dlaczego sortowanie powinno być szybsze? Sortowanie jest w najlepszym wypadku O (n log n), podczas gdy jest to O (n) – Pyrolistical

+0

Korekta, sortowanie oparte na porównaniu to O (n log n). Istnieją rodzaje liniowe, takie jak sortowanie wiadro lub sortowanie radix. EDYCJA: zazwyczaj trzeba mieć określone rodzaje danych do sortowania kubełków lub sortowania radix, aby rzeczywiście były bardziej wydajne niż sortowania porównawcze. To, o czym oni nadrabiają, z czasem pożerają w kosmosie. FTR, powyższy pseudo kod jest sortowaniem kubełkowym. – saramah

2

użyciu domyślnej wartości funkcji wartości mieszania:

>> 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

Zostało to wybrane jako odpowiedź, ale spójrz na wyniki testu porównawczego, które mam, przedstawione poniżej. –

+0

Czy to nie "nowe" (0) "spowoduje ten sam obiekt dla każdego elementu hash? 'Hash.new {| h, k | h [k] = 0} 'zamiast? – karatedog

5

jednorazowym przejściu przez hash gromadzić liczniki. Użyj .max(), aby znaleźć hash z największą wartością.

 
#!/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] 

lub toczyć je wszystkie w jednym wierszu:

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

Jeśli chcesz tylko dodać element do tyłu.pierwszy():

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

Pierwsza próbka Kiedyś to w jaki sposób to zrobić w Perlu zwykle. Drugi to więcej Ruby-owski. Oba działają ze starszymi wersjami Rubiego. Chciałem je porównać, a także zobaczyć, jak rozwiązanie Wayne'a by przyspieszyć więc przetestowane z benchmarkiem:

 
#!/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 

Oto wyniki:

 
          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

bardzo, bardzo ładne! dziękuję bardzo za te informacje ... właściwie czytałem o "benchmarku", żeby to zrobić. Jeszcze raz dziękuję. –

+0

Pokazuje, dlaczego benchmarking jest ważny. Zakładałem, że użycie iniekcji będzie szybsze niż wykonywanie pętli przez macierz, ale rozwiązanie Wayne'a skraca czas o połowę. –

+0

@ Z.E.D., Otrzymuję błąd składni, 'nieoczekiwany TIDENTIFIER, oczekujący '}'' on line 15, 'a.max {| a, b | a [1] b [1]} .first', caret at 'b [. (Ruby 1.9.1). –

0

Powróci wartość najpopularniejszej w tablicy

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