随机“走”在有限区域的中心位置?

时间:2014-03-07 22:51:39

标签: java algorithm math random processing

我不确定我是否可以正确地表达这个问题,但这里就是......

我想编写一个例子,其中小点具有根据它们移动的速度 - 但是,还有一个随机运动叠加到“正确”的运动上。使用下面的Processing代码,我得到以下动画:

marbles.gif

右边的点应该朝向右下角,我对它的行为表现不错。问题是左点,它应该是“静态的” - 所以它只会显示“随机”运动“到位”;但是,正如动画.gif所示,它最终会偏离其原始位置一段距离。随机速度计算如下:

this.randspeed.set(random(0,1)-0.5, random(0,1)-0.5);

我猜想random(0,1)-0.5不会给我一个像高斯一样的“正态分布”(或者收敛到零);但话又说回来,即使它是一个“正确的”高斯,我仍然可以有这样的“运气”,所以说,正值[0:0.5]返回一整天,然后负值[-0.5:0]第二天返回,最后,它仍然是一个合适的高斯。

所以,我猜,我正在寻找一种方法将(伪?)随机序列(由random(0,1)-0.5生成的序列)转换为伪随机序列,但其中平均和N个样本(比方说,10)是0.我不知道如何称之为 - 一个随机序列周期性地收敛到零,我猜??

请注意,我一直在尝试直接更改position;保存position而改变finalpos - 改变位置似乎更像是一个“自然的”平滑运动(特别是模数帧运算,所以每帧都没有分配新的随机速度);但是,它还允许随机噪声累积,并将点“推”远离其中心位置。另外,请注意我花了一些时间直到我可以在.gif上重现这一点,运行程序“live”似乎导致点更快地偏离原始位置(我已经阅读了一些关于硬件事件,如硬件磁盘写入用于在Linux上更改/dev/random的熵,但我真的不知道它是否相关)。

另外,我想到在点位置周围设置某种虚拟边框,并对从边界出来的随机运动进行碰撞检测 - 但在我看来,这似乎是太多的工作(以及矢量的CPU周期)这种事情;我希望随机函数能以某种方式以一种更容易的方式“缓和”,而不是。

那么,是否有推荐的方法在有限区域的中心位置周围进行这种随机运动?


marbles.pde

import java.util.*; // added for Iterator;

ArrayList<Marble> marbles = new ArrayList<Marble>();
Iterator<Marble> imarb;
color mclr = #0000FF;
int RNDLIMIT = 2;
int framecount = 0;

void setup() {
  size(600,400,P2D);
  Marble m_moving = new Marble(width/2, height/2, 2, 2);
  marbles.add(m_moving);
  Marble m_stopped = new Marble(width/2-100, height/2, 0, 0);
  marbles.add(m_stopped);
}

void draw() {
  background(255);

  strokeWeight(1);
  stroke(mclr);
  fill(mclr);

  imarb = marbles.iterator();
  while (imarb.hasNext()) {
    Marble m = imarb.next();
    m.update();
    ellipse(m.finalpos.x, m.finalpos.y, m.radius*2, m.radius*2);
  }
  framecount++;
  //~ saveFrame("marbles-######.png");
}

class Marble {

  PVector position = new PVector(0,0);
  PVector finalpos = new PVector(0,0);
  PVector speed = new PVector(0,0);
  PVector randspeed = new PVector(0,0);
  float radius=4;

  public Marble() {
  }

  public Marble(float inx, float iny, float invx, float invy) {
    this.position.set(inx, iny);
    this.speed.set(invx, invy);
  }

  public void update() {
    this.position.add(this.speed);
    if (framecount % 4 == 0) {
      this.randspeed.set(random(0,1)-0.5, random(0,1)-0.5);
      this.randspeed.setMag(RNDLIMIT);
    }
    int algoTry = 1; // 0
    switch(algoTry) {
      case 0:
        this.finalpos.set(PVector.add(this.position, this.randspeed));
        break;
      case 1:
        this.position.set(PVector.add(this.position, this.randspeed));
        this.finalpos.set(this.position);
        break;
    }
  }
}

4 个答案:

答案 0 :(得分:6)

典型的“随机游走”将始终蜿蜒而去,因为统计数据不会“平衡”。向左移动将不会通过向右移动来纠正。所以随机性的质量不是问题。

如果您希望点保持在特定位置,您应该存储该位置并使“正确”动作(如您所说)始终朝着该位置移动。从目标位置减去当前位置应该可以获得正确的“正确”运动。使用此解决方案,点将始终倾向于返回到开始的位置。

答案 1 :(得分:3)

嗯,我想我到了某个地方;感谢@Teepeemm的评论,我了解了Ornstein - Uhlenbeck流程,以及Brownian motion:“Wiener process描述......其中一个最着名的{ {3}} ”。重读Lévy processes(“随着时间的推移,这个过程往往偏向其长期意义......是一个嘈杂的放松过程的原型......弹簧的长度x(t)会波动随机地围绕弹簧休息长度x0; “),我意识到这不是我想要的 - 它会导致我的点最终落在中心位置,然后我将不得不”ping“每一个偶尔。

正如我意识到我需要永远地理解,然后编写代码,这些过程 - 我发现了这一点:

Ornstein - Uhlenbeck process

  
    

我想生成具有特定频率的噪声数据     特性:即功率谱密度(PSD)必须     与f ^ 0,f,f ^ 2等成比例。

  
     

f ^ 0 - 使用randn
  f ^( - 2) - 低通滤波器f ^ 0时间序列,或与积分器集成   f ^ 2 - 区分,与diff

一样

...所以我想,也许我可以以某种方式处理原始随机数,以获得我想要的“分布”。所以我想出了一个Processing补丁,你可以在下面找到rndquest2.pde。处理可以很容易地为点使用alpha颜色,如果背景没有被删除,它们会累积 - 因此更容易看出被调整的随机输出的实际分布是​​什么。我有这个图像:

Generation of noise with given PSD - Newsreader - MATLAB Central

“选择0”似乎指出random()生成具有均匀分布的序列(白噪声)。例如,“选择1”会使点倾向于粘在边缘上; “选择2”显然显示折叠;而且我也喜欢一个圆圈。最后,我猜到了一个类似于高斯(在中心最频繁,并且慢慢缩小到边缘)的东西,在“选择9”,类似径向折叠的东西。 “选择9”上仍然有一个可见的阈值边界,但如果它在OP中的上面的代码中实现,那么我会得到这样的结果:

rndquest2.pde.png

......实际上,就像我想要的那样! (不知道为什么开始就像它一样出现)但诀窍是随机向量一旦被限制/处理,应该被解释为位置(或者更确切地说,应该被添加到位置,获得一个新位置,用于计算finalpos)的新速度;它不应该直接添加到速度/速度!

因此,只需要在OP代码中添加这些更改:

...
float r1 =0, r2 = 0;
PVector rv = new PVector(r1, r2);
float radius = 10;
float pr1 =0; int pr3 =0;
...
int signum(float f) {
  if (f > 0) return 1;
  if (f < 0) return -1;
  return 0;
}

float getRandom() {
  float ret;
  ret = random(-radius,radius);
  return ret;
}

void getRandomVect() {
  r1 = getRandom();
  r2 = getRandom();
  rv.set(r1,r2);
  while(rv.mag() > radius) {
    r1 = getRandom();
    r2 = getRandom();
    rv.set(r1,r2);
  }
  pr1 = rv.mag()-radius/2;
  pr3 = int(radius-rv.mag());
  pr3 = (pr3 == 0) ? 1 : pr3;
  if (pr1>0) {
    r1 = rv.x - random(1)*2*signum(rv.x)*pr3;
    r2 = rv.y - random(1)*2*signum(rv.y)*pr3;
  }
  rv.set(r1,r2);
}
...
  public void update() {
    this.position.add(this.speed);
    if (framecount % 4 == 0) {
      getRandomVect();
      this.randspeed.set(PVector.div(PVector.sub(PVector.add(this.position, rv), this.finalpos), 4));
    }

    this.finalpos.set(PVector.add(this.finalpos, this.randspeed));
  }
...

...让它按照这篇文章中的gif所示工作。

嗯,希望这有助于某人,
干杯!


rndquest2.pde

PVector mainpos = new PVector(200.0, 200.0);
float radius = 50;
float x1 =0, y1 = 0;
float r1 =0, r2 = 0;
float pr1 =0, pr2 = 0;
int pr3 =0, pr4 = 0;
PVector rv = new PVector(r1, r2);
color clr = color(0,0,255,30);
int choice = 0;
int framecount = 0;

void setup() {
  size(600,400,P2D);
  background(255);
  textSize(14);
  textAlign(LEFT, TOP);
}

void draw() {
  try {
  strokeWeight(2);
  stroke(clr); // #0000FF web colors only
  fill(clr);
  point(mainpos.x, mainpos.y);
  r1 = getRandom();
  r2 = getRandom();
  switch(choice) {
    case 0:
      x1 = mainpos.x + r1;
      y1 = mainpos.y + r2;
      println("0"); // these help trigger the draw(), apparently..
      break;
    case 1:
      rv.set(r1,r2);
      if(rv.mag() > radius) {
        rv.setMag(radius);
      }
      x1 = mainpos.x + rv.x;
      y1 = mainpos.y + rv.y;
      println("1");
      break;
    case 2:
      rv.set(r1,r2);
      if(rv.mag() > radius) {
        rv.sub(PVector.mult(rv,0.1*(rv.mag()-radius)));
      }
      x1 = mainpos.x + rv.x;
      y1 = mainpos.y + rv.y;
      println("2");
      break;
    case 3:
      rv.set(r1,r2);
      while(rv.mag() > radius) {
        r1 = getRandom();
        r2 = getRandom();
        rv.set(r1,r2);
      }
      x1 = mainpos.x + rv.x;
      y1 = mainpos.y + rv.y;
      println("3");
      break;
    case 4:
      pr1 = rv.x;
      pr2 = rv.y;
      rv.set(r1-pr1,r2-pr2);
      while(rv.mag() > radius) {
        r1 = getRandom();
        r2 = getRandom();
        rv.set(r1-pr1,r2-pr2);
      }
      x1 = mainpos.x + rv.x;
      y1 = mainpos.y + rv.y;
      println("4");
      break;
    case 5:
      pr1 = rv.x;
      pr2 = rv.y;
      rv.set(r1-pr1,r2-pr2);
      if(rv.mag() > radius) {
        rv.mult(1.0/(rv.mag()-radius));
      }
      x1 = mainpos.x + rv.x;
      y1 = mainpos.y + rv.y;
      println("5");
      break;
    case 6:
      pr1 = (pr1 + r1)/2.0;
      pr2 = (pr2 + r2)/2.0;
      rv.set(pr1,pr2);
      if(rv.mag() > radius) {
        rv.mult(1.0/(rv.mag()-radius));
      }
      x1 = mainpos.x + rv.x;
      y1 = mainpos.y + rv.y;
      println("6");
      break;
    case 7:
      r1 = (pr1 + r1)/2.0;
      r2 = (pr2 + r2)/2.0;
      rv.set(r1,r2);
      while(rv.mag() > radius) {
        r1 = getRandom();
        r2 = getRandom();
        r1 = (pr1 + r1)/2.0;
        r2 = (pr2 + r2)/2.0;
        rv.set(r1,r2);
      }
      pr1 = rv.x;
      pr2 = rv.y;
      x1 = mainpos.x + rv.x;
      y1 = mainpos.y + rv.y;
      println("7");
      break;
    case 8:
      rv.set(r1,r2);
      while(rv.mag() > radius) {
        r1 = getRandom();
        r2 = getRandom();
        rv.set(r1,r2);
      }
      //~ pr1 = abs(rv.x)-radius/2;
      //~ pr2 = abs(rv.y)-radius/2;
      pr1 = rv.mag()-radius/2;
      //~ pr3 = int(radius-abs(rv.x));
      //~ pr4 = int(radius-abs(rv.y));
      pr3 = int(radius-pr1);
      pr3 = (pr3 == 0) ? 1 : pr3;
      //~ pr4 = (pr4 == 0) ? 1 : pr4;
      if (pr1>0)
        r1 = rv.x - random(1)*2*signum(rv.x)*pr1; //framecount ; b2i(int(random(radius)) % pr3 == 0)*
      if (pr1>0) //(pr2>0)
        r2 = rv.y - random(1)*2*signum(rv.y)*pr1;//pr2;
      rv.set(r1,r2);
      x1 = mainpos.x + rv.x;
      y1 = mainpos.y + rv.y;
      println("8");
      break;
    case 9:
      rv.set(r1,r2);
      while(rv.mag() > radius) {
        r1 = getRandom();
        r2 = getRandom();
        rv.set(r1,r2);
      }
      pr1 = rv.mag()-radius/2;
      pr3 = int(radius-rv.mag()); //pr1);
      pr3 = (pr3 == 0) ? 1 : pr3;
      if (pr1>0) {
        r1 = rv.x - random(1)*2*signum(rv.x)*pr3; //framecount ; b2i(int(random(radius)) % pr3 == 0)*
        r2 = rv.y - random(1)*2*signum(rv.y)*pr3;//pr2;
        //~ r1 = rv.x - 2*signum(rv.x)*pr3; //like an X for pr3 = int(radius-pr1);
        //~ r2 = rv.y - 2*signum(rv.y)*pr3;

      }
      rv.set(r1,r2);
      x1 = mainpos.x + rv.x;
      y1 = mainpos.y + rv.y;
      println("9");
      break;
  }
  // note: patch does not draw point(mainpos.x + getRandom(), ..)
  point(x1, y1);

  fill(255);
  stroke(255); //~ stroke(255,0,0);
  rect(mainpos.x-radius,100,mainpos.x-radius+100,20);
  fill(0,0,255);
  stroke(clr);
  text(String.format("choice %d (f:%d)", choice, framecount), mainpos.x-radius, 100);
  framecount++;
  if (framecount % 5000 == 0) {
    saveFrame(String.format("rndquest2-%d-%d-######.png", choice, framecount));
  }
  } catch(Exception e) {
    e.printStackTrace();
  }
}

int signum(float f) {
  if (f > 0) return 1;
  if (f < 0) return -1;
  return 0;
}

int b2i(boolean inb) {
  if (inb) return 1;
  else return 0;
}

float getRandom() {
  float ret;
  ret = random(-radius,radius);
  return ret;
}

void mousePressed() {
  choice = (choice + 1) % 10;
  background(255);
  framecount = 0;
}

答案 2 :(得分:1)

如果您希望在“实际”点的某个距离内随机移动,您可以尝试从“实际”位置获得固定的最大距离,并且不允许球在该半径之外。

如果您不想要硬限制,可以添加某种力将物体吸引到其“实际”位置,并使其随着该点的距离线性,平方或通过其他功能增加你选择的。然后,物体可以自由地在其“实际”位置移动,但仍然保持相对靠近。

答案 3 :(得分:0)

您正在模拟随机游走。通常,n步之后的随机游走将从sqrt(n)开始(更具体地说,它将遵循Law of the Iterated Logarithm,因此它的大小在n之后}步骤是O(sqrt(n log log n)))。这是一个漫长的说法,随着时间的推移,步行将会消失(但因为它是二维的,它最终将返回原点)。

要解决此问题,您需要向原点漂移。一个具有此属性的随机过程是Ornstein - Uhlenbeck process,它向原点漂移,与原点的距离成正比。 (并且随机游走的随机部分仍会使其在其原点周围摆动。)

这可以通过

中的某些内容在您的原始代码中完成
double driftScale = .01;
double wiggleScale = 1;
Point origin = new Point(0,0);
...
this.randspeed.set(driftScale*(origin.x-this.position.x)+wiggleScale*(random(0,1)-.5),
                   driftScale*(origin.y-this.position.y)+wiggleScale*(random(0,1)-.5));

用标准普通高斯替换random(0,1)-.5会更好,但我不知道这种影响有多明显。最大的区别在于,使用当前代码,点可以从一开始就获得最大距离。使用高斯,它理论上可以任意远(但它仍会漂移回原点)。

我也不太确定这与您的最终解决方案有多大匹配。我在跟踪你的逻辑时遇到了麻烦(使用PVector而10个案例没有帮助。)