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