[英]How to stop a random walk
plot(0:70,0:70, type="n", xlab="X", ylab="Y")
x<-40
y<-40
x2<-60
y2<-60
points(x, y, pch=16, col="red", cex=1.5)
points(x2, y2, pch=16, col="green", cex=1.5)
for (i in 1:10000){
xi<-sample(c(1,0,-1),1)
yi<-sample(c(1,0,-1),1)
x2i<-sample(c(1,0,-1),1)
y2i<-sample(c(1,0,-1),1)
lines(c(x,x+xi),c(y,y+yi))
lines(c(x2,x2+x2i),c(y2,y2+y2i), col="red")
x<-x+xi
y<-y+yi
x2<-x2+x2i
y2<-y2+y2i
if(x2==x && y==y2) {
break
}
}
我有兩行隨機走動,兩行相遇時我需要停下來。
首先,我畫了一個空圖和兩條線的起點。 然后,我有一個for循環,用於直線移動,在繪圖上繪制它們並為下一次迭代獲取新的起點。
我試圖使行在使用時相遇時停止: if(x2==x && y==y2) { break }
但行僅在它們位於相同點且同時(在同一迭代中)停止如果他們中的一個越過另一個,我就需要他們停下來。 如果一個交叉點已經畫出另一條線。 我認為問題在於已經繪制的點不會保存在任何地方,因此我無法將它們與直線的點進行比較。 也許我需要將積分保存到循環之外? 有人知道如何制止它嗎?
N <- 10000
D <- 1
coef.1 <- matrix(NA,N,2)
coef.2 <- matrix(NA,N,2)
path.1 <- matrix(NA,N,2)
path.2 <- matrix(NA,N,2)
path.1[1,] <- c(40,40)
path.2[1,] <- c(60,60)
d.start <- sqrt(sum((path.1[1,]-path.2[1,])^2))
ch <- "."
set.seed(1)
system.time({
for (i in 2:N){
if (i%%50==0) cat(ch)
path.1[i,] <- path.1[i-1,] + sample(-D:D,2)
path.2[i,] <- path.2[i-1,] + sample(-D:D,2)
coef.1[i,] <- get.line(path.1[(i-1):i,])
coef.2[i,] <- get.line(path.2[(i-1):i,])
r.1 <- sqrt(max(rowSums((path.1[1:i,]-path.1[1,])^2)))
r.2 <- sqrt(max(rowSums((path.2[1:i,]-path.2[1,])^2)))
if (r.1+r.2 < d.start) next # paths do not overlap
ch <- "1"
d.1 <- sqrt(min(rowSums((path.2[1:i,]-path.1[1,])^2)))
d.2 <- sqrt(min(rowSums((path.1[1:i,]-path.2[1,])^2)))
if (d.1>r.1 & d.2>r.2) next
ch <- "2"
cross <- sapply(2:i,
function(k){seg.intersect(path.2[(k-1):k,],path.1[(i-1):i,],k)})
if (any(cross)) break
cross <- sapply(2:i,
function(k){seg.intersect(path.1[(k-1):k,],path.2[(i-1):i,],k)})
if (any(cross)) break
}
})
# 11111111112222222222222222222222
# user system elapsed
# 1016.82 0.13 1024.18
print(paste("End at Step: ",i))
# [1] "End at Step: 1624"
plot(0:100,0:100, type="n", xlab="X", ylab="Y")
points(path.1[1,1],path.1[1,2], pch=16, col="red", cex=1.5)
points(path.2[1,1],path.2[1,2], pch=16, col="green", cex=1.5)
lines(path.1[1:i,])
lines(path.2[1:i,],col="red")
正如@CarlWitthoft指出的那樣,在每個步驟中,您都必須檢查所有先前的線段是否存在交叉。 這就產生了一個嚴重的問題,因為在每個新步驟i
,都有2*(i-1)
交叉測試。 因此,如果您在步驟k
到達交叉口,將進行2*k*(k+1)
測試。 如果k ~O(10000)
,則可能存在100MM測試。
為了提高效率,我們不僅在每個步驟中存儲了兩個新點,而且還存儲了新創建的線段的斜率和截距。 這樣避免了在每個步驟中重新計算所有先前線段的斜率和截距。 此外,我們還計算了每個步驟中每個路徑的路徑半徑r。 這是起點與路徑上距起點最遠的點之間的距離。 如果路徑起點和另一條路徑上最接近點之間的距離大於路徑半徑,則可能沒有交叉,我們可以跳過此步驟的各個線段比較。
由於其他原因,您的問題很有趣。 測試交叉的正常方法是確定兩條線之間的交點是否在任何一條線上。 這很麻煩但是很簡單。 但是,有許多特殊情況:線是否平行? 如果是這樣,它們是巧合嗎? 如果是這樣,那么片段重疊嗎? 垂直線(slope = Inf)呢? 因為您將增量設置為[-1,1]上的隨機整數,所以所有這些可能性很可能最終會在具有10000步的路徑中發生。 因此,上面的函數seg.intersect(...)
必須考慮所有這些可能性。 您可能會認為R中有一個函數可以做到這一點,但我找不到一個函數,所以這里是一個(混亂的)版本:
get.line <- function(l) { # returns slope and intercept
if (diff(l)[1]==0) return(c(Inf,NA))
m <- diff(l)[2]/diff(l)[1]
b <- l[1,2]-m*l[1,1]
return(c(m,b))
}
is.between <- function(x,vec) { # test if x is between values in vec
return(x>=range(vec)[1] & x<=range(vec)[2])
}
special.cases = function(l1,l2, coeff) {
# points coincide: not a line segment!
if (rowSums(diff(l1)^2)==0 | rowSums(diff(l2)^2)==0) return(c(NA,FALSE))
# both lines vertical
if (is.infinite(coeff[1,1]) & is.infinite(coeff[2,1])) {
if (l1[1,1]!=l2[1,1]) return(c(NA,FALSE))
t1 <- is.between(l1[1,2],l2[,2]) | is.between(l1[2,2],l2[,2])
t2 <- is.between(l2[1,2],l1[,2]) | is.between(l2[2,2],l1[,2])
return(c(NA,t1|t2))
}
# only l1 is vertical
if (is.infinite(coeff[1,1]) & is.finite(coeff[2,1])) {
x <- l1[1,1]
y <- c(x,1) %*% coeff[2,]
return(c(x,y))
}
# only l2 is vertical
if (is.finite(coeff[1,1]) & is.infinite(coeff[2,1])) {
x <- l2[1,1]
y <- c(x,1) %*% coeff[1,]
return(c(x,y))
}
# parallel, non-coincident lines
if (diff(coeff[,1])==0 & diff(coeff[,2])!=0) return(c(NA,FALSE))
# parallel, coincident lines
if (diff(coeff[,1])==0 & diff(coeff[,2])==0) {
x <- l1[1,1]
y <- l1[1,2]
return(c(x,y))
}
# base case: finite slopes, not parallel
x <- -diff(coeff[,2])/diff(coeff[,1])
y <- c(x,1) %*% coeff[1,]
return(c(x,y))
}
seg.intersect <- function(l1,l2,i){
pts <- list(l1,l2)
coeff <- rbind(coef.1[i,],coef.2[i,])
z <- special.cases(l1,l2, coeff)
if (is.na(z[1])) return (z[2])
# print(coeff)
# print(z)
found <- do.call("&",
lapply(pts,function(x){is.between(z[1],x[,1]) & is.between(z[2],x[,2])}))
return(found)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.