我想按如下方式对数组进行排序:
["10a","10b","9a","9b","8a","8b"]
当我打电话时,
a = a.sort {|a,b| a <=> b}
它将如下排序:
["10a","10b","8a","8b","9a","9b"]
10
是一个字符串,不作为数字处理。当我第一次按整数然后按字符串排序时,它会做同样的事情。有没有人知道如何处理10作为10而不将其变成整数?这会弄乱字母a
,b
等等。
答案 0 :(得分:7)
当我第一次按整数然后按字符串排序时,它会做同样的事情。
这本来是我的第一直觉,似乎完美无缺:
%w[10a 10b 9a 9b 8a 8b].sort_by {|el| [el.to_i, el] }
# => ['8a', '8b', '9a', '9b', '10a', '10b']
答案 1 :(得分:2)
我做这样的事情:
ary = ["10a","10b","9a","9b","8a","8b"]
sorted_ary = ary.sort_by{ |e|
/(?<digit>\d+)(?<alpha>\D+)/ =~ e
[digit.to_i, alpha]
}
ary # => ["10a", "10b", "9a", "9b", "8a", "8b"]
sorted_ary # => ["8a", "8b", "9a", "9b", "10a", "10b"]
对于这类问题, sorted_by
会比sort
更快。因为要排序的值不是直接比较,我们需要深入研究它以获取用于整理的值,正常排序必须为每个元素多次执行。相反,使用sort_by
缓存计算值,然后根据它进行排序。
/(?<digit>\d+)(?<alpha>\D+)/ =~ e
并不是您通常会看到的正则表达式。 named-captures ?<digit>
和?<alpha>
定义了在该表单中使用时可立即访问的局部变量的名称。
[digit.to_i, alpha]
返回一个数组,由前导数字转换为整数,后跟字符。然后,该数组用于sort_by
进行比较。
使用Fruity对sort
与sort_by
进行基准测试:我为正在排序的数组添加了一些长度,以便更好地推动例程以获得更好的时间分辨率。
require 'fruity'
ARY = (%w[10a 10b 9a 9b 8a 8b] * 1000).shuffle
compare do
cary_to_i_sort_by { ARY.sort_by { |s| s.to_i(36) } }
cary_to_i_sort { ARY.map { |s| s.to_i(36) }.sort.map {|i| i.to_s(36)} }
end
compare do
jorge_sort_by { ARY.sort_by {|el| [el.to_i, el] } }
jorg_sort { ARY.map {|el| [el.to_i, el] }.sort.map(&:last) }
end
# >> Running each test 2 times. Test will take about 1 second.
# >> cary_to_i_sort_by is faster than cary_to_i_sort by 19.999999999999996% ± 1.0%
# >> Running each test once. Test will take about 1 second.
# >> jorge_sort_by is faster than jorg_sort by 10.000000000000009% ± 1.0%
Ruby sort_by
使用Schwartzian Transform,在处理我们必须计算要排序的值的对象时,它可以在排序速度上产生重大差异。
你可以在ARY的定义中运行100_000而不是1_000的基准吗?
require 'fruity'
ARY = (%w[10a 10b 9a 9b 8a 8b] * 100_000).shuffle
compare do
cary_to_i_sort_by { ARY.sort_by { |s| s.to_i(36) } }
cary_to_i_sort { ARY.map { |s| s.to_i(36) }.sort.map {|i| i.to_s(36)} }
end
compare do
jorge_sort_by { ARY.sort_by {|el| [el.to_i, el] } }
jorg_sort { ARY.map {|el| [el.to_i, el] }.sort.map(&:last) }
end
# >> Running each test once. Test will take about 10 seconds.
# >> cary_to_i_sort_by is faster than cary_to_i_sort by 2x ± 1.0
# >> Running each test once. Test will take about 26 seconds.
# >> jorg_sort is similar to jorge_sort_by
Wikepedia上的文章有一个很好的efficiency analysis和example,解释了为什么sort_by
首选进行代价高昂的比较。
Ruby&#39;} sort_by
documentation也很好地涵盖了这一点。
我不认为阵列的大小会有很大的不同。如果有的话,随着数组大小的增加,如果中间值的计算成本很高,sort_by
由于其缓存仍然会更快。请记住,sort_by
是所有已编译的代码,而使用基于Ruby脚本的转换会因为数组转换后执行速度较慢,切换到sort
然后从子文件中提取原始对象-arrays。一个更大的数组意味着它必须要做更多次。
答案 2 :(得分:1)
两种不使用String#to_i的方式(但依赖于每个字符串由一个或多个数字后跟一个小写字母组成的假设)。
ary = ["10a","10b","9a","9b","8a","8b","100z", "96b"]
<强>#1 强>
mx = ary.map(&:size).max
ary.sort_by { |s| s.rjust(mx) }
#=> ["8a", "8b", "9a", "9b", "10a", "10b", "96b", "100z"]
<强>#2 强>
ary.sort_by { |s| s.to_i(36) }
#=> ["8a", "8b", "9a", "9b", "10a", "10b", "96b", "100z"]
嗯,我想知道是否:
ary.map { |s| s.rjust(mx) }.sort.map(&:lstrip)
或
ary.map { |s| s.to_i(36) }.sort.map {|i| i.to_s(36)}
会更快。
答案 3 :(得分:0)
▶ a = ["10a","10b","9a","9b","8a","8b"]
▶ a.sort { |a,b| a.to_i == b.to_i ? a <=> b : a.to_i <=> b.to_i }
#=> [
# [0] "8a",
# [1] "8b",
# [2] "9a",
# [3] "9b",
# [4] "10a",
# [5] "10b"
#]
希望它有所帮助。
答案 4 :(得分:0)
["10a","10b","9a","9b","8a","8b"]
.sort_by{|s| s.split(/(\D+)/).map.with_index{|s, i| i.odd? ? s : s.to_i}}
#=> ["8a", "8b", "9a", "9b", "10a", "10b"]
["10a3","10a4", "9", "9aa","9b","8a","8b"]
.sort_by{|s| s.split(/(\D+)/).map.with_index{|s, i| i.odd? ? s : s.to_i}}
#=> ["8a", "8b", "9", "9aa", "9b", "10a3", "10a4"]
答案 5 :(得分:0)
先生们,启动引擎!
我决定对提供的各种解决方案进行基准测试。我感到好奇的一件事是将sort_by
解决方案转换为sort
解决方案的效果。例如,我比较了我的方法
def cary_to_i(a)
a.sort_by { |s| s.to_i(36) }
end
到
def cary_to_i_sort(a)
a.map { |s| s.to_i(36) }.sort.map {|i| i.to_s(36)}
end
这总是涉及将原始数组映射到sort_by
块中的转换值,对该数组进行排序,然后将结果映射回原始数组中的元素(何时可以完成)。
我尝试使用sort_by
的一些方法进行此sort
- 到 - sort_by
转换。毫不奇怪,转换为sort
的速度通常更快,但改进量相差很大。
方法比较
module Methods
def mudasobwa(a)
a.sort { |a,b| a.to_i == b.to_i ? a <=> b : a.to_i <=> b.to_i }
end
def jorg(a)
a.sort_by {|el| [el.to_i, el] }
end
def jorg_sort(a)
a.map {|el| [el.to_i, el] }.sort.map(&:last)
end
def the(a)
a.sort_by {|e| /(?<digit>\d+)(?<alpha>\D+)/ =~ e
[digit.to_i, alpha] }
end
def the_sort(a)
a.map {|e| /(?<digit>\d+)(?<alpha>\D+)/ =~ e
[digit.to_i, alpha]}.sort.map {|d,a| d.to_s+a }
end
def engineer(a) a.sort_by { |s|
s.scan(/(\d+)(\D+)/).flatten.tap{ |a| a[0] = a[0].to_i } }
end
def sawa(a) a.sort_by { |s|
s.split(/(\D+)/).map.with_index { |s, i| i.odd? ? s : s.to_i } }
end
def cary_rjust(a)
mx = a.map(&:size).max
a.sort_by {|s| s.rjust(mx)}
end
def cary_rjust_sort(a)
mx = a.map(&:size).max
a.map { |s| s.rjust(mx) }.sort.map(&:lstrip)
end
def cary_to_i(a)
a.sort_by { |s| s.to_i(36) }
end
def cary_to_i_sort(a)
a.map { |s| s.to_i(36) }.sort.map {|i| i.to_s(36)}
end
end
include Methods
methods = Methods.instance_methods(false)
#=> [:mudasobwa, :jorg, :jorg_sort, :the, :the_sort,
# :cary_rjust, :cary_rjust_sort, :cary_to_i, :cary_to_i_sort]
测试数据和助手
def test_data(n)
a = 10_000.times.to_a.map(&:to_s)
b = [*'a'..'z']
n.times.map { a.sample + b.sample }
end
def compute(m,a)
send(m,a)
end
确认方法返回相同的值
a = test_data(1000)
puts "All methods correct: #{methods.map { |m| compute(m,a) }.uniq.size == 1}"
基准代码
require 'benchmark'
indent = methods.map { |m| m.to_s.size }.max
n = 500_000
a = test_data(n)
puts "\nSort random array of size #{n}"
Benchmark.bm(indent) do |bm|
methods.each do |m|
bm.report m.to_s do
compute(m,a)
end
end
end
<强>测试强>
Sort random array of size 500000
user system total real
mudasobwa 4.760000 0.000000 4.760000 ( 4.765170)
jorg 2.870000 0.020000 2.890000 ( 2.892359)
jorg_sort 2.980000 0.020000 3.000000 ( 3.010344)
the 9.040000 0.100000 9.140000 ( 9.160944)
the_sort 4.570000 0.090000 4.660000 ( 4.668146)
engineer 10.110000 0.070000 10.180000 ( 10.198117)
sawa 27.310000 0.160000 27.470000 ( 27.504958)
cary_rjust 1.080000 0.010000 1.090000 ( 1.087788)
cary_rjust_sort 0.740000 0.000000 0.740000 ( 0.746132)
cary_to_i 0.570000 0.000000 0.570000 ( 0.576570)
cary_to_i_sort 0.460000 0.020000 0.480000 ( 0.477372)
<强>附录强>
@theTinMan证明sort_by
和sort
方法之间的比较对测试数据的选择很敏感。使用他使用的数据:
def test_data(n)
(%w[10a 10b 9a 9b 8a 8b] * (n/6)).shuffle
end
我得到了这些结果:
Sort random array of size 500000
user system total real
mudasobwa 0.620000 0.000000 0.620000 ( 0.622566)
jorg 0.620000 0.010000 0.630000 ( 0.636018)
jorg_sort 0.640000 0.010000 0.650000 ( 0.638493)
the 8.790000 0.090000 8.880000 ( 8.886725)
the_sort 2.670000 0.070000 2.740000 ( 2.743085)
engineer 3.150000 0.040000 3.190000 ( 3.184534)
sawa 3.460000 0.040000 3.500000 ( 3.506875)
cary_rjust 0.360000 0.010000 0.370000 ( 0.367094)
cary_rjust_sort 0.480000 0.010000 0.490000 ( 0.499956)
cary_to_i 0.190000 0.010000 0.200000 ( 0.187136)
cary_to_i_sort 0.200000 0.000000 0.200000 ( 0.203509)
请注意,绝对时间也会受到影响。
有人能解释基准测试差异的原因吗?