我不确定我是否可以正确地表达这个问题,但这里就是......
我想编写一个例子,其中小点具有根据它们移动的速度 - 但是,还有一个随机运动叠加到“正确”的运动上。使用下面的Processing
代码,我得到以下动画:
右边的点应该朝向右下角,我对它的行为表现不错。问题是左点,它应该是“静态的” - 所以它只会显示“随机”运动“到位”;但是,正如动画.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;
}
}
}
答案 0 :(得分:6)
典型的“随机游走”将始终蜿蜒而去,因为统计数据不会“平衡”。向左移动将不会通过向右移动来纠正。所以随机性的质量不是问题。
如果您希望点保持在特定位置,您应该存储该位置并使“正确”动作(如您所说)始终朝着该位置移动。从目标位置减去当前位置应该可以获得正确的“正确”运动。使用此解决方案,点将始终倾向于返回到开始的位置。
答案 1 :(得分:3)
正如我意识到我需要永远地理解,然后编写代码,这些过程 - 我发现了这一点:
我想生成具有特定频率的噪声数据 特性:即功率谱密度(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中的上面的代码中实现,那么我会得到这样的结果:
......实际上,就像我想要的那样! (不知道为什么开始就像它一样出现)但诀窍是随机向量一旦被限制/处理,应该被解释为位置(或者更确切地说,应该被添加到位置,获得一个新位置,用于计算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个案例没有帮助。)