Случайная "прогулка" вокруг центрального места в ограниченном пространстве?

Я не уверен, могу ли я правильно ответить на этот вопрос, но вот он идет.

Я хочу привести пример, где маленькие точки имеют скорость, по которой они перемещаются, - но также есть случайное движение, наложенное на "правильное" движение. Используя следующий код 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", по-видимому, приводит к тому, что точка быстрее расходится с исходным местоположением (я читал что-то об аппаратных событиях, записи диска используются для изменения энтропии для /dev/random в Linux, но я не знаю, связано ли это).

Кроме того, я думал о настройке какой-то виртуальной границы вокруг точки точки и обнаружении столкновения для случайного движения, выходящего из границы, - но мне кажется, что это слишком много работы (и циклы процессора для вектора операции) для такого рода вещей; Я бы надеялся, что случайная функция может быть как-то "закалена" проще.

Итак, был ли рекомендованный способ приблизиться к подобному случайному движению вокруг центрального местоположения в ограниченной области?


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;
    }
  }
}

Ответ 1

Типичное "случайное блуждание" всегда блуждает, потому что статистика не "балансирует". Перемещение много влево не будет исправлено с движением вправо. Поэтому качество случайности не является проблемой.

Если вы хотите, чтобы точка оставалась в определенном месте, вы должны сохранить это местоположение и сделать "правильное" движение (как вы его называли), всегда двигаться в этом месте. Некоторое вычитание текущего местоположения из целевого местоположения должно дать вам правильное "правильное" движение. С помощью этого решения точка всегда будет склоняться назад к началу.

Ответ 2

Ну, я думаю, я где-то попал; благодаря комментарию @Teepeemm, я узнал о процессе Орнштейна - Уленбека, а также о том, что Brownian движение: "описывается процесс Wiener... один из самых известных Процессы Lévy ". Перечитывание Процесс Ornstein - Uhlenbeck ( "С течением времени процесс имеет тенденцию дрейфовать к его среднему значению... является прототипом шумного процесс релаксации... длина x (t) of spring будет стохастически колебаться вокруг длины ожидания spring x0;" ), я понял, что это не то, что я хочу - это привело бы к тому, что моя точка в конечном итоге центральное положение, и тогда мне пришлось бы "пинговать" это время от времени.

Так же, как я понял, что мне понадобится навсегда понять кулак, а затем код, эти процессы - я нашел это:

Генерация шума при заданном PSD - Newsreader - MATLAB Central

Я хочу генерировать данные шума с определенной частотой   характеристики: То есть спектральная плотность мощности (PSD) должна быть   пропорционально f ^ 0, f, f ^ 2 и т.д.

f ^ 0 - использовать randn
f ^ (- 2) - фильтр нижних частот временного ряда f ^ 0 или интегрировать с cumsum f ^ 2 - дифференцируем, как при diff

... поэтому я подумал, может быть, я как-нибудь обработаю необработанные случайные числа, чтобы получить "распределение", как я хочу. Поэтому я придумал патч для обработки, который вы найдете ниже как rndquest2.pde. Обработка позволяет легко использовать альфа-цвета для точек, и если фон не стирается, они накапливаются, поэтому легче видеть, что является фактическим распределением произвольного выходного файла. Я получил это изображение:

rndquest2.pde.png

"Выбор 0", кажется, указывает, что random() генерирует последовательность с равномерным распределением (белый шум). Например, "выбор 1" приведет к тому, что точка будет прилипать к краям; "Выбор 2" вполне очевидно показывает складывание; и я предпочел бы круг. В конце концов, я получил нечто похожее на Гаусса (наиболее частое в центре и медленно уменьшающееся до краев) на "выбор 9", похоже на радиальную складку. По-прежнему существует видимая граница порога на "выбор 9", но если она реализована в коде выше в OP, тогда я получаю что-то вроде этого:

marbles2.gif

... который, собственно, так я и хотел! (не знаю, почему начало вышло так же, как и было). Фокус в том, что случайный вектор, однажды ограниченный/обработанный, следует интерпретировать как позицию (точнее, следует добавить в позицию, чтобы получить новую позицию, используется для вычисления новой скорости для 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;
}

Ответ 3

Если вы хотите случайное перемещение на определенном расстоянии от "фактической" точки, вы можете попробовать иметь фиксированное максимальное расстояние от "фактического" местоположения и не разрешать мяч за пределами этого радиуса.

Если вам не нужен жесткий предел, вы можете добавить какую-то силу, которая привлекает объект к его "фактическому" местоположению и увеличивает его с расстоянием от этой точки линейно, квадратично или некоторой другой функцией по вашему выбору. Тогда объект будет свободен передвигаться по своему "фактическому" местоположению, но по-прежнему будет находиться поблизости.

Ответ 4

Вы моделируете случайное блуждание. Как правило, случайное блуждание после шагов n будет иметь порядок sqrt(n) с того места, где оно началось (более конкретно, оно будет подчиняться закону закона Iterated Логарифм, так что его величина после шагов n равна O(sqrt(n log log n))). Это долгий путь сказать, что прогулка будет блуждать со временем (но потому, что она двумерна, она в конечном итоге вернется в начало).

Чтобы решить эту проблему, вы хотите иметь дрейф назад к началу координат. Одним из случайных процессов, обладающих этим свойством, является процесс Ornstein - Uhlenbeck, который имеет дрейф к происхождению, который пропорционален его расстоянию от начала координат. (И случайная часть случайного блуждания все равно заставит ее шевелить вокруг своего начала.)

Это может быть выполнено в вашем исходном коде чем-то в строках

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 случаев не помогло).