【问题标题】:Ruby array arranged in ascending orderRuby数组按升序排列
【发布时间】:2019-04-14 05:09:14
【问题描述】:

您好,我正在制作一个简单的函数来按升序生成浮点数组,但我想知道是否有更简单的方法可以做到这一点。 我将使用它来创建一个表格,这样我就可以生成随机数学问题来通过拉格朗日数值方法解决它们。

拉格朗日法使用X、F(X)和X0,其中X0必须在X的范围内,否则无法解决问题

到目前为止,这是我的代码

xn = []
#i is a random number selected from a list
i.times do
    value=rand(0.1..15.1)
    xn<<value 
end
#ordena el arreglo de manera ascendente
xn.sort!

编辑:更新代码

【问题讨论】:

  • 您标记此ruby-on-rails 有什么特别的原因吗?我在这里看到的是纯 Ruby,没有导轨。

标签: arrays ruby sorting random


【解决方案1】:

正如 Sid 所指出的,当您问“是否有更简单的方法可以做到这一点”时,很大程度上取决于上下文。如果您通过“更简单”来寻找效率,那么答案不仅仅是代码行数。

如果您真的想将所有订单统计信息作为一个集合查看,那么使用 Ruby 的块构造函数功能将您的方法转换为单线可能更具可读性,并且下面提供的实现的时间显示它稍微快一些,但仅百分之几。

但是,如果您有一个非常大的 N 并按顺序处理值,您可能更喜欢生成器方法。下面以ordered_random_generator 实现的一个是每个元素的 O(1) 计算和存储,因此生成整个集合需要 O(N),但如果在使用后丢弃每个元素,则需要 O(1) 存储。如果存储元素,实际上这比基于排序的方法要慢,因为在计算中评估 kth 个根的成本很高。

另一种可能性是您实际上对整组值并不感兴趣,而是使用它来获取特定的分位数或顺序统计信息,例如 100 个有序元素中的第 10 个。在这种情况下,您可以在 O(1) 时间内使用正确的 distribution of the kth order statistic out of N 直接生成一个随机值,并使用 O(1) 存储。

这是各种选项的实现,显示了生成整个数组的三种方法的时间安排,并显示了第四种方法的分布有效性。

# Your original.
# O(N log N) time due to sort, O(N) storage.
def ordered_random1(n, range_spec = 1.0..n)
  ary = []
  n.times do
    value = rand(range_spec)
    ary << value
  end
  ary.sort!
end

# Same sort-based algorithm using Ruby's Array constructor with a block.
# O(N log N) time due to sort, O(N) storage.
def ordered_random2(n, range_spec = 1.0..n)
  Array.new(n) { rand(range_spec) }.sort!
end

# Generator which uses distributional properties to generate the minimum of
# k uniforms from k = N down to 1, scaling the result down to the remaining
# sub-range of the original range.
# O(1) time per element, O(1) storage.  However, computation time has a very
# large constant due to the transcendental function evaluation for kth root.
def ordered_random_generator(n, range_spec = 1.0..n)
  x = range_spec.first
  upper = range_spec.last
  Enumerator.new do |yielder|
    n.times do |i|
      range = upper - x
      u_min = 1.0 - rand ** (1.0 / (n - i))
      x += range * u_min
      yielder.yield x
    end
  end
end

# Use generator to fill array of size N.
# O(N) time and storage.
def ordered_random3(n, range_spec = 1.0..n)
  gen = ordered_random_generator(n, range_spec)
  Array.new(n) { gen.next }
end

require 'random_variates'   # 'gem install random_variates' to get from rubygems

# Use distributional properties of uniform order statistics to directly
# generate instances of the kth of N values.
# O(1) time, O(1) storage.
def kth_of_n_generator(k:, n:, range_spec: 0.0..1.0)
  # Uniform order stats have a beta distribution. Beta is a ratio of Gammas.
  x = Gamma.new(alpha: k).next
  y = Gamma.new(alpha: n - k + 1).next
  beta = x / (x + y)
  (range_spec.last - range_spec.first) * beta + range_spec.first
end

# Time for Demos!
my_range = 0.1..15.1
puts "SAMPLE OUTPUT FOR RANGE = #{my_range}:"
puts " original: #{ordered_random1(5, my_range)}"
puts "one-liner: #{ordered_random2(5, my_range)}"
puts "generator: #{ordered_random3(5, my_range)}"
puts "direct generation of min & max using kth_of_n_generator: #{
  kth_of_n_generator(k: 1, n: 5, range_spec: my_range)
}, #{
  kth_of_n_generator(k: 5, n: 5, range_spec: my_range)
}"

REPS = 10_000
n = 9
puts "\nDEMO DISTRIBUTIONAL CORRECTNESS OF SINGLETON GENERATOR (range = 0.0..1.0)"
(1..n).each do |k|
  total = Array.new(REPS) { kth_of_n_generator(k: k, n: n) }.inject(:+)
  quantile = k.to_f / (n + 1)
  suffix = case k
    when 1
      "st"
    when 2
      "nd"
    when 3
      "rd"
    else
      "th"
  end
  print "Average of #{REPS} values of #{k}#{suffix} of #{n}: #{total / REPS} "
  puts "[Expected value is #{quantile}]"
end

require 'benchmark/ips'
[100, 10_000].each do |n|
  puts "\nBENCHMARKING ARRAYS OF SIZE #{n}"
  Benchmark.ips do |b|
    b.report(' original:') { ordered_random1(n, my_range) }
    b.report('one-liner:') { ordered_random2(n, my_range) }
    b.report('generator:') { ordered_random3(n, my_range) }
    b.compare!
  end
end

这是我机器上的输出示例。您的时间将根据您的硬件、操作系统和运行的 Ruby 版本而有所不同。由于随机性,每次运行的具体值会有所不同,但都是一致的。

SAMPLE OUTPUT FOR RANGE = 0.1..15.1:
 original: [3.2143763318277223, 3.424117583339602, 4.98763316107166, 7.67915049946293, 13.002051529711663]
one-liner: [3.698584735327408, 3.7940473868424713, 8.133265097991108, 10.797493427133121, 13.519291528088747]
generator: [1.379949057529254, 3.330310564043854, 14.175279996588, 14.187770450655005, 14.747374304212487]
direct generation of min & max using kth_of_n_generator: 2.3844682728553956, 14.093371351681753

DEMO DISTRIBUTIONAL CORRECTNESS OF SINGLETON GENERATOR (range = 0.0..1.0)
Average of 10000 values of 1st of 9: 0.10061353514079374 [Expected value is 0.1]
Average of 10000 values of 2nd of 9: 0.19841217568287062 [Expected value is 0.2]
Average of 10000 values of 3rd of 9: 0.3018753486695847 [Expected value is 0.3]
Average of 10000 values of 4th of 9: 0.40002514960574265 [Expected value is 0.4]
Average of 10000 values of 5th of 9: 0.5003591617651723 [Expected value is 0.5]
Average of 10000 values of 6th of 9: 0.5974291957317844 [Expected value is 0.6]
Average of 10000 values of 7th of 9: 0.6980418879340753 [Expected value is 0.7]
Average of 10000 values of 8th of 9: 0.8012294219961899 [Expected value is 0.8]
Average of 10000 values of 9th of 9: 0.9002379495094114 [Expected value is 0.9]

BENCHMARKING ARRAYS OF SIZE 100
Warming up --------------------------------------
           original:     4.037k i/100ms
          one-liner:     4.242k i/100ms
          generator:   773.000  i/100ms
Calculating -------------------------------------
           original:     40.412k (± 2.0%) i/s -    205.887k in   5.096825s
          one-liner:     41.852k (± 2.3%) i/s -    212.100k in   5.070662s
          generator:      7.676k (± 4.2%) i/s -     38.650k in   5.045488s

Comparison:
          one-liner::    41852.1 i/s
           original::    40412.3 i/s - same-ish: difference falls within error
          generator::     7675.6 i/s - 5.45x  slower


BENCHMARKING ARRAYS OF SIZE 10000
Warming up --------------------------------------
           original:    29.000  i/100ms
          one-liner:    30.000  i/100ms
          generator:     7.000  i/100ms
Calculating -------------------------------------
           original:    295.387  (± 2.0%) i/s -      1.479k in   5.009243s
          one-liner:    304.406  (± 2.0%) i/s -      1.530k in   5.028485s
          generator:     78.104  (± 2.6%) i/s -    392.000  in   5.020934s

Comparison:
          one-liner::      304.4 i/s
           original::      295.4 i/s - same-ish: difference falls within error
          generator::       78.1 i/s - 3.90x  slower

请注意,对于此处测试的两种数组大小,生成器方法比两种基于排序的方法要慢。由于 O(N) 与 O(N log N) 的渐近行为,较大数组大小的差距正在缩小,但如果您的主要关注点是速度,则可能不足以引起人们的兴趣。

【讨论】:

    【解决方案2】:

    代码本身看起来不错,可以满足您的目的。是否有上下文将在哪里使用?因为“简单”可能是由代码的上下文驱动的。

    例如,

    1. 您可以配置所需的随机数数量和随机数范围。您可以在外部配置排序顺序。

    2. 您可以将其封装在实用程序类中,并像 API 一样将其公开给其他类。

    3. 如果需要一百万个随机排序的数字,您希望这些数字流式传输吗?如果是这样,就有 Ruby 库。

    ...等等。上下文将很有用。希望这可以帮助。

    【讨论】:

    • 我想把它变成一个函数,因为我会再次使用它,所以我可以创建一个包含 X 和 F(X) 值的表,我添加了更多关于像你这样的问题的信息说谢谢!
    • 很好,希望答案对您有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-11
    • 1970-01-01
    • 2021-04-22
    • 2018-08-11
    • 1970-01-01
    相关资源
    最近更新 更多