【问题标题】:I can't seem to generate PsuedoRandom numbers based on co-ordinates我似乎无法根据坐标生成伪随机数
【发布时间】:2015-12-19 08:18:11
【问题描述】:

我正在尝试创建一个由 PRNG 程序生成的 1 和 0 的无限地图,但只存储一个可见的 7x7 网格。例如在屏幕上,您将看到一个 7x7 的网格,其中显示了 1 和 0 的部分地图,当您向右推动时,它会移动 1 和 0,并将下一行放置到位。

如果您随后向左移动,因为它是伪随机生成的,它会重新生成并显示原始图像。您将能够无限地上下移动。

我遇到的问题是初始地图看起来不是那么随机,并且当您向右或向左滚动时模式会重复,我认为这与我生成数字的方式有关。例如您可以从查看 x=5000 和 y= 10000 开始,因此它需要从这两个值生成一个唯一的种子。

(复杂度是视口大小的变量)

void CreateMap(){
    if(complexity%2==0) {
        complexity--;
    }

    if (randomseed) {
        levelseed=Time.time.ToString();
    }

    psuedoRandom = new System.Random(levelseed.GetHashCode());
    offsetx=psuedoRandom.Next();
    offsety=psuedoRandom.Next();
    levellocation=psuedoRandom.Next();

    level = new int[complexity,complexity];
    middle=complexity/2;

    for (int x=0;x<complexity;x++){
        for (int y=0;y<complexity;y++){
            int hashme=levellocation+offsetx+x*offsety+y;
            psuedoRandom = new System.Random(hashme.GetHashCode());
            level[x,y]=psuedoRandom.Next(0,2);
        }
    }
}

这是我用来左右移动的,

    void MoveRight(){
    offsetx++;
    for (int x=0;x<complexity-1;x++){
        for (int y=0;y<complexity;y++){
            level[x,y]=level[x+1,y];
        }
    }

    for (int y=0;y<complexity;y++){
        psuedoRandom = new System.Random(levellocation+offsetx*y);
        level[complexity-1,y]=psuedoRandom.Next(0,2);
    }
}

void MoveLeft(){
    offsetx--;
    for (int x=complexity-1;x>0;x--){
        for (int y=0;y<complexity;y++){
            level[x,y]=level[x-1,y];
        }
    }

    for (int y=0;y<complexity;y++){
        psuedoRandom = new System.Random(levellocation+offsetx*y);
        level[0,y]=psuedoRandom.Next(0,2);
    }
}

要提取它,我需要能够设置

Level[x,y]=Returnrandom(offsetx,offsety)

int RandomReturn(int x, int y){
    psuedoRandom = new System.Random(Whatseedshouldiuse);   
    return (psuedoRandom.Next (0,2));
}

【问题讨论】:

  • 是否存在所需地图的最大尺寸,例如 1024 x 1024 条目?
  • 不,我试图让它看起来无限。例如您可以从查看坐标 10000、445554 开始,这也是完全随机生成的。
  • 太简单了,假设世界是 100 x 100,视口在位置 60,60。因此,您可以在 x 中看到 60-66 的网格,在 y 中看到 60-66 的网格。 x(60),y(60) 应该填充随机的 1 或 0,并且 x(61),y(60) 也应该填充随机的 1 或 0。因此我需要有一个唯一的值来播种我认为基于 2 个坐标?
  • 是 if (randomseed) true or false... if false 什么是 levelseed?
  • @Munkybunky 我认为您的问题是您正在循环中创建一个“新”随机...不要像您一样在循环内部重新声明...而是在类级别声明它,然后简单地使用 psuedoRandom.Next

标签: c# seeding procedural-generation


【解决方案1】:

我认为您的问题是您正在循环中创建“新”随机...不要像您一样在循环内部重新声明...而是在类级别声明它,然后简单地使用 psuedoRandom.Next 例如见this SO post for an example of the issue you are experiencing

而不是像你正在做的那样在每次迭代时重新实例化一个 Random() 类:

for (int x=0;x<complexity;x++){
    for (int y=0;y<complexity;y++){
        int hashme=levellocation+offsetx+x*offsety+y;
        psuedoRandom = new System.Random(hashme.GetHashCode());
        level[x,y]=psuedoRandom.Next(0,2);
    }
}

做一些类似的事情

for (int x=0;x<complexity;x++){
    for (int y=0;y<complexity;y++){
        // Give me the next random integer
        moreRandom = psuedoRandom.Next(); 
    }
}

编辑:正如 Enigmativity 在这篇文章下方的评论中指出的那样,在每次迭代中重新启动也是浪费时间/资源。

附:如果您确实需要这样做,那么为什么不使用“时间相关的默认种子值”而不是指定一个呢?

【讨论】:

  • 另外你可能应该指出,打电话给new Random(someSeedValue)是浪费时间。如果您创建Random 的单个实例,那么调用new Random() 就足够了。
  • 我认为如果它是静态的会起作用,例如没有动静。示例 5x5 网格,每行 10001 由上述随机性生成,使用 x 和 y 坐标。您从新坐标向左移动生成随机数,第 2 列移动到第 1 列,然后您有一个不同的第 1 列,可能用 1 填充。
  • 我认为我需要的是一种基于坐标值一致地生成随机数的方法。因此,如果我在地图 x 和 y 中引用偏移量,我将始终得到相同的结果
  • 如果我问你给我一个介于 1 和 0 之间的随机数来表示坐标 200,50 而你说的是 0,那么我问你要负载更多然后再问你要一个随机数坐标 200,50 在 1 和 0 之间,你会给我同样的结果。
  • @Munkybunky - 听起来你确实需要使用new Random(someSeedValue),因为你想要来自相同坐标的相同值。问题是.GetHashCode() 不是特别随机,所以你的随机数不是那么随机。
【解决方案2】:

我会使用SipHash 来散列坐标:

  • 实现起来比较简单
  • 它需要一个键(地图的种子)和一个消息(坐标)作为输入
  • 您可以增加或减少轮数作为质量/性能的权衡。
  • 与您的代码不同,结果可以跨进程和机器重现。

SipHash 网站列出了两个 C# 实现:

【讨论】:

    【解决方案3】:

    我已经玩了一下创建rnd(x, y, seed)

    class Program
    {
        const int Min = -1000; // define the starting point
    
        static void Main(string[] args)
        {
            for (int x = 0; x < 10; x++)
            {
                for (int y = 0; y < 10; y++)
                    Console.Write((RND(x, y, 123) % 100).ToString().PadLeft(4));
                Console.WriteLine();
            }
            Console.ReadKey();
        }
    
        static int RND(int x, int y, int seed)
        {
            var rndX = new Random(seed);
            var rndY = new Random(seed + 123); // a bit different
            // because Random is LCG we can only move forward
            for (int i = Min; i < x - 1; i++)
                rndX.Next();
            for (int i = Min; i < y - 1; i++)
                rndY.Next();
            // return some f(x, y, seed) = function(rnd(x, seed), rnd(y, seed))
            return rndX.Next() + rndY.Next() << 1;
        }
    }
    

    它可以工作,但 ofc 是一个糟糕的实现(因为 RandomLCG),但它应该给出一个大致的想法。它的内存效率很高(没有数组)。可接受的折衷方案是实现值缓存,然后当位于地图的某些部分时,周围的值将从缓存中获取而不是生成。

    对于相同的xyseed,您将始终获得相同的值(这反过来又可以用作细胞种子)。

    TODO:找到一种方法使 Rnd(x)Rnd(y) 没有 LCG 和非常低效的初始化(有循环)。

    【讨论】:

      【解决方案4】:

      在你们提出了所有建议之后,我解决了这个问题。

          void Drawmap(){
          for (int x=0;x<complexity;x++){
              for (int y=0;y<complexity;y++){
                  psuedoRandom = new System.Random((x+offsetx)*(y+offsety));
                  level[x,y]=psuedoRandom.Next (0,2);
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-08
        • 2015-08-07
        • 2021-10-03
        • 2021-12-23
        • 1970-01-01
        相关资源
        最近更新 更多