Cualquier verdaderamente de propósito general eficaz método de estandarizar la representación de las formas , de modo que no cambiarán después de la rotación, traslación, reflexión o trivial cambios en la representación interna.
Una manera de hacer esto es hacer una lista de cada uno de ellos conectado forma como una secuencia alternante de borde y longitudes (firmado) los ángulos, desde uno de los extremos. (La forma debe ser "limpio", en el sentido de no tener longitud cero bordes o ángulos rectos.) Para hacer esta invariante bajo la reflexión, niega todos los ángulos si el primer cero uno es negativo.
(Debido a que cualquier conectados polilínea de n vértices tiene n-1 bordes separados por n-2 ángulos, he encontrado que es conveniente en la R
código a continuación para usar una estructura de datos que consta de dos matrices, una para el borde longitudes $lengths
y el otro para los ángulos, $angles
. Un segmento de línea no tendrá ningún ángulos en todos, por lo que es importante tratar de longitud cero matrices en una estructura de datos.)
Tales representaciones pueden ser ordenados lexicográficamente. Algunos asignación debe ser hecha por errores de punto flotante acumulada durante el proceso de normalización. Un elegante procedimiento de estimación de los errores como una función de las coordenadas originales. En la solución a continuación, un método más simple que se utiliza en el cual dos longitudes son considerados iguales cuando difieren por una cantidad muy pequeña en términos relativos. Los ángulos pueden diferir sólo por una cantidad muy pequeña en forma absoluta.
Para hacerlos invariantes bajo la inversión de la orientación subyacente, elija el lexicográficamente representación más antigua entre la de la polilínea y su reversión.
Manejar multi-parte polilíneas, arreglar sus componentes en orden lexicographic.
Para encontrar las clases de equivalencia bajo Euclidiana transformaciones, a continuación,
Crear estandarizada de las representaciones de las formas.
Realizar un lexicográfica de ordenación de la normalización de las representaciones.
Hacer un pase a través de la orden para identificar las secuencias de la igualdad de las representaciones.
El tiempo de cálculo es proporcional a O(n*log(n)*N), donde n es el número de características y N es el número más grande de los vértices de cualquier característica. Esto es eficiente.
Es probablemente vale la pena mencionar de paso que una agrupación preliminar basado en fácilmente calculada invariante propiedades geométricas, tales como la polilínea de longitud, centro, y momentos alrededor de ese centro, a menudo puede ser aplicado a agilizar todo el proceso. Uno sólo necesita encontrar subgrupos de congruencia de las características dentro de cada grupo preliminar. El método completo que se dan aquí sería necesario para las formas que de otra manera sería tan extraordinariamente similar que estas simples invariantes todavía no los distinguen. Características simples construido a partir de una trama de datos podría tener tales características, por ejemplo. Sin embargo, ya que la solución que aquí es tan eficiente de todos modos, que si uno se va a ir al esfuerzo de implementación, se puede trabajar bien por sí mismo.
Ejemplo
La mano izquierda de la figura muestra cinco polilíneas, además de 15 más que se obtuvieron a partir de los a través de random traslación, rotación, reflexión, y la reversión de la orientación interna (que no es visible). La mano derecha de la figura colores de acuerdo a sus Euclidiana equivalencia de la clase: todas las cifras de un color común son congruentes; diferentes colores no son congruentes.
R
código de la siguiente manera. Cuando las entradas se han actualizado para 500 formas, 500 extra (congruentes) las formas, con una media de 100 vértices por la forma, el tiempo de ejecución en esta máquina fue de 3 segundos.
Este código es incompleta: porque R
no tiene una unidad de ordenación lexicográfica, y yo no tenía ganas de codificación uno desde cero, yo simplemente realizar la clasificación en la primera coordenada de cada forma estandarizada. Que estará bien por el azar de las formas creadas aquí, pero para el trabajo de producción de un total de ordenación lexicográfica debe ser implementado. La función order.shape
sería el único afectado por este cambio. Su entrada es una lista de forma estandarizada s
, y su salida es la secuencia de los índices en s
que tipo es.
#
# Create random shapes.
#
n.shapes <- 5 # Unique shapes, up to congruence
n.shapes.new <- 15 # Additional congruent shapes to generate
p.mean <- 5 # Expected number of vertices per shape
set.seed(17) # Create a reproducible starting point
shape.random <- function(n) matrix(rnorm(2*n), nrow=2, ncol=n)
shapes <- lapply(2+rpois(n.shapes, p.mean-2), shape.random)
#
# Randomly move them around.
#
move.random <- function(xy) {
a <- runif(1, 0, 2*pi)
reflection <- sign(runif(1, -1, 1))
translation <- runif(2, -8, 8)
m <- matrix(c(cos(a), sin(a), -sin(a), cos(a)), 2, 2) %*%
matrix(c(reflection, 0, 0, 1), 2, 2)
m <- m %*% xy + translation
if (runif(1, -1, 0) < 0) m <- m[ ,dim(m)[2]:1]
return (m)
}
i <- sample(length(shapes), n.shapes.new, replace=TRUE)
shapes <- c(shapes, lapply(i, function(j) move.random(shapes[[j]])))
#
# Plot the shapes.
#
range.shapes <- c(min(sapply(shapes, min)), max(sapply(shapes, max)))
palette(gray.colors(length(shapes)))
par(mfrow=c(1,2))
plot(range.shapes, range.shapes, type="n",asp=1, bty="n", xlab="", ylab="")
invisible(lapply(1:length(shapes), function(i) lines(t(shapes[[i]]), col=i, lwd=2)))
#
# Standardize the shape description.
#
standardize <- function(xy) {
n <- dim(xy)[2]
vectors <- xy[ ,-1, drop=FALSE] - xy[ ,-n, drop=FALSE]
lengths <- sqrt(colSums(vectors^2))
if (which.min(lengths - rev(lengths))*2 < n) {
lengths <- rev(lengths)
vectors <- vectors[, (n-1):1]
}
if (n > 2) {
vectors <- vectors / rbind(lengths, lengths)
perps <- rbind(-vectors[2, ], vectors[1, ])
angles <- sapply(1:(n-2), function(i) {
cosine <- sum(vectors[, i+1] * vectors[, i])
sine <- sum(perps[, i+1] * vectors[, i])
atan2(sine, cosine)
})
i <- min(which(angles != 0))
angles <- sign(angles[i]) * angles
} else angles <- numeric(0)
list(lengths=lengths, angles=angles)
}
shapes.std <- lapply(shapes, standardize)
#
# Sort lexicographically. (Not implemented: see the text.)
#
order.shape <- function(s) {
order(sapply(s, function(s) s$lengths[1]))
}
i <- order.shape(shapes.std)
#
# Group.
#
equal.shape <- function(s.0, s.1) {
same.length <- function(a,b) abs(a-b) <= (a+b) * 1e-8
same.angle <- function(a,b) min(abs(a-b), abs(a-b)-2*pi) < 1e-11
r <- function(u) {
a <- u$angles
if (length(a) > 0) {
a <- rev(u$angles)
i <- min(which(a != 0))
a <- sign(a[i]) * a
}
list(lengths=rev(u$lengths), angles=a)
}
e <- function(u, v) {
if (length(u$lengths) != length(v$lengths)) return (FALSE)
all(mapply(same.length, u$lengths, v$lengths)) &&
all(mapply(same.angle, u$angles, v$angles))
}
e(s.0, s.1) || e(r(s.0), s.1)
}
g <- rep(1, length(shapes.std))
for (j in 2:length(i)) {
i.0 <- i[j-1]
i.1 <- i[j]
if (equal.shape(shapes.std[[i.0]], shapes.std[[i.1]]))
g[j] <- g[j-1] else g[j] <- g[j-1]+1
}
palette(rainbow(max(g)))
plot(range.shapes, range.shapes, type="n",asp=1, bty="n", xlab="", ylab="")
invisible(lapply(1:length(i), function(j) lines(t(shapes[[i[j]]]), col=g[j], lwd=2)))