一尘不染

如何在不预先生成整个序列的情况下生成可预测的序列改组?

algorithm

以下python代码准确描述了我要为任意大小(填充)的序列实现的目标:

import random
fixed_seed = 1 #generate the same sequence every time with a fixed seed
population = 1000
sample_count = 5 #demonstration number
num_retries = 3  #just enough to show the repeatable behaviour
for trynum in xrange(num_retries):
    #generate the fresh/ordered sequence (0->population)...
    seq = range(population)
    #seed the random number generator the same way every time...
    random.seed(fixed_seed)
    #shuffle the sequence...
    random.shuffle(seq)
    #display results for this try...
    sample_sequence = [str(x) for x in seq[:sample_count]]
    print "try %s: %s..." % (trynum + 1, ", ".join(sample_sequence))
#Sample output...
#try 1: 995, 721, 62, 326, 541...
#try 2: 995, 721, 62, 326, 541...
#try 3: 995, 721, 62, 326, 541...

该方法的问题在于它需要首先在内存中生成整个序列。对于庞大的人口来说,这可能是个问题。

请注意,此方法的潜在一大优势是您可以随时选择 任何 阵列位置。

现在-
如果碰巧遇到的问题使您将总体大小设置为2的幂(负1),则可以使用线性反馈移位寄存器来获得可预测的随机序列。LFSR很整齐,并且在有关它们的维基百科文章中作了很好的解释。

下面的python代码演示了这一点(并且我进行了一堆唯一性测试,以确保它能够如所宣传的那样工作)。再次参见Wikipedia文章,以获取有关代码工作方式的解释(Galois配置)。

TAP_MASKS = { #only one needed, but I included 3 to make the code more useful
    10: 0x00000240, #taps at 10, 7
    16: 0x0000B400, #taps at 16, 14, 13, 11
    32: 0xE0000200, #taps at 32, 31, 30, 10
}

def MaxLengthLFSR(seed, register_length):
    "Gets next value from seed in max-length LFSR using Galois configuration."
    lsb = seed & 1
    next_val = seed >> 1
    if lsb == 1:
        mask = TAP_MASKS[register_length]
        next_val ^= mask
    return next_val

reglen = 16  #number of bits in register
population = (2**reglen) - 1 #not used, just showing it
fixed_seed = 1   #seed == startval in this case (could randomize in population)
sample_count = 5 #demonstration number
num_retries = 3  #just enough to show the repeatable behaviour
for trynum in xrange(num_retries):
    next_val = fixed_seed
    seq = [fixed_seed, ]
    for x in xrange(sample_count - 1):
        next_val = MaxLengthLFSR(next_val, reglen)
        seq.append(next_val)
    seq = [str(x) for x in seq]
    print "try %s: %s..." % (trynum + 1, ", ".join(seq))
#Sample output...
#try 1: 1, 46080, 23040, 11520, 5760...
#try 2: 1, 46080, 23040, 11520, 5760...
#try 3: 1, 46080, 23040, 11520, 5760...

这很好,因为您可以拥有庞大的种群,并且无需使用大量内存即可轻松计算可重复的非重复随机数序列。

缺点是a)限于大小为(2 **
N-1)的“混洗”序列,以及b)您无法确定随机序列中特定位置的值在任意位置的值。您需要知道特定点的值,然后从那里开始执行序列。

后者(b)在大多数情况下都是可以的,因为大多数时候您会按顺序生成序列,因此您只需要记住最后一个值即可。2局限性(a)的力量是一种交易杀手,但是…取决于应用程序。

对于任意序列长度,如何实现类似最大长度LFSR的非重复结果?

另外,有一个解决方案很好,您可以知道给定序列位置的数字而无需遍历该序列到该位置。


注意:如果您想为最大长度的LFSR(一个生成整个寄存器群而无需重复一次)的LFSR抽头位置提供一个好的开始集,则此链接非常好,并且每个寄存器大小都有很多抽头位置(最大还是32位)。

还请注意,我已经看到许多问题与我的问题和改组/ LFSR紧密相关,但是它们都与我所追求的不完全相关(任意大小的线性序列的可预测改组)。或至少据我所能理解。

我最近一直在研究线性同余生成器,它看起来似乎很有前途,但是我还无法使它们工作。我将不再问这个问题,而是问它,如果我发现问题并且工作了,则发布答案。


阅读 248

收藏
2020-07-28

共1个答案

一尘不染

我之前实际上已经写过:使用分组密码进行安全置换。简而言之:

  1. 是的,您可以使用LFSR生成长度为2的幂的置换。您还可以使用任何分组密码。使用分组密码,您还可以在索引n或元素n的索引处找到该元素。
  2. 要生成长度为l的任意排列,请创建一个长度小于2的最小幂次。当您要查找第n个置换元素时,请应用置换函数,如果生成的数字超出所需范围,请再次应用;重复此操作,直到该数字在可接受的范围内。

第2步所需的迭代次数平均不超过2;最坏的情况是很高,但极不可能发生。

2020-07-28