ตรวจสอบว่าอาร์เรย์สองอาร์เรย์มีเนื้อหาเหมือนกันหรือไม่ (เรียงตามลำดับ)


91

ฉันใช้ Ruby 1.8.6 กับ Rails 1.2.3 และจำเป็นต้องตรวจสอบว่าอาร์เรย์สองอาร์เรย์มีองค์ประกอบเหมือนกันหรือไม่โดยไม่คำนึงว่าจะอยู่ในลำดับเดียวกันหรือไม่ หนึ่งในอาร์เรย์ได้รับการรับรองว่าไม่มีรายการที่ซ้ำกัน (อีกอันอาจตอบได้ในกรณีนี้)

ความคิดแรกของฉันคือ

require 'set'
a.to_set == b.to_set

แต่ฉันสงสัยว่ามีวิธีที่มีประสิทธิภาพมากกว่านี้หรือไม่



ลอง array.should = ~ another_array check stackoverflow.com/questions/2978922/…
Athena

คุณสามารถบันทึกความสับสนได้มากโดย: 1) ระบุว่าองค์ประกอบของอาร์เรย์จำเป็นต้องเรียงลำดับหรือไม่ และ 2) ยกตัวอย่างง่ายๆเพื่อชี้แจงว่าคุณหมายถึงอะไร "อาร์เรย์ทั้งสองมีองค์ประกอบเหมือนกันหรือไม่" (เช่นทำ[1,2]และ[2,1,1]มีองค์ประกอบเดียวกันหรือไม่)
Cary Swoveland

Ruby 2.6 ได้เปิดตัวdifferenceซึ่งเสนอวิธีแก้ปัญหาทั้งเร็วมากและอ่านง่ายมาก ข้อมูลเพิ่มเติมที่นี่
SRack

คำตอบ:


144

ไม่จำเป็นต้องมีการแปลงเพื่อตั้งค่า:

a.sort == b.sort

ไม่มีการแปลง? .uniq.sortแล้วคืออะไร? นอกจากนี้ยังuniqคล้ายกับto_setภายในบวกเพิ่มเติม.to_a.sort
Victor Moroz

ยอมรับสิ่งนี้เนื่องจากใกล้เคียงที่สุดกับสิ่งที่ฉันใช้แม้ว่าจะไม่มีuniqs อันที่จริงฉันลงเอยด้วยการสร้างหนึ่งในอาร์เรย์ด้วยRange#to_aดังนั้นฉันจึงต้องsortอีกอันหนึ่งเท่านั้น
Taymon

11
สิ่งนี้จะใช้ไม่ได้หากอาร์เรย์มีองค์ประกอบที่ไม่สามารถเรียงลำดับได้ง่ายๆ (เช่นอาร์เรย์ของแฮช) วิธีแก้ปัญหาของ sahil dhankhar ดูเหมือนจะเป็นวิธีแก้ปัญหาทั่วไป
brad

40

สำหรับสองอาร์เรย์ A และ B: A และ B มีเนื้อหาเหมือนกันถ้า: (A-B).blank? and (B-A).blank?

หรือคุณสามารถตรวจสอบ: ((A-B) + (B-A)).blank?

ตามที่แนะนำโดย @ cort3z โซลูชันนี้ als0 ใช้ได้กับอาร์เรย์โพลีมอร์ฟิกเช่น

 A = [1 , "string", [1,2,3]]
 B = [[1,2,3] , "string", 1]
 (A-B).blank? and (B-A).blank? => true
 # while A.uniq.sort == B.uniq.sort will throw error `ArgumentError: comparison of Fixnum with String failed` 

::::::::::: แก้ไข :::::::::::::

ตามที่แนะนำไว้ในความคิดเห็นวิธีแก้ปัญหาข้างต้นล้มเหลวสำหรับรายการที่ซ้ำกันแม้ว่าตามคำถามที่ไม่จำเป็นเนื่องจากผู้ถามไม่สนใจที่จะซ้ำกัน (เขากำลังแปลงอาร์เรย์ของเขาเพื่อตั้งค่าก่อนที่จะตรวจสอบและมาสก์ที่ซ้ำกันและแม้ว่าคุณจะดู คำตอบที่ได้รับการตอบรับเขาใช้ตัวดำเนินการ. uniq ก่อนที่จะตรวจสอบและนั่นก็ปิดบังข้อมูลซ้ำด้วย) แต่ถึงกระนั้นถ้าคุณสนใจที่ซ้ำกันเพียงแค่เพิ่มการตรวจสอบการนับก็จะแก้ไขปัญหาเดียวกันได้ (ตามคำถามมีเพียงอาร์เรย์เดียวเท่านั้นที่สามารถมีรายการที่ซ้ำกันได้) ดังนั้นทางออกสุดท้ายคือ: A.size == B.size and ((A-B) + (B-A)).blank?


สิ่งนี้จะล้มเหลวหากอาร์เรย์มีรายการที่ซ้ำกัน เช่น if A=[1]and B=[1,1], (A-B)and (B-A)will return blank. โปรดดูเอกสารอาร์เรย์
jtpereyda

@dafrazzman เห็นด้วยกับคุณโดยสิ้นเชิง ฉันได้แก้ไขคำตอบของฉันเพื่อรวมความคิดเห็นของคุณ แต่ถ้าคุณได้ดูคำถามอย่างละเอียด (หรือคำตอบที่ยอมรับ) ผู้ถามกำลังใช้: a.to_set == b.to_set และคำตอบที่ยอมรับนั้นกำลังใช้a.uniq.sort == b.uniq.sortและทั้งสองอย่างให้ผลลัพธ์เดียวกัน((A-B) + (B-A)).blank?สำหรับ A = [1] และ B = [1,1] เห็นด้วยไหม เนื่องจากเขาเพิ่งขอให้ปรับปรุงโซลูชันดั้งเดิมของเขาโซลูชันดั้งเดิมของฉันก็ยังใช้งานได้ :) ตกลง?
Sahil Dhankhar

1
โซลูชันนี้ค่อนข้างดีเนื่องจากจัดการกับวัตถุหลายประเภท สมมติว่าคุณมีA = [123, "test", [], some_object, nil]และB = A#because I am lazyจากนั้นก็A.uniq.sortจะโยนความผิดพลาด (เปรียบเทียบสตริงและอาร์เรย์ล้มเหลว)
Automatico

นี่จะเป็น O (n) หรือไม่เนื่องจากขึ้นอยู่กับขนาดอาร์เรย์ (linear)
user3007294

1
มันจะไม่ทำงานหากอาร์เรย์มีขนาดเท่ากัน แต่องค์ประกอบที่ซ้ำกันไม่เหมือนกัน ตัวอย่างเช่นA = [1, 1, 2]และB = [1, 2, 2]
Boudi

23

การเปรียบเทียบความเร็ว

require 'benchmark/ips'
require 'set'

a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3, 4, 5, 6]

Benchmark.ips do |x|
  x.report('sort')   { a.sort == b.sort }  
  x.report('sort!')  { a.sort! == b.sort! }  
  x.report('to_set') { a.to_set == b.to_set }  
  x.report('minus')  { ((a - b) + (b - a)).empty? }  
end  

Warming up --------------------------------------
            sort    88.338k i/100ms
           sort!   118.207k i/100ms
          to_set    19.339k i/100ms
           minus    67.971k i/100ms
Calculating -------------------------------------
            sort      1.062M (± 0.9%) i/s -      5.389M in   5.075109s
           sort!      1.542M (± 1.2%) i/s -      7.802M in   5.061364s
          to_set    200.302k (± 2.1%) i/s -      1.006M in   5.022793s
           minus    783.106k (± 1.5%) i/s -      3.942M in   5.035311s

ลำดับ btw ของ elemetns ไม่มีผลต่อsortความเร็วของ
Morozov

ทำให้ฉันประหลาดใจ ... ฉันคาดว่าการเปรียบเทียบโดยชุดจะมีประสิทธิภาพดีกว่าคนอื่น ๆ ทั้งหมดเนื่องจากชุดการค้นหา O (n) ความซับซ้อนของเวลา ดังนั้นการเรียงลำดับใด ๆ ที่นำไปใช้งานได้ดีจะต้องใช้ O (n logn) ในขณะที่การส่งไปยังชุดและการค้นหาค่าโดยรวมจะทำให้เป็นเวลา O (n)
Oleg Afanasyev

1
ฉันคาดหวังว่าto_setจะเริ่มมีประสิทธิภาพดีกว่าอาร์เรย์ที่มีขนาดใหญ่พอที่ O (n logn) จะเริ่มมีความสำคัญมากกว่าความพยายามในการแปลงอาร์เรย์เพื่อตั้งค่า
Andrius Chamentauskas

1
สิ่งนี้มีประโยชน์ แต่ไม่ใช่คำตอบในตัวเอง? อาจจะดีกว่าที่จะเพิ่มสิ่งนี้ลงในโซลูชันที่มีอยู่
SRack

21

รูบี้ 2.6+

เปิดตัวทับทิม differenceใน 2.6

สิ่งนี้ให้วิธีแก้ปัญหาที่รวดเร็วและอ่านง่ายมากที่นี่ดังนี้:

a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3, 4, 5, 6]

a.difference(b).any?
# => false
a.difference(b.reverse).any?
# => false

a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3]
a.difference(b).any?
# => true

การเรียกใช้เกณฑ์มาตรฐาน:

a = Array.new(1000) { rand(100) }
b = Array.new(1000) { rand(100) }

Benchmark.ips do |x|
  x.report('sort')   { a.sort == b.sort }  
  x.report('sort!')  { a.sort! == b.sort! }  
  x.report('to_set') { a.to_set == b.to_set }  
  x.report('minus')  { ((a - b) + (b - a)).empty? }  
  x.report('difference') { a.difference(b).any? }
end

      sort     13.908k (± 2.6%) i/s -     69.513k in   5.001443s
     sort!     14.656k (± 3.0%) i/s -     73.736k in   5.035744s
    to_set     5.125k  (± 2.9%) i/s -     26.023k in   5.082083s
     minus     16.398k (± 2.2%) i/s -     83.181k in   5.074938s
difference     27.839k (± 5.2%) i/s -    141.048k in   5.080706s

หวังว่าจะช่วยใครสักคน!


5
ความแตกต่างคือการทำลายในกรณีนี้ a = [1, 2, 3] b = [1, 2, 3, 4, 5] a.difference (b) .any? => false
error-404


8

หากคุณคาดหวัง[:a, :b] != [:a, :a, :b] to_setไม่ได้ผล คุณสามารถใช้ความถี่แทน:

class Array
  def frequency
    p = Hash.new(0)
    each{ |v| p[v] += 1 }
    p
  end
end

[:a, :b].frequency == [:a, :a, :b].frequency #=> false
[:a, :b].frequency == [:b, :a].frequency #=> true

ทำไมไม่เพียงแค่a.sort == b.sortว่าเขาสนใจเรื่องความถี่?
fl00r

4
@ fl00r จะเกิดอะไรขึ้นถ้ารายการเทียบไม่ได้? ["", :b].frequency == [:b, ""].frequency #=> true
Victor Moroz

2
นอกจากนี้คุณสามารถทำบางสิ่งบางอย่างได้เช่นa.group_by{|i| i} == b.group_by{|i| i}
fl00r

7

หากคุณทราบว่าอาร์เรย์มีความยาวเท่ากันและไม่มีอาร์เรย์ที่ซ้ำกันสิ่งนี้ก็ใช้ได้เช่นกัน:

( array1 & array2 ) == array1

คำอธิบาย:ตัว&ดำเนินการในกรณีนี้ส่งคืนสำเนาของ a1 ไม่พบรายการใด ๆ ที่ไม่พบใน a2 ซึ่งเหมือนกับ a1 iff ดั้งเดิมทั้งสองอาร์เรย์มีเนื้อหาเหมือนกันโดยไม่มีรายการที่ซ้ำกัน

Analyis:ระบุว่าคำสั่งซื้อที่มีการเปลี่ยนแปลงผมคาดเดานี้จะดำเนินการเป็นซ้ำสองครั้งเพื่อให้อย่างต่อเนื่องO(n*n)โดยเฉพาะอย่างยิ่งเลวร้ายยิ่งสำหรับอาร์เรย์ที่มีขนาดใหญ่กว่าที่ควรดำเนินการกับกรณีที่เลวร้ายที่สุดa1.sort == a2.sortO(n*logn)


2
ไม่ได้ผลเสมอไป: a1 = [1,2,3], a2 = [2, 1, 3] a1 && a2ผลตอบแทน[2,1,3]สำหรับฉันซึ่งไม่เท่ากับa1
Kalyan Raghu

@Kaylan คุณไม่ได้หมายความว่าจะทำงานเมื่อa1==a2? มันอาจใช้งานได้ถ้าarray1ทางด้านขวาของความเท่าเทียมถูกแทนที่ด้วยarray2แต่ฉันสงสัยว่า&จะรับประกันลำดับขององค์ประกอบที่ส่งคืนมา
Cary Swoveland

1
@KalyanRaghu &เป็นตัวดำเนินการตั้งค่าจุดตัดสำหรับอาร์เรย์&&เป็นตรรกะและ - แตกต่างกันมาก!
Kimball

3

รวม&และsizeอาจเร็วเกินไป

require 'benchmark/ips'
require 'set'

Benchmark.ips do |x|
  x.report('sort')   { a.sort == b.sort }  
  x.report('sort!')  { a.sort! == b.sort! }  
  x.report('to_set') { a.to_set == b.to_set }  
  x.report('minus')  { ((a - b) + (b - a)).empty? }
  x.report('&.size') { a.size == b.size && (a & b).size == a.size }  
end  

Calculating -------------------------------------
                sort    896.094k (±11.4%) i/s -      4.458M in   5.056163s
               sort!      1.237M (± 4.5%) i/s -      6.261M in   5.071796s
              to_set    224.564k (± 6.3%) i/s -      1.132M in   5.064753s
               minus      2.230M (± 7.0%) i/s -     11.171M in   5.038655s
              &.size      2.829M (± 5.4%) i/s -     14.125M in   5.010414s

2

แนวทางหนึ่งคือการวนซ้ำบนอาร์เรย์โดยไม่มีรายการที่ซ้ำกัน

# assume array a has no duplicates and you want to compare to b
!a.map { |n| b.include?(n) }.include?(false)

สิ่งนี้ส่งคืนอาร์เรย์ของความจริง หากมีเท็จปรากฏขึ้นด้านนอกinclude?จะคืนค่าจริง ดังนั้นคุณต้องสลับสิ่งทั้งหมดเพื่อตรวจสอบว่ามันตรงกันหรือไม่


@ Victor Moroz คุณถูกต้องและการนับความถี่จะเป็น O (n)
รอน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.