एक लागत-पथ समाधान है लेकिन आपको इसे स्वयं कोड करना होगा। जब प्रश्न में छवि के प्रत्येक बिंदु पर लागू किया जाता है तो यह कैसा दिख सकता है (गणनाओं को गति देने के लिए थोड़ा सा छोटा):
काली कोशिकाएँ आसपास के बहुभुजों का हिस्सा होती हैं। नीले (लंबे) के माध्यम से हल्के नारंगी (छोटे) से लेकर रंग, अधिकतम दूरी (अधिकतम 50 कोशिकाओं तक) दिखाते हैं, जो बहुभुज कोशिकाओं को बाधित किए बिना लाइन-ऑफ-द-ट्रैवर्सल तक पहुंच सकते हैं। (इस छवि की सीमा के बाहर किसी भी कोशिका को बहुभुज के हिस्से के रूप में माना जाता है।)
आइए डेटा के रेखापुंज प्रतिनिधित्व का उपयोग करते हुए एक कुशल तरीके पर चर्चा करें। इस प्रतिनिधित्व में सभी "आसपास" पॉलीगोनल कोशिकाओं में, नॉनज़रो वैल्यूज़ और किसी भी सेल को "के माध्यम से" देखा जा सकता है, का शून्य मान होगा।
चरण 1: एक पड़ोस डेटा संरचना को पूर्वव्यापी करना
आपको पहले यह तय करना होगा कि एक सेल को दूसरे को ब्लॉक करने का क्या मतलब है। सबसे उचित नियमों में से एक मैं यह पा सकता हूं: पंक्तियों और स्तंभों के लिए अभिन्न निर्देशांक (और वर्ग कोशिकाओं को संभालने) का उपयोग करते हुए, आइए हम विचार करें कि कौन सी कोशिकाएं मूल (0,0) के दृश्य से सेल (i, j) को रोक सकती हैं। मैं सेल (i ', j') को नामांकित करता हूं जो कि उन सभी कोशिकाओं के बीच (i, j) से (0,0) को जोड़ने वाले लाइन सेगमेंट के सबसे करीब है, जिनके निर्देशांक i और j से अलग-अलग होते हैं। 1. क्योंकि यह हमेशा नहीं होता है एक अनूठा समाधान प्राप्त करें (उदाहरण के लिए, (i, j) = (1,2) दोनों (0,1) और (1,1) समान रूप से अच्छी तरह से काम करेंगे), संबंधों को हल करने के कुछ साधनों की आवश्यकता है। संबंधों के इस संकल्प के लिए यह अच्छा होगा कि ग्रिड में परिपत्र पड़ोस के समरूपता का सम्मान करें: समन्वय को नकारना या निर्देशांक को स्विच करना इन पड़ोस को संरक्षित करता है। इसलिए हम तय कर सकते हैं कि कौन सी कोशिकाएँ ब्लॉक होती हैं (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)
इस स्क्रीनिंग रिलेशन के इस चित्रण का उपयोग करने के लिए मूल्य का उपयोग किया गया था: तीर उन कोशिकाओं से इंगित करते हैं जो उन्हें तुरंत स्क्रीन करते हैं। इस दूरी को मूल से दूरी के अनुपात में रखा गया है, जो इस पड़ोस के बीच में है:
यह गणना तेज है और किसी दिए गए पड़ोस के लिए केवल एक बार किया जाना चाहिए। उदाहरण के लिए, 5 मीटर कोशिकाओं वाले ग्रिड पर 200 मीटर की दूरी पर देखने पर, पड़ोस का आकार 200/5 = 40 यूनिट होगा।
चरण 2: चयनित बिंदुओं पर अभिकलन लागू करना
शेष सीधा है: यह निर्धारित करने के लिए कि इस पड़ोस डेटा संरचना के संबंध में (x, y) (पंक्ति और स्तंभ निर्देशांक में स्थित) "घिरा हुआ" है, फिर से (i, j) की भरपाई के साथ पुनरावर्ती शुरुआत करें। = (0,0) (पड़ोस मूल)। यदि बहुभुज ग्रिड में मान (x, y) + (i, j) नॉनजरो है, तो वहां दृश्यता अवरुद्ध हो जाती है। अन्यथा, हमें उन सभी ऑफसेटों पर विचार करना होगा जिन्हें ऑफसेट (i, j) पर अवरुद्ध किया जा सकता है (जो O (1) समय में डेटा संरचना द्वारा दिए गए समय का उपयोग करके पाया जाता है screen
)। यदि कोई भी अवरुद्ध नहीं है, तो हम परिधि तक पहुंच गए हैं और निष्कर्ष निकालते हैं कि (x, y) घिरा नहीं है, इसलिए हम गणना को रोकते हैं (और पड़ोस में किसी भी शेष बिंदुओं का निरीक्षण करने के लिए परेशान नहीं करते हैं)।
हम एल्गोरिथ्म के दौरान पहुंची दूर-दूरी की दृष्टि का ट्रैक रखकर और भी उपयोगी जानकारी एकत्र कर सकते हैं। यदि यह वांछित त्रिज्या से कम है, तो सेल घिरा हुआ है; अन्यथा यह नहीं है।
यहाँ R
इस एल्गोरिथम का एक प्रोटोटाइप है। ऐसा लगता है की तुलना में लंबा है, क्योंकि R
पुनरावृत्ति को लागू करने के लिए आवश्यक सरल (सरल) स्टैक संरचना का समर्थन नहीं करता है, इसलिए एक स्टैक को कोडित भी करना पड़ता है। वास्तविक एल्गोरिथ्म लगभग दो-तिहाई रास्ते से शुरू होता है और केवल एक दर्जन लाइनों या तो की जरूरत है। (और आधे लोग केवल ग्रिड के किनारे के आस-पास की स्थिति को संभालते हैं, पड़ोस के भीतर-बाहर अनुक्रमित अनुक्रमों के लिए जाँच करते हैं। यह बहुभुज ग्रिड को k
इसकी परिधि के चारों ओर पंक्तियों और स्तंभों द्वारा विस्तारित करके और अधिक कुशल बनाया जा सकता है , किसी को भी नष्ट कर सकता है। बहुभुज ग्रिड को रखने के लिए थोड़ी अधिक रैम की कीमत पर इंडेक्स रेंज की जाँच की आवश्यकता है।)
#
# 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) कोशिकाओं के निरीक्षण की आवश्यकता होगी। सबसे खराब स्थिति तब होती है जब पड़ोस के भीतर बिखरी हुई बहुभुज कोशिकाओं की एक छोटी संख्या होती है, लेकिन फिर भी पड़ोस की सीमा तक नहीं पहुँचा जा सकता है: इन्हें प्रत्येक पड़ोस में लगभग सभी कोशिकाओं का निरीक्षण करने की आवश्यकता होती है, जो एक O (k ^ 2) है ऑपरेशन।
निम्नलिखित व्यवहार विशिष्ट है जो सामना किया जाएगा। K के छोटे मानों के लिए, जब तक कि बहुभुज अधिकांश ग्रिड को भर नहीं देते, तब तक अधिकांश गैर-बहुभुज कोशिकाएं स्पष्ट रूप से नायाब होंगी और एल्गोरिथ्म ओ (k) की तरह। मध्यवर्ती मूल्यों के लिए, स्केलिंग O (k ^ 2) की तरह दिखने लगता है। जैसा कि k वास्तव में बड़ा हो जाता है, अधिकांश कोशिकाओं को घेर लिया जाएगा और पूरे पड़ोस का निरीक्षण करने से पहले इस तथ्य को अच्छी तरह से निर्धारित किया जा सकता है: एल्गोरिथम का कम्प्यूटेशनल प्रयास जिससे व्यावहारिक सीमा तक पहुंचता है। यह सीमा तब प्राप्त होती है जब पड़ोस की त्रिज्या ग्रिड में सबसे बड़े जुड़े गैर-बहुभुज क्षेत्रों के व्यास से संपर्क करती है।
एक उदाहरण के रूप में, मैंने प्रत्येक कॉल में उपयोग किए गए स्टैक संचालन की संख्या वापस करने counting
के screen
लिए प्रोटोटाइप में कोडित विकल्प का उपयोग किया है। यह कम्प्यूटेशनल प्रयास को मापता है। निम्नलिखित ग्राफ पड़ोस त्रिज्या के एक समारोह के रूप में स्टैक ऑप्स की औसत संख्या को प्लॉट करता है। यह पूर्वानुमानित व्यवहार को प्रदर्शित करता है।
हम ग्रिड पर 13 मिलियन अंकों के मूल्यांकन के लिए आवश्यक गणना का अनुमान लगाने के लिए इसका उपयोग कर सकते हैं। मान लीजिए कि k = 200/5 = 40 का एक पड़ोस उपयोग किया जाता है। तब कुछ सौ स्टैक ऑपरेशन की आवश्यकता होगी (बहुभुज ग्रिड की जटिलता के आधार पर और जहां पॉलीगोन के सापेक्ष 13 मिलियन अंक स्थित हैं), यह देखते हुए कि एक कुशल संकलित भाषा में, कुछ हज़ार सरल संख्यात्मक कार्यों पर की आवश्यकता होगी (जोड़, गुणा, पढ़, लिख, ऑफसेट, आदि)। अधिकांश पीसी उस दर पर लगभग एक लाख अंक की घेरे का मूल्यांकन करने में सक्षम होंगे। (R
कार्यान्वयन अधिक है, इससे बहुत धीमा है, क्योंकि यह इस तरह के एल्गोरिदम में खराब है, यही कारण है कि यह केवल एक प्रोटोटाइप हो सकता है।) तदनुसार, हम उम्मीद कर सकते हैं कि एक यथोचित कुशल और उपयुक्त भाषा में एक कुशल कार्यान्वयन - सी ++। और पायथन के दिमाग में आया - एक मिनट या उससे कम में 13 मिलियन अंकों के मूल्यांकन को पूरा कर सकता है, यह मानते हुए कि पूरे बहुभुज ग्रिड रैम में रहते हैं।
जब एक ग्रिड रैम में फिट होने के लिए बहुत बड़ा होता है, तो यह प्रक्रिया ग्रिड के टाइल वाले भागों पर लागू की जा सकती है। उन्हें केवल k
पंक्तियों और स्तंभों द्वारा ओवरलैप करना होगा ; परिणामों की मॉसिंग करते समय ओवरलैप्स पर मैक्सिमा लें।
अन्य अनुप्रयोगों
पानी की एक शरीर के "को लाने" बारीकी से अपने अंकों की "surroundedness" से संबंधित है। वास्तव में, यदि हम वाटरबॉडी के व्यास के बराबर या उससे अधिक के पड़ोस वाले त्रिज्या का उपयोग करते हैं, तो हम वाटरबॉडी में हर बिंदु पर (गैर-दिशात्मक) भ्रूण की एक ग्रिड बनाएंगे। एक छोटे से पड़ोस के दायरे का उपयोग करके, हम कम से कम सभी उच्चतम-भ्रूण बिंदुओं पर लाने के लिए एक कम बाध्य प्राप्त करेंगे, जो कुछ अनुप्रयोगों में काफी अच्छा हो सकता है (और कम्प्यूटेशनल प्रयास को काफी कम कर सकता है)। इस एल्गोरिथ्म का एक प्रकार जो विशिष्ट दिशाओं के संबंध में "जांच की गई" को सीमित करता है, उन दिशाओं में कुशलता से लाने के लिए एक तरीका है। ध्यान दें कि ऐसे वेरिएंट के लिए कोड को संशोधित करने की आवश्यकता होती है screen
; के लिए कोड panvisibility
बिल्कुल नहीं बदलता है।