【问题标题】:Non-repeating pseudo random number stream with 'clumping'具有“丛集”的非重复伪随机数流
【发布时间】:2010-11-28 12:36:09
【问题描述】:

我正在寻找一种方法来生成具有某种奇怪属性的伪随机流 - 我想要附近的数字团块。

棘手的部分是,无论范围有多大,我都只能保持有限的状态。有一些算法可以给出一系列具有最小状态的结果(线性同余?)

聚集意味着下一个数字接近而不是远的可能性更高。

理想序列示例(mod 10):1 3 9 8 2 7 5 6 4
我怀疑这在更大的流中会更明显,但很难手动进入。

更新:
我不明白为什么这是不可能的,但是,是的,我正在寻找,正如 Welbog 总结的那样:

  • 不重复
  • 非跟踪
  • “聚集”

【问题讨论】:

  • 如果您无法追踪您去过哪些号码,那么您就没有希望确保号码不会重复。 “结块”,就其本质而言,会导致碰撞。你不能同时拥有聚集、不跟踪和不重复这三者。
  • 不重复是否意味着该值必须与前一个元素不同。或者它应该在整个范围内都是独一无二的?因为如果是前者,那么您可以使用与上次迭代相同的种子调用随机生成器。你还想要整数还是浮点数?
  • 你不能同时拥有这三个,因为无状态分布必然允许重复。您需要值与以前的值保持接近这一事实使得它变得更加困难,因为您不能只使用任何非重复的混沌函数来实现它。如果您的域是实数,则可以使重复的可能性很小,但是如果不跟踪以前生成的内容,就不能使它们成为不可能。以你的例子为例,在不知道函数已经生成 1、3、9、8、2、7、5 和 6 的情况下,任何函数如何正确输出 4?它需要状态。
  • welbog - 声明很好(我在想有状态的 LCG)。只是状态必须相对于生成的流的大小保持不变(所以没有跟踪)
  • @OldCodeOrder:我想要一个算法来告诉我给定程序是否会因给定输入而停止。如果你能给我,我可以给你一个无记忆的非重复聚集随机分布。

标签: algorithm language-agnostic math random


【解决方案1】:

级联几个周期小于您需要的 LFSR,将它们组合起来以获得结果,例如变化最快的寄存器控制最低有效值。因此,如果 L1 的周期为 3,L2 的周期为 15,L3 的周期更长,则 N = L1(n) + 3 * L2(n/3) + 45 * L3(n/45)。这显然会生成 3 个聚集值,然后跳转并生成另外 3 个聚集值。使用除乘法以外的其他方法(例如混合较高周期寄存器的某些位)或不同周期,以使团块的传播范围比第一个寄存器的周期更宽。它不会特别顺利地随机,但它会是块状且不重复的。

【讨论】:

  • 如果我只是连接位,是什么阻止了重复?
  • 如果每个 LFSR 是级联的,所以下一个只有在前一个循环时才递增,那么组合的重复周期将是其他寄存器周期的乘积。
【解决方案2】:

郑重声明,我属于“非重复、非随机、非跟踪是一种致命的组合”阵营,我希望一些简单的实验能够有所启发。这无论如何都不是正式的证明。也许有人会支持它。

所以,我可以很容易地生成一个具有一些随机性的序列:

给定 x_i,x_(i+1) ~ U(x_i, r),其中 r > x_i。

例如:

如果 x_i = 6,x_(i+1) 是从 (6+epsilon, some_other_real>6) 中随机选择的。这保证了不重复,但代价是分布单调增加。

如果没有某些条件(如单调性),生成数字序列本身固有的,你怎么能保证唯一性没有携带状态?

编辑:所以在研究了 RBarryYoung 对“Linear Congruential Generators”的说法(不是区别……这就是 RBY 的意思)之后,很明显,我错了!这些序列存在,并且必然存在下一个编号仅取决于当前编号的任何 PRNG,并且某些全局的、不变的状态在一个周期内不能有重复(在一些初始烧毁期之后)。

【讨论】:

  • 线性同余微分器满足所有要求,除了聚集,没有问题。
【解决方案3】:

通过根据其大小的概率分布及其范围的概率分布来定义“聚类特征”,您可以使用简单的随机生成器与基础分布并生成序列。

【讨论】:

  • 这听起来很有希望。你能告诉我如何做到这一点的信息吗?如果我使用多个生成器,我会担心重叠(我无法跟踪我已经达到了哪些数字)
  • @OldcodeOrder:现在我看到了挑战。我认为原始问题中对“状态”的引用是关于尊重团块统计形状的必要状态。现在...如果您只需要唯一的数字,最好使用单个生成器,依靠自己的数学来确保唯一性...
【解决方案4】:

获得“块状”数字的一种方法是使用正态分布。

您从“初始”随机值开始随机列表,然后生成具有前一个随机值的平均值和恒定方差的随机数,并根据需要重复。整个随机数列表的总体方差应该大致保持不变,但数字的“运行平均值”会随机漂移,没有特别的偏差。

>>> r = [1]
>>> for x in range(20):
    r.append(random.normalvariate(r[-1], 1))
>>> r
[1, 0.84583267252801408, 0.18585962715584259, 0.063850022580489857, 1.2892164299497422, 
0.019381814281494991, 0.16043424295472472, 0.78446377124854461, 0.064401889591144235, 
0.91845494342245126, 0.20196939102054179, -1.6521524237203531, -1.5373703928440983, 
-2.1442902977248215, 0.27655425357702956, 0.44417440706703393, 1.3128647361934616, 
2.7402744740729705, 5.1420432435119352, 5.9326297626477125, 5.1547981880261782]

我知道通过查看数字很难分辨,但您可以在某种程度上看到数字有点聚集在一起 - 5.X 位于末尾,0.X 位于末尾在第二行。

如果您只需要整数,您可以使用非常大的均值和方差,然后截断/除法以获得整数输出。根据定义,正态分布是连续分布,这意味着所有实数都是潜在输出 - 它不限于整数。

这是 Excel 中以这种方式生成的 200 个数字的快速散点图(从 0 开始,恒定方差为 1):

scatter data http://img178.imageshack.us/img178/8677/48855312.png


啊,我刚刚读到你想要不重复的数字。在正态分布中无法保证这一点,因此您可能必须考虑其他人提到的其他一些方法。

【讨论】:

  • 为了放大,在很长一段时间内,大多数(好吧,我会飞跃!)正常生成器将有重复,因为在它们的尾部附近,它们不太接近均匀性并且具有不连续性。使其不重复(全局)和不跟踪是不可能的。
【解决方案5】:

我不知道现有的算法可以做到这一点,但推出自己的算法似乎并不难(取决于“有限数量的状态”要求的严格程度)。例如:

RANGE = (1..1000)
CLUMP_ODDS = .5
CLUMP_DIST = 10

last = rand(RANGE)
while still_want_numbers
  if rand(CLUMP_ODDS)   # clump!
    next = last + rand(CLUMP_DIST) - (CLUMP_DIST / 2)  # do some boundary checking here
  else   # don't clump!
    next = rand(RANGE)
  end
  print next
  last = next
end

这有点简陋,但这样的东西能满足您的需要吗?

【讨论】:

  • 我正在寻找更复杂的东西,因为这需要跟踪以避免重复,我没有状态。
  • 啊!我错过了你不想重复任何数字的部分。你是对的,那么,这行不通。
【解决方案6】:

在 [0, 10] 范围内,以下应给出均匀分布。 random() 产生一个(伪)随机数 r0 <= r < 1

x(n + 1) = (x(n) + 5 * (2 * random() - 1)) mod 10

您可以通过对 random() 进行去线性化来获得所需的行为 - 例如,random()^k 将偏向于 k > 1 的小数字。一个可能的函数可能如下,但您将不得不尝试一些指数来找到您想要的分布。如果您使用以下函数,请保持指数为奇数... ;)

x(n + 1) = (x(n) + 5 * (2 * random() - 1)^3) mod 10

【讨论】:

  • 这看起来很有希望,但是当我把一些东西放在一起时,它最终会重复。
  • 我知道 - 当我开始写答案时,您还没有指定序列必须是非重复的。
【解决方案7】:

怎么样(伪代码)

// clumpiness static in that value retained between calls
static float clumpiness = 0.0f; // from 0 to 1.0        
method getNextvalue(int lastValue)
   float r = rand();  // float from 0 to 1

   int change = MAXCHANGE * (r - 0.5) * (1 - clumpiness); 

   clumpiness += 0.1 * rand() ;
   if (clumpiness >= 1.0) clumpiness -= 1.0;
   // -----------------------------------------
   return Round(lastValue + change);

【讨论】:

    【解决方案8】:

    也许您可以生成一个随机序列,然后进行一些战略元素交换以获得所需的属性。

    例如,如果您在 a>ba 的序列中找到 3 个值 a,b,c >c,那么你可以交换元素 ab 或元素 ac.

    编辑回应评论:

    是的,您可以在流上使用您喜欢的任何大小的缓冲区。您的交换规则可以是确定性的,或者基于另一个已知的、可重现的伪随机序列。

    【讨论】:

    • 这需要是一个流,所以我看不到整个序列。这几乎似乎缓冲了少量值 - 我不确定这是否会产生正确的行为,或者它是如何可重复的
    【解决方案9】:

    是否像 0, 94, 5, 1, 3, 4, 14, 8, 10, 9, 11, 6, 12, 7, 16, 15, 17, 19, 22, 21, 20, 13 这样的序列, 18, 25, 24, 26, 29, 28, 31, 23, 36, 27, 42, 41, 30, 33, 34, 37, 35, 32, 39, 47, 44, 46, 40, 38, 50 , 43, 45, 48, 52, 49, 55, 54, 57, 56, 64, 51, 60, 53, 59, 62, 61, 69, 68, 63, 58, 65, 71, 70, 66, 73 , 67, 72, 79, 74, 81, 77, 76, 75, 78, 83, 82, 85, 80, 87, 84, 90, 89, 86, 96, 93, 98, 88, 92, 99, 95 , 97, 2, 91 (mod 100) 你觉得好看吗?

    这是一个小型 ruby​​ 程序的输出(解释如下):

    #!/usr/bin/env ruby
    
    require 'digest/md5'
    
    $seed = 'Kind of a password'
    $n = 100 # size of sequence
    $k = 10  # mixing factor (higher means less clumping)
    def pseudo_random_bit(k, n)
      Digest::MD5.hexdigest($seed + "#{k}|#{n}")[-1] & 1
    end
    
    def sequence(x)
      h = $n/2
      $k.times do |k|
        # maybe exchange 1st with 2nd, 3rd with 4th, etc
        x ^= pseudo_random_bit(k, x >> 1) if x < 2*h
        # maybe exchange 1st with last
        if [0, $n-1].include? x
          x ^= ($n-1)*pseudo_random_bit(k, 2*h)
        end
        # move 1st to end
        x = (x - 1) % $n
        # maybe exchange 1st with 2nd, 3rd with 4th, etc
        # (corresponds to 2nd with 3rd, 4th with 5th, etc)
        x ^= pseudo_random_bit(k, h+(x >> 1)) if x < 2*(($n-1)/2)
        # move 1st to front
        x = (x + 1) % $n
      end
      x
    end
    
    puts (0..99).map {|x| sequence(x)}.join(', ')
    

    这个想法基本上是从序列 0..n-1 开始,并通过在序列上传递 k 次来扰乱顺序(更多的传递意味着更少的聚集)。在每一遍中,首先查看位置 0 和 1、2 和 3、4 和 5 等处的数字对(一般:2i 和 2i+1),然后为每一对掷硬币。 Heads (=1) 表示交换对中的数字,tails (=0) 表示不交换它们。然后对位置 1 和 2、3 和 4 等处的对执行相同的操作(一般:2i+1 和 2i+2)。正如您提到的,您的序列是 mod 10,如果这对的硬币指示它,我还交换了位置 0 和 n-1。

    k 传递到区间 [x-k, x+k] 的任意数之后,单个数 x 可以模 n 映射并且近似二项式分布在 x 周围。数对 (x, x+1) 没有独立修改。

    作为伪随机生成器,我只使用了哈希函数 MD5 的 128 个输出位中的最后一个,请选择您想要的任何函数。多亏了聚集,一个人不会得到一个“安全”(=不可预测的)随机序列。

    【讨论】:

    • 如果序列看起来太单调增加:也可以交换数字块让序列跳得更多。
    【解决方案10】:

    也许您可以采用与此处描述的 LSFR 类似的方式将 2 个或更多 LCG 链接在一起。在整个周期中,用其种子增加最不重要的 LCG,增加下一个 LCG。您只需要为每个 LCG 存储一个种子。然后,您可以对每个部分进行加权并将这些部分相加。为避免“丛生”LstSig 部分中的重复,您可以在每个完整周期中随机重新播种 LCG。

    【讨论】:

      猜你喜欢
      • 2015-12-28
      • 1970-01-01
      • 2013-05-19
      • 1970-01-01
      • 2011-05-31
      • 1970-01-01
      • 2022-01-21
      相关资源
      最近更新 更多