มีวิธีแก้ปัญหาเส้นทางราคา แต่คุณจะต้องรหัสด้วยตัวคุณเอง นี่คือลักษณะที่ปรากฏเมื่อนำไปใช้กับทุกจุดของภาพในคำถาม (หยาบเล็กน้อยเพื่อเพิ่มความเร็วในการคำนวณ):
เซลล์สีดำเป็นส่วนหนึ่งของรูปหลายเหลี่ยมโดยรอบ สีต่าง ๆ ตั้งแต่สีส้มอ่อน (สั้น) ถึงสีน้ำเงิน (ยาว) แสดงระยะทางสูงสุด (ออกสูงสุด 50 เซลล์) ที่สามารถเข้าถึงได้โดยการสำรวจเส้นทางแนวสายตาโดยไม่ดักจับเซลล์รูปหลายเหลี่ยม (เซลล์ใดก็ตามที่อยู่นอกขอบเขตของภาพนี้ถือว่าเป็นส่วนหนึ่งของรูปหลายเหลี่ยม)
เรามาพูดถึงวิธีการที่มีประสิทธิภาพในการทำเช่นนี้โดยใช้การแทนค่า raster ของข้อมูล ในการเป็นตัวแทนนี้เซลล์รูปหลายเหลี่ยม "โดยรอบ" ทั้งหมดจะมีกล่าวว่าค่าที่ไม่ใช่ศูนย์และเซลล์ใด ๆ ที่สามารถ "เห็นผ่าน" จะมีค่าเป็นศูนย์
ขั้นตอนที่ 1: การคำนวณโครงสร้างข้อมูลพื้นที่ใกล้เคียงล่วงหน้า
ก่อนอื่นคุณต้องตัดสินใจว่ามันมีความหมายอย่างไรสำหรับเซลล์หนึ่งที่จะบล็อกอีกเซลล์หนึ่ง หนึ่งในกฎที่ยุติธรรมที่สุดที่ฉันสามารถหาได้คือ: โดยใช้พิกัดรวมสำหรับแถวและคอลัมน์ (และสมมติว่าเซลล์สี่เหลี่ยมจัตุรัส) ให้เราพิจารณาว่าเซลล์ใดอาจบล็อกเซลล์ (i, j) จากมุมมองที่จุดเริ่มต้น (0,0) ฉันเสนอชื่อเซลล์ (i ', j') ซึ่งใกล้เคียงกับส่วนของเส้นเชื่อมต่อ (i, j) ถึง (0,0) ในทุกเซลล์ที่พิกัดต่างจาก i และ j เป็นอย่างมาก 1 เนื่องจากสิ่งนี้ไม่ได้อยู่เสมอ ให้ผลลัพธ์ที่เป็นเอกลักษณ์ (ตัวอย่างเช่นด้วย (i, j) = (1,2) ทั้ง (0,1) และ (1,1) จะทำงานได้ดีพอ ๆ กัน) จำเป็นต้องใช้วิธีแก้ปัญหาความสัมพันธ์บางอย่าง มันจะดีสำหรับการแก้ปัญหาของความสัมพันธ์นี้เพื่อเคารพ symmetries ของละแวกวงกลมในกริด: negating พิกัดหรือสลับพิกัดรักษาย่านเหล่านี้ ดังนั้นเราสามารถตัดสินใจได้ว่าเซลล์ใดปิดกั้น (i,
R
แสดงกฎนี้เป็นรหัสต้นแบบต่อไปนี้เขียนใน รหัสนี้ส่งกลับโครงสร้างข้อมูลที่จะสะดวกในการพิจารณา "การล้อม" ของเซลล์ใด ๆ ในตาราง
screen <- function(k=1) {
#
# Returns a data structure:
# $offset is an array of offsets
# $screened is a parallel array of screened offset indexes.
# $distance is a parallel array of distances.
# The first index always corresponds to (0,0).
#
screened.by <- function(xy) {
uv <- abs(xy)
if (reversed <- uv[2] > uv[1]) {
uv <- rev(uv)
}
i <- which.min(c(uv[1], abs(uv[1]-uv[2]), uv[2]))
ij <- uv + c(floor((1-i)/3), floor(i/3)-1)
if (reversed) ij <- rev(ij)
return(ij * sign(xy))
}
#
# For each lattice point within the circular neighborhood,
# find the unique lattice point that screens it from the origin.
#
xy <- subset(expand.grid(x=(-k:k), y=(-k:k)),
subset=(x^2+y^2 <= k^2) & (x != 0 | y != 0))
g <- t(apply(xy, 1, function(z) c(screened.by(z), z)))
#
# Sort by distance from the origin.
#
colnames(g) <- c("x", "y", "x.to", "y.to")
ij <- unique(rbind(g[, 1:2], g[, 3:4]))
i <- order(abs(ij[,1]), abs(ij[,2])); ij <- ij[i, , drop=FALSE]
rownames(ij) <- 1:length(i)
#
# Invert the "screened by" relation to produce the "screened" relation.
#
# (Row, column) offsets.
ij.df <- data.frame(ij, i=1:length(i))
#
# Distances from the origin (in cells).
distance <- apply(ij, 1, function(u) sqrt(sum(u*u)))
#
# "Screens" relation (represented by indexes into ij).
g <- merge(merge(g, ij.df), ij.df,
by.x=c("x.to", "y.to"), by.y=c("x","y"))
g <- subset(g, select=c(i.x, i.y))
h <- by(g$i.y, g$i.x, identity)
return( list(offset=ij, screened=h, distance=distance) )
}
ค่าของscreen(12)
ใช้ในการสร้างภาพของความสัมพันธ์ในการคัดกรองนี้: ลูกศรชี้จากเซลล์ไปยังเซลล์ที่คัดกรองพวกมันทันที เฉดสีมีสัดส่วนตามระยะทางถึงจุดกำเนิดซึ่งอยู่ตรงกลางของย่านนี้:
การคำนวณนี้รวดเร็วและจำเป็นต้องทำเพียงครั้งเดียวสำหรับพื้นที่ใกล้เคียงที่กำหนด ตัวอย่างเช่นเมื่อมองออกไป 200 ม. บนกริดที่มีเซลล์ 5 ม. ขนาดพื้นที่ใกล้เคียงจะเท่ากับ 200/5 = 40 หน่วย
ขั้นตอนที่ 2: ใช้การคำนวณกับคะแนนที่เลือก
ส่วนที่เหลือนั้นตรงไปตรงมา: เพื่อตรวจสอบว่าเซลล์ตั้งอยู่ที่ (x, y) (ในพิกัดแถวและคอลัมน์) เป็น "ล้อมรอบ" ด้วยความเคารพต่อโครงสร้างข้อมูลพื้นที่ใกล้เคียงนี้หรือไม่ทำการทดสอบซ้ำเริ่มต้นด้วยออฟเซ็ต (i, j) = (0,0) (ที่มาของพื้นที่ใกล้เคียง) หากค่าในตารางรูปหลายเหลี่ยมที่ (x, y) + (i, j) ไม่ใช่ศูนย์การมองเห็นจะถูกปิดกั้นที่นั่น มิฉะนั้นเราจะต้องพิจารณาถึงการชดเชยทั้งหมดที่อาจถูกบล็อกที่ออฟเซ็ต (i, j) (ซึ่งพบในเวลา O (1) โดยใช้โครงสร้างข้อมูลที่ส่งคืนโดยscreen
) หากไม่มีสิ่งใดถูกปิดกั้นเราได้มาถึงขอบเขตแล้วและสรุปว่า (x, y) ไม่ถูกล้อมรอบดังนั้นเราจึงหยุดการคำนวณ (และไม่ต้องสนใจตรวจสอบจุดที่เหลืออยู่ในละแวกใกล้เคียง)
เราสามารถรวบรวมข้อมูลที่เป็นประโยชน์ได้มากขึ้นโดยการติดตามระยะห่างของแนวสายตาที่ไกลที่สุดในระหว่างอัลกอริทึม หากนี่น้อยกว่ารัศมีที่ต้องการเซลล์จะถูกล้อมรอบ มิฉะนั้นจะไม่
นี่คือR
ต้นแบบของอัลกอริทึมนี้ ยาวกว่าที่คิดเพราะR
ไม่สนับสนุนโครงสร้างสแต็ก (แบบง่าย) ที่จำเป็นต่อการใช้การเรียกซ้ำดังนั้นสแต็กจะต้องมีรหัสด้วย อัลกอริทึมที่แท้จริงเริ่มต้นประมาณสองในสามของการผ่านและต้องการเพียงหนึ่งโหลหรือมากกว่านั้น (และครึ่งหนึ่งของผู้ที่จัดการสถานการณ์รอบ ๆ ขอบกริดตรวจสอบหาดัชนีนอกขอบเขตภายในละแวกนี้อาจทำให้มีประสิทธิภาพมากขึ้นโดยการขยายตารางรูปหลายเหลี่ยมด้วยk
แถวและคอลัมน์รอบปริมณฑล ต้องการการตรวจสอบช่วงดัชนีที่ค่าใช้จ่ายของ RAM เพิ่มอีกเล็กน้อยเพื่อเก็บตารางรูปหลายเหลี่ยม)
#
# Test a grid point `ij` for a line-of-sight connection to the perimeter
# of a circular neighborhood.
# `xy` is the grid.
# `counting` determines whether to return max distance or count of stack ops.
# `perimeter` is the assumed values beyond the extent of `xy`.
#
# Grid values of zero admit light; all others block visibility
# Returns maximum line-of-sight distance found within `nbr`.
#
panvisibility <- function(ij, xy, nbr=screen(), counting=FALSE, perimeter=1) {
#
# Implement a stack for the algorithm.
#
count <- 0 # Stack count
stack <- list(ptr=0, s=rep(NA, dim(nbr$offset)[1]))
push <- function(x) {
n <- length(x)
count <<- count+n # For timing
stack$s[1:n + stack$ptr] <<- x
stack$ptr <<- stack$ptr+n
}
pop <- function() {
count <<- count+1 # For timing
if (stack$ptr <= 0) return(NULL)
y <- stack$s[stack$ptr]
#stack$s[stack$ptr] <<- NA # For debugging
stack$ptr <<- stack$ptr - 1
return(y)
}
#
# Initialization.
#
m <- dim(xy)[1]; n <- dim(xy)[2]
push(1) # Stack the *indexes* of nbr$offset and nbr$screened.
dist.max <- -1
#
# The algorithm.
#
while (!is.null(i <- pop())) {
cell <- nbr$offset[i, ] + ij
if (cell[1] <= 0 || cell[1] > m || cell[2] <= 0 || cell[2] > n) {
value <- perimeter
} else {
value <- xy[cell[1], cell[2]]
}
if (value==0) {
if (nbr$distance[i] > dist.max) dist.max <- nbr$distance[i]
s <- nbr$screened[[paste(i)]]
if (is.null(s)) {
#exited = TRUE
break
}
push(s)
}
}
if (counting) return ( count )
return(dist.max)
}
ในตัวอย่างนี้เซลล์รูปเหลี่ยมมีสีดำ สีให้ระยะห่างระหว่างเส้นสายตาสูงสุด (ไม่เกิน 50 เซลล์) สำหรับเซลล์ที่ไม่ใช่รูปหลายเหลี่ยมตั้งแต่สีส้มอ่อนสำหรับระยะทางสั้นถึงสีน้ำเงินเข้มสำหรับระยะทางไกลที่สุด (เซลล์มีความกว้างหนึ่งหน่วยและสูง) ริ้วที่เห็นได้ชัดนั้นถูกสร้างขึ้นโดย "เกาะ" รูปหลายเหลี่ยมขนาดเล็กที่อยู่ตรงกลางของ "แม่น้ำ": แต่ละเซลล์จะบล็อกเซลล์อื่น ๆ เป็นแนวยาว
การวิเคราะห์อัลกอริทึม
โครงสร้างสแต็กจะทำการค้นหากราฟความลึกของพื้นที่ใกล้เคียงก่อนเพื่อหาหลักฐานว่าเซลล์ไม่ได้ล้อมรอบ ในกรณีที่เซลล์อยู่ไกลจากรูปหลายเหลี่ยมใด ๆ การค้นหานี้จะต้องมีการตรวจสอบเซลล์ O (k) เท่านั้นเพื่อหารัศมีวงกลม k กรณีที่เลวร้ายที่สุดเกิดขึ้นเมื่อมีจำนวนของเซลล์รูปหลายเหลี่ยมที่กระจัดกระจายอยู่ในละแวกใกล้เคียง แต่ถึงแม้ว่าจะไม่สามารถเข้าถึงขอบเขตของละแวกเพื่อนบ้านได้: สิ่งเหล่านี้ต้องการการตรวจสอบเกือบทุกเซลล์ในแต่ละพื้นที่ใกล้เคียงซึ่งเป็น O (k ^ 2) การทำงาน
พฤติกรรมต่อไปนี้เป็นสิ่งปกติที่จะเกิดขึ้น สำหรับค่าขนาดเล็กของ k ยกเว้นว่ารูปหลายเหลี่ยมเติมส่วนใหญ่ของกริดเซลล์ที่ไม่ใช่รูปหลายเหลี่ยมส่วนใหญ่จะไม่ถูกล้อมรอบอย่างชัดเจนและอัลกอริทึมจะปรับขนาดเช่น O (k) สำหรับค่าระดับกลางการปรับสเกลจะเริ่มดูเหมือน O (k ^ 2) เมื่อ k มีขนาดใหญ่จริง ๆ เซลล์ส่วนใหญ่จะถูกล้อมรอบและสามารถระบุความจริงได้ดีก่อนที่จะตรวจสอบพื้นที่ใกล้เคียงทั้งหมด: ความพยายามในการคำนวณของอัลกอริทึมจึงมาถึงขีด จำกัด ในทางปฏิบัติ ข้อ จำกัด นี้จะบรรลุได้เมื่อรัศมีพื้นที่ใกล้เคียงเข้าสู่เส้นผ่านศูนย์กลางของพื้นที่ที่ไม่ใช่รูปหลายเหลี่ยมที่เชื่อมต่อที่ใหญ่ที่สุดในตาราง
ตัวอย่างเช่นฉันใช้counting
ตัวเลือกที่เข้ารหัสในต้นแบบของscreen
เพื่อส่งคืนจำนวนการดำเนินการสแต็กที่ใช้ในการโทรแต่ละครั้ง สิ่งนี้วัดความพยายามในการคำนวณ กราฟต่อไปนี้แสดงจำนวนเฉลี่ยของสแต็ก ops เป็นฟังก์ชันของรัศมีพื้นที่ใกล้เคียง มันแสดงพฤติกรรมที่ทำนายไว้
เราสามารถใช้สิ่งนี้เพื่อประเมินการคำนวณที่จำเป็นในการประเมิน 13 ล้านคะแนนในตาราง สมมติว่ามีการใช้ย่าน k = 200/5 = 40 โดยเฉลี่ยแล้วการปฏิบัติการกองซ้อนสองสามร้อยครั้ง (ขึ้นอยู่กับความซับซ้อนของรูปหลายเหลี่ยมกริดและตำแหน่งที่ 13 ล้านจุดนั้นสัมพันธ์กับรูปหลายเหลี่ยม) ซึ่งหมายความว่าในภาษาที่รวบรวมได้อย่างมีประสิทธิภาพ จะต้อง (เพิ่มคูณอ่านเขียนชดเชย ฯลฯ ) พีซีส่วนใหญ่จะสามารถประเมินความสามารถรอบตัวได้ประมาณล้านจุดในอัตรานั้น (ในR
การใช้งานช้ากว่านั้นมากเพราะมันแย่ในอัลกอริธึมชนิดนี้ซึ่งเป็นเหตุผลว่าทำไมมันจึงถูกพิจารณาเป็นเพียงต้นแบบเท่านั้น) ดังนั้นเราอาจหวังว่าการใช้งานที่มีประสิทธิภาพในภาษาที่มีประสิทธิภาพและเหมาะสม - C ++ และ Python มาถึงใจ - สามารถประเมินผลได้ 13 ล้านคะแนนในหนึ่งนาทีหรือน้อยกว่าโดยสมมติว่าตารางรูปหลายเหลี่ยมทั้งหมดอยู่ใน RAM
เมื่อกริดมีขนาดใหญ่เกินกว่าจะใส่ลงใน RAM ได้ขั้นตอนนี้สามารถใช้กับส่วนเรียงต่อกันของตาราง พวกเขาจะต้องทับซ้อนกันโดยk
แถวและคอลัมน์ ใช้เวลาสูงสุดที่ทับซ้อนกันเมื่อทำการโมเสคผลลัพธ์
แอพพลิเคชั่นอื่น ๆ
"ดึง" ของร่างกายของน้ำมีความสัมพันธ์อย่างใกล้ชิดกับ "surroundedness" ของจุดของมัน ในความเป็นจริงถ้าเราใช้รัศมีพื้นที่ใกล้เคียงเท่ากับหรือมากกว่าเส้นผ่าศูนย์กลางของ waterbody เราจะสร้างตารางของการดึง (ไม่ใช่ทิศทาง) ที่ทุกจุดใน waterbody โดยการใช้รัศมีพื้นที่ใกล้เคียงที่มีขนาดเล็กเราจะได้รับขอบเขตที่ต่ำกว่าสำหรับการดึงที่จุดดึงสูงสุดทั้งหมดซึ่งในบางแอปพลิเคชันอาจดีพอ (และสามารถลดความพยายามในการคำนวณได้อย่างมาก) ตัวแปรของอัลกอริธึมที่ จำกัด "การคัดเลือกโดย" ความสัมพันธ์กับทิศทางที่เฉพาะเจาะจงจะเป็นวิธีหนึ่งในการคำนวณการดึงข้อมูลอย่างมีประสิทธิภาพในทิศทางเหล่านั้น โปรดทราบว่าตัวแปรดังกล่าวต้องการการแก้ไขโค้ดสำหรับscreen
; รหัสสำหรับpanvisibility
จะไม่เปลี่ยนแปลงเลย