在固定大小的数组上扩展选择的算法

时间:2012-08-10 14:32:20

标签: ruby

这不是一个特别的红宝石问题:更多关于算法的一般性问题。但是可能有一些特定于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件物品,那就有点蠢事:我不能立刻想到一个很好的程序化方法。我觉得它必须已经被某人解决了。有什么建议?

5 个答案:

答案 0 :(得分: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] 

答案 1 :(得分:2)

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

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

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

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


  4. 0 % x

答案 2 :(得分:1)

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

假设您有100个项目并且您想要选择30.然后您的“步幅”将是3.3333 ...所以您从3.3333的“运行总计”开始,然后选择项目3.然后6.66666 - 所以您选择项目6.接下来是10.0 - 所以你选择项目10.依此类推......

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

答案 3 :(得分:1)

这个here有一个类似的问题,但解决方案是在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]

答案 4 :(得分:-1)

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

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

例如Array#sample

def spread(arr, count) do
    arr.sample(count, Random.new(0))
end