具有“聚集”的非重复伪随机数流

时间:2009-09-15 16:05:16

标签: algorithm language-agnostic math random

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

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

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

期望序列的实例(模式10):1 3 9 8 2 7 5 6 4
我怀疑使用更大的流会更加明显,但很难手工输入。

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

  • 不重复
  • 非跟踪
  • “Clumped”

10 个答案:

答案 0 :(得分:5)

级联一些周期小于您需要的LFSR,将它们组合起来得到的结果比最快改变的寄存器控制最不重要的值。因此,如果您具有周期3的L1,具有周期15的L2和具有更大周期的L3,则N = L1(n)+ 3 * L2(n / 3)+ 45 * L3(n / 45)。这显然会产生3个丛集值,然后跳跃并且通常会产生另外3个丛集值。使用除乘法之外的其他内容(例如混合较高周期寄存器的某些位)或不同的周期,以使clump扩展比第一个寄存器的周期更宽。它不会特别顺利随机,但它会变得笨重而且不重复。

答案 1 :(得分:1)

为了记录,我在“非重复,非随机,非跟踪是一个致命的组合”阵营,我希望一些简单的虽然实验会有所启发。这不是任何方式的正式证明。也许有人会支持它。

因此,我可以轻松生成一个具有一些随机性的序列:

给定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的下一个数字仅依赖于当前数字,而某些全局非变化状态不能在一个周期内重复(在一些初始刻录之后)。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

例如,如果您在序列中找到3个值 a,b,c ,则 a > b a > c ,然后有可能你可以交换元素 a b 或元素 a c

编辑以回应评论:

是的,您可以在流上设置一个缓冲区,无论您感觉满意。您的交换规则可能是确定性的,或者基于另一个已知的,可重现的伪随机序列。

答案 4 :(得分:0)

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

使用“初始”随机值启动随机列表,然后生成一个随机数,其中包含前一个随机值的均值和一个常数方差,并根据需要重复。整个随机数列表的总体方差应该近似不变,但数字的“平均值”会随机漂移,没有特别的偏差。

>>> 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 :(得分:0)

我不知道现有的算法可以做到这一点,但看起来并不困难(取决于“有限的状态”要求有多严格)。例如:

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)

怎么样(伪代码)

// 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);

答案 7 :(得分:0)

在[0,10]范围内,以下应给出均匀分布。 random()会产生一个带有r的(伪)随机数0 <= 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

答案 8 :(得分:0)

序列如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)对你好看吗?

这是一个小红宝石程序的输出(下面的解释):

#!/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)并翻转每对的硬币。头(= 1)表示交换对中的数字,尾(= 0)表示不交换它们。然后对位置1和2,3和4等处的对做同样的事情(一般:2i + 1和2i + 2)。正如你所提到的那样你的序列是mod 10,如果这对的硬币决定了它,我还会交换位置0和n-1。

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

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

答案 9 :(得分:0)

也许您可以按照此处描述的LSFR描述的类似方式将2个或更多个LCG链接在一起。在整个循环中用最少重要的LCG增加下一个LCG。您只需为每个LCG存储种子。然后,您可以对每个零件进行加权并将零件加在一起。为了避免在'clumped'LstSig部分中重复出现,您可以在每个完整周期中随机重新定位LCG。