Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/video/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
以最快的方式从ruby中的独占范围获得最大值_Ruby_Optimization_Performance_Range - Fatal编程技术网

以最快的方式从ruby中的独占范围获得最大值

以最快的方式从ruby中的独占范围获得最大值,ruby,optimization,performance,range,Ruby,Optimization,Performance,Range,好的,假设你在ruby上有一个很大的范围。我想找到一种方法来获得范围内的最大值 范围是独占的(用三个点定义),这意味着它的结果中不包括结束对象。它可以由整数、字符串、时间或响应和#succ的任何对象组成。(这是范围内开始/结束对象的唯一要求) 以下是一个独占范围的示例: past = Time.local(2010, 1, 1, 0, 0, 0) now = Time.now range = past...now range.include?(now) # =>

好的,假设你在ruby上有一个很大的范围。我想找到一种方法来获得范围内的最大值

范围是独占的(用三个点定义),这意味着它的结果中不包括结束对象。它可以由整数、字符串、时间或响应
#succ
的任何对象组成。(这是范围内开始/结束对象的唯一要求)

以下是一个独占范围的示例:

  past  = Time.local(2010, 1, 1, 0, 0, 0)
  now   = Time.now
  range = past...now

  range.include?(now)  # => false
现在我知道我可以这样做来获得最大值:

  range.max  # => returns 1 second before "now" using Enumerable#max
但这将需要大量的时间来执行。我也知道,无论最终物体是什么,我都可以减去1秒。但是,对象可能不是时间,甚至可能不支持
-
。我更愿意找到一个有效的通用解决方案,但我愿意将特例代码与通用解决方案的回退结合起来(稍后将详细介绍)

如上所述,使用
Range#last
也不起作用,因为它是一个独占范围,不包括其结果中的最后一个值

我能想到的最快方法是:

  max = nil
  range.each { |value| max = value }

  # max now contains nil if the range is empty, or the max value
这类似于
Enumerable#max
所做的(哪个范围继承),只是它利用了每个值都将大于上一个值的事实,因此我们可以跳过使用
#
将每个值与上一个值进行比较(就像
Range#max
所做的那样),从而节省一点时间

我考虑的另一种方法是为常见的ruby类型(如Integer、String、Time、Date、DateTime)提供特殊的案例代码,然后将上述代码用作备用代码。这可能有点难看,但在遇到这些对象类型时可能效率更高,因为我可以使用从
Range#last
的减法来获得最大值,而无需任何迭代


有人能想出比这更有效/更快的方法吗?

我不确定速度(初始测试似乎也不太快),但下面可能会满足您的需要:

past  = Time.local(2010, 1, 1, 0, 0, 0)
now   = Time.now
range = past...now

range.to_a[-1]
非常基本的测试(在我的头脑中计数)表明,这大约需要4秒,而您提供的方法大约需要5-6秒。希望这有帮助


编辑1:删除了第二个解决方案,因为它完全错误。

我能想到的最简单的解决方案,适用于包含范围和独占范围:

range.max
其他一些可能的解决办法:

range.entries.last
range.entries[-1]
这些解决方案都是O(n),在大范围内会非常缓慢。原则上的问题是Ruby中的范围值是使用
succ
方法从一开始就对所有值进行迭代枚举的。元素不必实现返回上一个值的方法(即
pred

最快的方法是找到最后一项的前置项(O(1)解决方案):

这仅适用于具有实现
pred
的元素的范围。Ruby的更高版本为整数实现
pred
。如果该方法不存在,您必须自己添加它(本质上相当于您建议的特殊情况代码,但实现起来稍微简单)

一些快速基准测试表明,对于大范围(在本例中,
range=1…1000000
),最后一种方法在许多数量级上是最快的,因为它是O(1):


建议在注释中使用
range.last-(range.exclude_end??1:0)
。它确实适用于没有其他方法的日期,但永远不会适用于非数字范围<代码>字符串#-不存在,对于整数参数没有意义<但是,代码>字符串#pred。

我认为没有任何不涉及枚举范围的方法可以实现这一点,至少如前所述,除非您有关于如何构造范围的其他信息,因此可以在不枚举的情况下推断所需的值。在所有的建议中,我选择了
#max
,因为它似乎最具表现力

require 'benchmark'
N = 20
Benchmark.bm(30) do |r|
  past, now  = Time.local(2010, 2, 1, 0, 0, 0), Time.now
  @range = past...now
  r.report("range.max") do
    N.times { last_in_range = @range.max }
  end
  r.report("explicit enumeration") do
    N.times { @range.each { |value| last_in_range = value } }
  end
  r.report("range.entries.last") do
    N.times { last_in_range = @range.entries.last }
  end
  r.report("range.to_a[-1]") do
    N.times { last_in_range = @range.to_a[-1] }
  end
end
                                user     system      total        real
range.max                      49.406000   1.515000  50.921000 ( 50.985000)
explicit enumeration           52.250000   1.719000  53.969000 ( 54.156000)
range.entries.last             53.422000   4.844000  58.266000 ( 58.390000)
range.to_a[-1]                 49.187000   5.234000  54.421000 ( 54.500000)

我注意到第三和第四个选项显著增加了系统时间。我认为这与显式创建数组有关,这似乎是避免使用它们的一个很好的理由,即使它们在运行时间上并不明显更昂贵。

在最后一行中,您有
range.last.pred
。这对我来说不合适。
也不包括range.last.prev
。请您解释一下这一部分好吗?所以请尝试使用
pred
,如果失败,请尝试
range。last-1
否则返回到另一个解决方案
pred
是在Ruby的更高版本中为整数实现的(它在我的1.8.7-174中,应该在1.9+中)。它不适用于实现
succ
的所有类,因此可能需要自己定义它。更紧凑、更具日期意识的实现如下:
range.last-(range.exclude\u end±1:0)
但它仍然不那么可读,我必须说。@kirushik:
range.last-1
range.last.pred
不同。范围元素可以是实现
succ
的任何元素,它们不一定有
-
方法,更不用说接受
Fixnum
参数的方法(例如
String#-
不存在)。问题是,
pred
在大多数类上也不存在。对于某些类,实现实际上可以像
self-1
一样简单。
                                          user     system      total        real
r.entries.last                       11.760000   0.880000  12.640000 ( 12.963178)
r.entries[-1]                        11.650000   0.800000  12.450000 ( 12.627440)
last = nil; r.each { |v| last = v }  20.750000   0.020000  20.770000 ( 20.910416)
r.max                                17.590000   0.010000  17.600000 ( 17.633006)
r.exclude_end? ? r.last.pred : r.last 0.000000   0.000000   0.000000 (  0.000062)
require 'benchmark'
N = 20
Benchmark.bm(30) do |r|
  past, now  = Time.local(2010, 2, 1, 0, 0, 0), Time.now
  @range = past...now
  r.report("range.max") do
    N.times { last_in_range = @range.max }
  end
  r.report("explicit enumeration") do
    N.times { @range.each { |value| last_in_range = value } }
  end
  r.report("range.entries.last") do
    N.times { last_in_range = @range.entries.last }
  end
  r.report("range.to_a[-1]") do
    N.times { last_in_range = @range.to_a[-1] }
  end
end
                                user     system      total        real
range.max                      49.406000   1.515000  50.921000 ( 50.985000)
explicit enumeration           52.250000   1.719000  53.969000 ( 54.156000)
range.entries.last             53.422000   4.844000  58.266000 ( 58.390000)
range.to_a[-1]                 49.187000   5.234000  54.421000 ( 54.500000)