1

这不是一个特别的红宝石问题:更多的是关于算法的一般问题。但是可能有一些特定于 ruby​​ 的数组方法很有帮助。

我有一个包含 30 个项目的数组。我要求在 15 到 30 之间的项目数,并且我想从整个数组中选择给定数量的项目,使其尽可能均匀分布。选择需要是非随机的,每次都返回相同的结果。

假设有人要 16 件商品。如果我返回前 16 个,那将是一个巨大的失败。相反,我可以返回所有奇数加上最后一个;如果我将数字 1 到 30 存储在数组中,我可以回馈

myArr.spread(16)
=> [1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,30]

如果有人要 20 件商品,那就有点诡计了:我想不出一个好的程序化方式来做这件事。我觉得它一定已经被某人解决了。有什么建议么?

4

5 回答 5

3

我最终这样做了,受到 Alex D 的启发:我一步步完成 n-1 次,然后总是将最后一个元素添加到末尾。

class Array
  def spread(n)
    step = self.length.to_f / (n -1) 
    (0..(n-2)).to_a.collect{|i| self[i * step]} + [self.last]
  end
end

 > (1..30).to_a.spread(3)
 => [1, 16, 30] 
 > (1..30).to_a.spread(4)
 => [1, 11, 21, 30] 
 > (1..30).to_a.spread(5)
 => [1, 8, 16, 23, 30] 
 > (1..30).to_a.spread(15)
 => [1, 3, 5, 7, 9, 11, 13, 16, 18, 20, 22, 24, 26, 28, 30] 
于 2012-08-13T13:02:16.920 回答
2

最近实现了这个方法——尽管我称之为keep——用于备份保留应用程序,我想我会分享我的解决方案。它类似于 Alex D 的答案,但算法有两个主要区别:

  1. “步幅”是使用(length + (length / n) - 1).to_f / nwheren是所需的项目数来计算的。n根据进入的次数计算偏移量length可确保始终包含最后一项。

  2. 它使用模运算而不是递增:如果元素的索引除以“步幅”的余数在 0 和 1 之间(包括 0,不包括 1),则该元素包含在结果中。始终为 0的事实0 % x确保始终返回第一个元素。

  3. 考虑到边缘情况,例如当元素数量少于所需数量时。


class Array
  def keep(n)
    if n < 1
      []
    elsif length <= n
      self.clone
    else
      stride = (length + (length / n) - 1).to_f / n
      select.with_index do |_, i|
        remainder = i % stride
        (0 <= remainder && remainder < 1)
      end
    end
  end
end
于 2013-10-22T22:40:10.367 回答
1

将数组的大小除以您要选择的项目数(不要使用截断除法)——这将是您在数组上行走时选择项目时的“步幅”。继续将“步幅”添加到运行总计中,直到它等于或超过数组的大小。每次添加“步幅”时,取整数部分并将其用作数组的索引以选择一个项目。

假设您有 100 个项目,并且您想选择 30 个。那么您的“步幅”将为 3.3333 ......所以您从 3.3333 的“运行总数”开始,然后选择项目 3。然后是 6.66666 - 所以您选择项目 6。接下来是 10.0 -- 所以你选择项目 10。等等......

测试以确保您不会出现“减一”错误,并且如果数组大小或要选择的项目数为零,您也不会除以零。还使用保护子句来确保要选择的项目数不大于数组中的数字。

于 2012-08-10T15:08:34.733 回答
1

这里有一个类似的问题但解决方案是在 python 中。

在 Ruby 中,它将是:

class Array
    def spread( count)
        length = self.length
        result = Array.new
        0.upto(count-1) do |i|
            result << self[(i * length.to_f / count).ceil]
        end
        return result
    end
end

arr = Array(1..30)
puts arr.spread(20)
#=> [1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30]
于 2012-08-10T15:23:37.687 回答
-1

您可以尝试使用具有固定种子的Random (doc) :

  • 使用Random对象,您可以随机选择数组的元素
  • 固定种子确保每次调用函数都会生成随机数列表。

例如与 Array#sample

def spread(arr, count) do
    arr.sample(count, Random.new(0))
end
于 2012-08-10T14:52:11.730 回答