Berikut adalah dua cara lain untuk menemukan duplikat.
Gunakan satu set
require 'set'
def find_a_dup_using_set(arr)
s = Set.new
arr.find { |e| !s.add?(e) }
end
find_a_dup_using_set arr
#=> "hello"
Gunakan selectsebagai pengganti findarray semua duplikat.
Menggunakan Array#difference
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
def find_a_dup_using_difference(arr)
arr.difference(arr.uniq).first
end
find_a_dup_using_difference arr
#=> "hello"
Penurunan .first untuk mengembalikan larik semua duplikat.
Kedua metode kembali nil jika tidak ada duplikat.
Saya mengusulkan agarArray#difference ditambahkan ke inti Ruby. Informasi lebih lanjut ada dalam jawaban saya di sini .
Tolok ukur
Mari kita bandingkan metode yang disarankan. Pertama, kita membutuhkan array untuk pengujian:
CAPS = ('AAA'..'ZZZ').to_a.first(10_000)
def test_array(nelements, ndups)
arr = CAPS[0, nelements-ndups]
arr = arr.concat(arr[0,ndups]).shuffle
end
dan metode untuk menjalankan benchmark untuk berbagai test array:
require 'fruity'
def benchmark(nelements, ndups)
arr = test_array nelements, ndups
puts "\n#{ndups} duplicates\n"
compare(
Naveed: -> {arr.detect{|e| arr.count(e) > 1}},
Sergio: -> {(arr.inject(Hash.new(0)) {|h,e| h[e] += 1; h}.find {|k,v| v > 1} ||
[nil]).first },
Ryan: -> {(arr.group_by{|e| e}.find {|k,v| v.size > 1} ||
[nil]).first},
Chris: -> {arr.detect {|e| arr.rindex(e) != arr.index(e)} },
Cary_set: -> {find_a_dup_using_set(arr)},
Cary_diff: -> {find_a_dup_using_difference(arr)}
)
end
Saya tidak memasukkan jawaban @ JjP karena hanya satu duplikat yang akan dikembalikan, dan ketika jawabannya diubah untuk melakukan itu sama dengan jawaban @ Naveed sebelumnya. Saya juga tidak memasukkan jawaban @ Marin, yang, ketika diposting sebelum jawaban @ Naveed, mengembalikan semua duplikat daripada hanya satu (titik kecil tetapi tidak ada gunanya mengevaluasi keduanya, karena mereka identik ketika mengembalikan hanya satu duplikat).
Saya juga memodifikasi jawaban lain yang mengembalikan semua duplikat untuk mengembalikan hanya yang pertama ditemukan, tetapi yang seharusnya tidak berpengaruh pada kinerja, karena mereka menghitung semua duplikat sebelum memilih satu.
Hasil untuk setiap tolok ukur terdaftar dari yang tercepat hingga yang paling lambat:
Pertama anggap array berisi 100 elemen:
benchmark(100, 0)
0 duplicates
Running each test 64 times. Test will take about 2 seconds.
Cary_set is similar to Cary_diff
Cary_diff is similar to Ryan
Ryan is similar to Sergio
Sergio is faster than Chris by 4x ± 1.0
Chris is faster than Naveed by 2x ± 1.0
benchmark(100, 1)
1 duplicates
Running each test 128 times. Test will take about 2 seconds.
Cary_set is similar to Cary_diff
Cary_diff is faster than Ryan by 2x ± 1.0
Ryan is similar to Sergio
Sergio is faster than Chris by 2x ± 1.0
Chris is faster than Naveed by 2x ± 1.0
benchmark(100, 10)
10 duplicates
Running each test 1024 times. Test will take about 3 seconds.
Chris is faster than Naveed by 2x ± 1.0
Naveed is faster than Cary_diff by 2x ± 1.0 (results differ: AAC vs AAF)
Cary_diff is similar to Cary_set
Cary_set is faster than Sergio by 3x ± 1.0 (results differ: AAF vs AAC)
Sergio is similar to Ryan
Sekarang pertimbangkan sebuah array dengan 10.000 elemen:
benchmark(10000, 0)
0 duplicates
Running each test once. Test will take about 4 minutes.
Ryan is similar to Sergio
Sergio is similar to Cary_set
Cary_set is similar to Cary_diff
Cary_diff is faster than Chris by 400x ± 100.0
Chris is faster than Naveed by 3x ± 0.1
benchmark(10000, 1)
1 duplicates
Running each test once. Test will take about 1 second.
Cary_set is similar to Cary_diff
Cary_diff is similar to Sergio
Sergio is similar to Ryan
Ryan is faster than Chris by 2x ± 1.0
Chris is faster than Naveed by 2x ± 1.0
benchmark(10000, 10)
10 duplicates
Running each test once. Test will take about 11 seconds.
Cary_set is similar to Cary_diff
Cary_diff is faster than Sergio by 3x ± 1.0 (results differ: AAE vs AAA)
Sergio is similar to Ryan
Ryan is faster than Chris by 20x ± 10.0
Chris is faster than Naveed by 3x ± 1.0
benchmark(10000, 100)
100 duplicates
Cary_set is similar to Cary_diff
Cary_diff is faster than Sergio by 11x ± 10.0 (results differ: ADG vs ACL)
Sergio is similar to Ryan
Ryan is similar to Chris
Chris is faster than Naveed by 3x ± 1.0
Catatan yang find_a_dup_using_difference(arr)akan jauh lebih efisien jika Array#differencediimplementasikan dalam C, yang akan terjadi jika ditambahkan ke inti Ruby.
Kesimpulan
Banyak jawaban yang masuk akal tetapi menggunakan Set adalah pilihan terbaik yang jelas . Ini tercepat dalam kasus-kasus menengah-keras, paling cepat bersama dalam yang paling sulit dan hanya dalam kasus-kasus sepele komputasi - ketika pilihan Anda tidak masalah lagi - dapat dikalahkan.
Satu kasus yang sangat istimewa di mana Anda dapat memilih solusi Chris adalah jika Anda ingin menggunakan metode ini untuk secara terpisah menduplikasi duplikat ribuan array kecil dan berharap menemukan duplikat yang biasanya kurang dari 10 item. Ini akan menjadi sedikit lebih cepat karena menghindari overhead tambahan kecil untuk membuat Set.
arr == arr.uniqakan menjadi cara yang mudah dan elegan untuk memeriksa apakaharrmemiliki duplikat, namun, itu tidak memberikan yang digandakan.