【问题标题】:ruby enumerators: immediately skip multiple iterations (or start iterating from n)ruby 枚举器:立即跳过多次迭代(或从 n 开始迭代)
【发布时间】:2017-12-01 01:21:21
【问题描述】:

我正在迭代列表(18 项)的排列,如下所示:

List = [item0..item18] # (unpredictable)
Permutation_size = 7
Start_at = 200_000_000

for item, i in List.repeated_permutation(Permutation_size).each_with_index
  next if i < Start_at
  # do stuff
end

Start_at 用于从以前保存的状态恢复,所以它总是不同的,但几乎需要 200s 才能达到 2 亿,所以我想知道是否有更快的方法来跳过多次迭代或从迭代 n 开始(将枚举数转换为数组需要更长的时间)。如果没有,我们也将不胜感激创建自定义 repeated_permutation(n).each_with_index(产生相同顺序的结果)的方法。

请随时将我重定向到现有答案(我还没有找到任何答案)

附言。 (我想出的)

class Array
  def rep_per_with_index len, start_at = 0
    b = size
    raise 'btl' if b > 36
    counter = [0]*len
    # counter = (start_at.to_s b).split('').map {|i| '0123456789'.include?(i) ? i.to_i : (i.ord - 87)} #this is weird, your way is way faster
    start_at.to_s(b).chars.map {|i| i.to_i b}
    counter.unshift *[0]*(len - counter.length)
    counter.reverse!
    i = start_at
    Enumerator.new do |y|
      loop do
        y << [counter.reverse.map {|i| self[i]}, i]
        i += 1
        counter[0] += 1
        counter.each_with_index do |v, i|
          if v >= b
            if i == len - 1
              raise StopIteration
            else
              counter[i] = 0
              counter[i + 1] += 1
            end
          else
            break
          end
        end
      end
    end
  end
end

【问题讨论】:

  • list 的最大近似大小是多少?
  • 理想情况下,我想要一个通用解决方案,但现在大小始终为 18

标签: ruby next skip continue enumerator


【解决方案1】:

我首先构造了一个辅助方法change_base,带有三个参数:

  • off,在给定数组arr的重复排列序列中的base-10偏移量,
  • m,数字系统库;和
  • p,排列大小。

该方法执行三个步骤来构造数组off_m

  • off 转换为基数m(基数m);
  • 将基数m 值的数字分隔到一个数组中;和
  • 如有必要,使用前导 0s 填充数组,使其大小为 p

通过设置m = arr.sizeoff_m 的每个数字都是arr 的偏移量,因此off_m 将base-10 偏移量映射到大小为p 的唯一排列。

def change_base(m, p, off)
  arr = off.to_s(m).chars.map { |c| c.to_i(m) }
  arr.unshift(*[0]*(p-arr.size)) 
end

一些例子:

change_base(16, 2, 32)
  #=> [2, 0]
change_base(16, 3, 255)
  #=> [0, 15, 15]
change_base(36, 4, 859243)
  #=> [18, 14, 35, 31]
18*36**3 + 14*36**2 + 35*36**1 + 31  
  #=> 859243

change_base 的此实现需要 m &lt;= 36。我认为这已经足够了,但是可以使用算法将基数为 10 的数字转换为具有任意大基数的数字。

我们现在构造一个方法,它接受给定的数组arr、每个排列的大小p 和排列序列中的给定base-10 偏移量。该方法返回一个排列,即一个大小为p 的数组,其元素是arr 的元素。

def offset_to_perm(arr, p, off)
  arr.values_at(*change_base(arr.size, p, off))
end

我们现在可以用一个例子来试试这个。

arr = (0..3).to_a
p = 2

(arr.size**p).times do |off|
  print "perm for off = "
  print " " if off < 10
  print "#{off}: "
  p offset_to_perm(arr, p, off)
end

perm for off =  0: [0, 0]
perm for off =  1: [0, 1]
perm for off =  2: [0, 2]
perm for off =  3: [0, 3]
perm for off =  4: [0, 1]
perm for off =  5: [1, 1]
perm for off =  6: [2, 1]
perm for off =  7: [3, 1]
perm for off =  8: [0, 2]
perm for off =  9: [1, 2]
perm for off = 10: [2, 2]
perm for off = 11: [3, 2]
perm for off = 12: [0, 3]
perm for off = 13: [1, 3]
perm for off = 14: [2, 3]
perm for off = 15: [3, 3]

如果我们希望从偏移量 5 开始,我们可以这样写:

i = 5
p offset_to_perm(arr, p, i)
[1, 1]
i = i.next #=> 6
p offset_to_perm(arr, p, i)
[2, 1]
...

【讨论】:

  • 我也做了同样的事情(作为枚举器),但我的迭代速度比[...].repeated_permutation(n).with_index 慢约 6 倍,你测试过速度吗?
  • 我承认我直到现在才查看您的代码。是的,这些方法非常相似。由于您有工作代码,我建议您将其从问题移至答案(最好添加一些基准)。我没有做任何性能测试。
猜你喜欢
  • 2020-09-04
  • 1970-01-01
  • 2011-02-07
  • 2018-09-07
  • 2012-06-08
  • 1970-01-01
  • 2012-05-03
  • 2011-02-06
  • 2019-05-23
相关资源
最近更新 更多