【问题标题】:Generating a repeatable pseudo-random number based coordinates生成可重复的基于伪随机数的坐标
【发布时间】:2011-05-23 14:10:10
【问题描述】:

我需要根据一组坐标生成可重复的伪随机数,这样对于给定的种子,我将始终为特定坐标生成相同的值。

我想我会为种子使用这样的东西:

/* 64bit seed value*/
struct seed_cord {
    uint16 seed;
    uint16 coord_x_int;
    uint16 coord_y_int;
    uint8  coord_x_frac;
    uint8  coord_y_frac;
}

其中coord_x_int是坐标的整数部分,小数部分由coord_x_frac / 0xFF给出。 seed 是一个随机预先确定的值。

但我必须承认,试图理解 PRNG 的所有复杂性有点让人不知所措。什么是我正在尝试的好生成器?

我在一个快速的 groovy 脚本中使用此方案测试了 Java 的 PRNG,结果如下:

显然,这不是像样的随机性。

我使用的脚本是:

import java.awt.image.BufferedImage
import javax.imageio.ImageIO

short shortSeed = new Random().next(16) as short

def image = new BufferedImage(512, 512, BufferedImage.TYPE_BYTE_GRAY)
def raster = image.getRaster()

//x
(0..1).each{ x ->
(0..255).each{ xFrac ->
//y
(0..1).each{ y ->
(0..255).each{ yFrac ->

long seed = (shortSeed as long) << 48 |
            (x as long)         << 32 |
            (y as long)         << 16 |
            (xFrac as long)     <<  8 |
            (yFrac as long)

def value = new Random(seed).next(8)
raster.setSample( (x? xFrac+256 : xFrac), (y? yFrac+256 : yFrac), 0 , value)

}}}}

ImageIO.write(image, "PNG", new File("randomCoord.png"))

【问题讨论】:

  • 你的问题有点含糊。您似乎在要求哈希函数,但您在谈论随机数。为什么它们必须是随机但可重复的?
  • 我打算将它用于程序生成。所以我需要为一个坐标生成一个随机分量,但只要生成器使用相同的主种子,我就需要每次为该坐标生成相同的结果。
  • 您可以从 JavaScript 中的字符串生成随机数,并且可以使用 javax.script 从 Java 调用 JavaScript 函数。 stackoverflow.com/questions/13964762/…

标签: algorithm random prng


【解决方案1】:

如果你真的只看 512x512,那么这就是呃……你感兴趣的 218 像素。

对于具有良好 ole MD5(128 位输出)的这类人群来说,有足够的空间。

如果这是您需要的输出类型,您可以只取整数的最低 32 位。实际上,任何类型的散列算法只要输出空间至少与 int 一样大。

现在,如果你偏执,你可以做各种有趣的事情。从您的坐标散列开始,然后将结果输入安全随机数生成器 (java.security.SecureRandom)。然后用你的生日连接 (x+y) 次的盐将其哈希 1000 次。

除了开玩笑,随机数生成器不一定会根据种子的微小变化而产生巨大变化的结果。它们的设计目的是在它们开始重复之前有一个非常、超级长的生成数字链,同时让这些链在数字空间中相当均匀地分布。

另一方面,SecureRandom 被设计为具有关于种子的混乱的附加特性。

【讨论】:

  • 可视化是坐标 [0..2),[0..2) 与 128:1 点的渲染。对于实际实现,我正在查看更大坐标集中的任意坐标,以及不同数量的点。
  • 好吧,即使您正在查看大于 2^128 的空间,散列也能很好地发挥作用,因为它已经比普通整数大了。
  • 可以在此处找到将 MD5 哈希转换为整数的方法:stackoverflow.com/questions/1779879/…
【解决方案2】:

大多数语言都有一个(或两个)PRNG 包,可让您使用特定种子初始化生成器。 PRNG 通常也可以作为更大加密包的一部分找到;它们往往比通用库中的更强大。

【讨论】:

  • 我担心的是,当我使用一系列几乎连续的种子时,开箱即用的解决方案会给我带来不错的随机性吗?
  • 我认为每个开箱即用的软件包在这方面都会做得很好。我相信,对种子的小改动总会给你生成的序列带来很大的变化。
  • 显然 Java 没有。我在问题中附加了一个测试用例。
  • 这令人不安。如果您重新组织脚本以生成单个 Random 对象并通过为每个像素调用 next(8) 来填充图像,会发生什么情况?假设它的行为更加随机,我唯一可以建议的是使用 java.security.SecureRandom 而不是 java.util.Random。
  • 使用普通的 Random 对象按预期工作,产生看似随机的噪声模式。
【解决方案3】:

我会采用我创建的这样一个程序,然后对其进行修改以选择坐标:

REM $DYNAMIC
COMMON SHARED n%, rbuf%, sz%, sw%, p1$
DECLARE SUB initialize ()
DECLARE SUB filbuf ()
DECLARE SUB setup ()
DECLARE FUNCTION Drnd# ()
DECLARE SUB core ()
DECLARE SUB modify ()
DIM SHARED pad1(340) AS STRING * 1
DIM SHARED trnsltr(66) AS STRING * 1 ' translates a 0-67 value into a pad character
DIM SHARED trnslt(255) AS INTEGER  'translates a pad value to 0-67 value -1 if error
DIM SHARED moders(26) AS INTEGER 'modding function prim number array
DIM SHARED moders2(26) AS INTEGER 'modding function prim number array
DIM SHARED ranbuf(1 TO 42) AS DOUBLE 'random number buffer if this is full and rbuf %>0
REM then this buffer is used to get new random values
REM rbuf% holds the index of the next random number to be used
REM subroutine setup loads the prime number table
REM from the data statements to be used
REM as modifiers in two different ways (or more)
REM subroutine initialize primes the pad array with initial values
REM transfering the values from a string into an array then
REM makes the first initial scrambling of this array
REM initializing pad user input phase:
CLS
INPUT "full name of file to be encrypted"; nam1$
INPUT "full name of output file"; nam2$
INPUT "enter password"; p2$
rbuf% = 0
n% = 0: sw% = 0
p3$ = STRING$(341, "Y")
p1$ = "Tfwd+-$wiHEbeMN<wjUHEgwBEGwyIEGWYrg3uehrnnqbwurt+>Hdgefrywre"
p1$ = p2$ + p1$ + p3$
PRINT "hit any key to continue any time after a display and after the graphic display"
p1$ = LEFT$(p1$, 341)
sz% = LEN(p1$)
CALL setup
CALL initialize
CLS
ibfr$ = STRING$(512, 32)
postn& = 1
OPEN nam1$ FOR BINARY AS #1
OPEN nam2$ FOR BINARY AS #2
g& = LOF(1)
max& = g&
sbtrct% = 512
WHILE g& > 0
LOCATE 1, 1
PRINT INT(1000 * ((max& - g&) / max&)) / 10; "% done";
IF g& < 512 THEN
ibfr$ = STRING$(g&, 32)
sbtrct% = g&
END IF
GET #1, postn&, ibfr$
FOR ste% = 1 TO LEN(ibfr$)
geh% = INT(Drnd# * 256)
MID$(ibfr$, ste%, 1) = CHR$(geh% XOR ASC(MID$(ibfr$, ste%, 1)))
NEXT ste%
PUT #2, postn&, ibfr$
postn& = postn& + sbtrct%
g& = g& - sbtrct%
WEND
CLOSE #2
CLOSE #1
PRINT "hit any key to exit"
i$ = ""
WHILE i$ = "": i$ = INKEY$: WEND
SYSTEM
END
DATA 3,5,7,9,11,13,17,19
DATA 23,29,33,37,43,47
DATA 53,59,67,71,73,79,83
DATA 89,91,97,101,107,109
DATA 43,45,60,62,36

REM $STATIC
SUB core
REM shuffling algorythinm
FOR a% = 0 TO 339
m% = (a% + 340) MOD 341: bez% = trnslt(ASC(pad1(340)))
IF n% MOD 3 = 0 THEN pad1(340) = trnsltr((2 * trnslt(ASC(pad1(a%))) + 67 -   trnslt(ASC(pad1(m%)))) MOD 67)
IF n% MOD 3 = 1 THEN pad1(340) = trnsltr((2 * (67 - trnslt(ASC(pad1(a%)))) + 67 - trnslt(ASC(pad1(m%)))) MOD 67)
IF n% MOD 3 = 2 THEN pad1(340) = trnsltr(((2 * trnslt(ASC(pad1(a%))) + 67 - trnslt(ASC(pad1(m%)))) + moders(n% MOD 27)) MOD 67)
pad1(a% + 1) = pad1(m%): n% = (n% + 1) MOD 32767
pad1(a%) = trnsltr((bez% + trnslt(ASC(pad1(m%)))) MOD 67)
NEXT a%
sw% = (sw% + 1) MOD 32767
END SUB

FUNCTION Drnd#
IF rbuf% = 0 THEN
CALL core
CALL filbuf
IF sw% = 32767 THEN CALL modify
END IF
IF rbuf% > 0 THEN yut# = ranbuf(rbuf%)
rbuf% = rbuf% - 1
Drnd# = yut#
END FUNCTION

SUB filbuf
q% = 42: temp# = 0
WHILE q% > 0
FOR p% = 1 TO 42
k% = (p% - 1) * 8
FOR e% = k% TO k% + 7
temp# = temp# * 67: hug# = ABS(trnslt(ASC(pad1(e%)))): temp# = temp# + hug#
NEXT e%
IF temp# / (67 ^ 8) >= 0 AND q% < 43 THEN
ranbuf(q%) = temp# / (67 ^ 8): q% = q% - 1
END IF
temp# = 0
NEXT p%
WEND
rbuf% = 42
END SUB

SUB initialize
FOR a% = 0 TO 340
pad1(a%) = MID$(p1$, a% + 1, 1)
NEXT a%
FOR a% = 0 TO 340
LOCATE 1, 1
IF a% MOD 26 = 0 THEN PRINT INT((340 - a%) / 26)
sum% = 0
FOR b% = 0 TO 340
qn% = INT(Drnd# * 81)
op% = INT(qn% / 3)
qn% = qn% MOD 3
IF qn% = 0 THEN sum% = sum% + trnslt(ASC(pad1(b%)))
IF qn% = 1 THEN sum% = sum% + (67 + 66 - trnslt(ASC(pad1(b%)))) MOD 67
IF qn% = 2 THEN sum% = sum% + trnslt(ASC(pad1(b%))) + moders(op%)
NEXT b%
pad1(a%) = trnsltr(sum% MOD 67)
NEXT a%
n% = n% + 1
END SUB

SUB modify
REM modifier shuffling routine
q% = 26
temp# = 0
WHILE q% > -1
FOR p% = 1 TO 27
k% = (p% - 1) * 4 + 3
FOR e% = k% TO k% + 3
temp# = temp# * 67
hug# = ABS(trnslt(ASC(pad1(e%))))
temp# = temp# + hug#
NEXT e%
IF (temp# / (67 ^ 4)) >= 0 AND q% > -1 THEN
SWAP moders(q%), moders(INT(27 * (temp# / (67 ^ 4))))
q% = q% - 1
END IF
temp# = 0
NEXT p%
WEND
END SUB

SUB setup
FOR a% = 0 TO 26
READ moders(a%)
moders2(a%) = moders(a%)
NEXT a%
REM setting up tables and modder functions
FOR a% = 0 TO 25
trnsltr(a%) = CHR$(a% + 97)
trnsltr(a% + 26) = CHR$(a% + 65)
NEXT a%
FOR a% = 52 TO 61
trnsltr(a%) = CHR$(a% - 4)
NEXT a%
FOR a% = 62 TO 66
READ b%
trnsltr(a%) = CHR$(b%)
NEXT a%
FOR a% = 0 TO 255
trnslt(a%) = -1
NEXT a%
FOR a% = 0 TO 66
trnslt(ASC(trnsltr(a%))) = a%
NEXT a%
RESTORE
END SUB

对 drand# 的调用为您提供从 0 到 1 的随机数,只需将其乘以所需的每个向量所需的范围 p2$ 是传递给密码处理程序的密码,该处理程序将其与其他一些随机字符组合,然后加盖大小到某个限制 p1$ 是包含最终修改密码的位置 drand# 本身调用了另一个 sub,它实际上是它自己的一个克隆,带有一些改组 用于确保生成的数字是真正随机的排序还有一个值表,这些值添加到要添加的值中,所有这些总体上使 RNG 随机数比没有随机数多得多。

此 RNG 对初始设置的密码的细微差异具有非常高的敏感性,但是您必须进行初始调用以设置并初始化以“引导”随机数生成器,此 RNG 将生成真正的随机数,该随机数将通过所有随机性测试 甚至比用手洗一副牌更随机,比掷骰子更随机.. 希望这有助于使用相同的密码将导致相同的向量序列

这个程序必须稍作修改才能选择随机向量而不是 它目前用作安全加密随机数生成器...

【讨论】:

    【解决方案4】:

    您可以为此任务使用加密,例如 AES。使用您的种子作为密码,使用坐标作为数据块的结构并对其进行加密。加密块将是您的随机数(您实际上可以使用它的任何部分)。这种方法用于Fortuna PRNG。同样的方法可用于需要随机访问数据的磁盘加密(请参阅Random access encryption with AES In Counter mode using Fortuna PRNG:)

    【讨论】:

      猜你喜欢
      • 2012-02-09
      • 2016-05-24
      • 2016-12-14
      • 2015-12-19
      • 2013-05-14
      • 2015-08-07
      • 2014-03-21
      • 2021-10-03
      • 2023-03-28
      相关资源
      最近更新 更多