这不是一个特别的红宝石问题:更多关于算法的一般性问题。但是可能有一些特定于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件物品,那就有点蠢事:我不能立刻想到一个很好的程序化方法。我觉得它必须已经被某人解决了。有什么建议?
答案 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的答案,算法有两个主要区别:
“步幅”是使用(length + (length / n) - 1).to_f / n
计算的,其中n
是所需的项目数。根据{{1}}进入n
的次数计算偏移量可确保始终包含最后一项。
它使用模运算而不是递增:如果元素的索引除以“stride”的余数在0和1之间(包括0,不包括1),则元素包含在结果中。 length
始终为0的事实确保始终返回第一个元素。
考虑边缘情况,例如当元素数量少于所需数量时。
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
对象,您可以随机选择数组元素def spread(arr, count) do
arr.sample(count, Random.new(0))
end