我正在寻找一种方法来生成一个有点奇怪属性的伪随机流 - 我想要附近的数字丛。
棘手的部分是,无论范围多大,我都只能保持有限的状态。有些算法可以给出一系列具有最小状态的结果(线性同余?)
聚集意味着下一个数字接近而不是远的概率更高。
期望序列的实例(模式10):1 3 9 8 2 7 5 6 4
我怀疑使用更大的流会更加明显,但很难手工输入。
更新:
我不明白为什么这是不可能的,但是,我正在寻找,正如Welbog所总结的那样:
答案 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。