use*_*294 4 ruby algorithm big-o
我的算法技巧很黯淡.我创建了一个方法来查看两个数组是否包含相同的元素(重复无关紧要):
one = [1, "taco", 3, 2, :piece, 4, 5, 5, 5, 5]
two = [:piece, 2, 5, 4, 1, "taco", 3]
def same_elements?(array_one, array_two)
return true if ( (array_one - array_two).empty? && (array_two - array_one).empty? )
return false
end
same_elements?(one, two)
Run Code Online (Sandbox Code Playgroud)
这返回true(这是正确的).问题是,我不确定这个算法的效率是多少.我的第一个猜测是O(n ^ 2)因为我们必须检查ab和ba.我知道O(n ^ 2)非常可怕.有没有更有效的方法来做到这一点?
平均为O(n + m)
最糟糕的情况是O(nm),但只有在你真的想要实现它时才会发生(参见最后一段).
如果array_one
选择大于array_two
,O(m + n)只是O(n),那么该算法平均在线性时间内运行.
另一种更简短的检查方法是:
one = [1, "taco", 3, 2, :piece, 4, 5, 5, 5, 5]
two = [:piece, 2, 5, 4, 1, "taco", 3]
puts Set[*one] == Set[*two] #=> true
# or
puts one.to_set == two.to_set #=> true
Run Code Online (Sandbox Code Playgroud)
return true if x
return false
Run Code Online (Sandbox Code Playgroud)
等同于
x
Run Code Online (Sandbox Code Playgroud)
所以你的代码可以写成:
def same_elements?(array_one, array_two)
(array_one - array_two).empty? && (array_two - array_one).empty?
end
Run Code Online (Sandbox Code Playgroud)
我用1E6元素创建了一个数组,其中一半是0到199999之间的随机数(用于碰撞),另一半是纯Ruby对象.
另一个数组就是第一个,随机洗牌.
N = 1_000_000
one = (1..N).map{rand < 0.5 ? rand(N/5) : Object.new}
two = one.sort_by{rand}
Run Code Online (Sandbox Code Playgroud)
比较集合需要1分钟,设置比较的果味报告比OP的方法快20%左右.
对于较小的整数数组,OP的方法要快一些.
注意:@engineersmnky在评论中提出的代码报告速度与其他方法相似.
当您使用常规阵列时,您的代码肯定不会 O(nm)
.
大概时间是:
在展望rb_ary_diff
中array.c
,这也难怪上面运行的同时所描述的所有方法:他们基本的工作方式相同.
rb_ary_diff
为array_two
(在O(m)中)创建一个哈希表,并迭代array_one
(在O(n)中)的每个元素,寻找哈希表中的值(平均为O(1)).整个操作平均为O(n + m).
这样做两次不会改变任何东西,因此整体时间复杂度保持为O(n + m).
制作此算法O(mn)的一种方法是完全禁用散列方法.除了证明这是一个非常糟糕的主意之外,没有理由这样做.
使用10_000个KeyObjects:
class KeyObject < Object
end
Run Code Online (Sandbox Code Playgroud)
集合比较不到1秒.
使用10_000个KeyObjects:
class KeyObject < Object
def hash
1
end
end
Run Code Online (Sandbox Code Playgroud)
集合比较需要超过14分钟!
2个不同的随机Ruby对象具有相同散列的概率大约是1E-20.严格来说,这个算法的最坏情况是O(mn),但是如果你不去寻找它就永远不会发生.找到与2个元素的碰撞并非易事,找到与1E6元素的碰撞不会偶然发生.