Forma rápida / eficiente de descomponer coeficientes de filtro 2D enteros separables


21

Me gustaría poder determinar rápidamente si un núcleo 2D dado de coeficientes enteros es separable en dos núcleos 1D con coeficientes enteros. P.ej

 2   3   2
 4   6   4
 2   3   2

es separable en

 2   3   2

y

 1
 2
 1

La prueba real de separabilidad parece ser bastante sencilla utilizando la aritmética de enteros, pero la descomposición en filtros 1D con coeficientes enteros está demostrando ser un problema más difícil. La dificultad parece radicar en el hecho de que las relaciones entre filas o columnas pueden ser no enteras (fracciones racionales), por ejemplo, en el ejemplo anterior tenemos relaciones de 2, 1/2, 3/2 y 2/3.

Realmente no quiero usar un enfoque de servicio pesado como SVD porque (a) es relativamente computacionalmente costoso para mis necesidades y (b) todavía no necesariamente ayuda a determinar los coeficientes enteros .

Algunas ideas ?


MÁS INFORMACIÓN

Los coeficientes pueden ser positivos, negativos o cero, y puede haber casos patológicos en los que la suma de uno o ambos vectores 1D sea cero, p. Ej.

-1   2  -1
 0   0   0
 1  -2   1

es separable en

 1  -2   1

y

-1
 0
 1

1
Recuerdo haber intentado resolver esto en la universidad. Casi lo logré, pero no recuerdo cómo. =) ¡No puedo dejar de pensar en eso ahora que lo mencionaste!
Phonon

@Phonon: je, bueno, sigue pensando: podría usar algo de inspiración en este caso. ;-)
Paul R

¿Es posible hacer lo mismo pero con valores dobles o flotantes?
Diego Catalano

@DiegoCatalano: vea la respuesta de Denis a continuación, y la pregunta a la que se vincula en math.stackexchange.com: creo que podría funcionar para el caso más general de coeficientes de coma flotante.
Paul R

@PaulR, ¿cómo podría uno contactarlo por correo electrónico? Gracias.
Royi

Respuestas:


11

Tomé @Phononla respuesta y la modifiqué un poco para que use el enfoque GCD solo en la fila superior y la columna izquierda, en lugar de en las sumas de fila / columna. Esto parece manejar los casos patológicos un poco mejor. Todavía puede fallar si la fila superior o la columna izquierda son todos ceros, pero estos casos se pueden verificar antes de aplicar este método.

function [X, Y, valid] = separate(M)    % separate 2D kernel M into X and Y vectors 
  X = M(1, :);                          % init X = top row of M
  Y = M(:, 1);                          % init Y = left column of M
  nx = numel(X);                        % nx = no of columns in M
  ny = numel(Y);                        % ny = no of rows in M
  gx = X(1);                            % gx = GCD of top row
  for i = 2:nx
    gx = gcd(gx, X(i));
  end
  gy = Y(1);                            % gy = GCD of left column
  for i = 2:ny
    gy = gcd(gy, Y(i));
  end
  X = X / gx;                           % scale X by GCD of X
  Y = Y / gy;                           % scale Y by GCD of Y
  scale = M(1, 1) / (X(1) * Y(1));      % calculate scale factor
  X = X * scale;                        % apply scale factor to X
  valid = all(all((M == Y * X)));       % result valid if we get back our original M
end

Muchas gracias a @Phonony @Jason Rpor las ideas originales para esto.


10

¡Lo tengo! Al publicar el código MATLAB, publicaremos una explicación esta noche o mañana

% Two original arrays
N = 3;
range = 800;
a = round( range*(rand(N,1)-0.5) )
b = round( range*(rand(1,N)-0.5) )

% Create a matrix;
M = a*b;
N = size(M,1);

% Sanity check
disp([num2str(rank(M)) ' <- this should be 1!']);

% Sum across rows and columns
Sa = M * ones(N,1);
Sb = ones(1,N) * M;

% Get rid of zeros
SSa = Sa( Sa~=0 );
SSb = Sb( Sb~=0 );

if isempty(SSa) | isempty(SSb)
    break;
end

% Sizes of array without zeros
Na = numel(SSa);
Nb = numel(SSb);

% Find Greatest Common Divisor of Sa and Sb.
Ga = SSa(1);
Gb = SSb(1);

for l=2:Na
    Ga = gcd(Ga,SSa(l));
end

for l=2:Nb
    Gb = gcd(Gb,SSb(l));
end

%Divide by the greatest common divisor
Sa = Sa / Ga;
Sb = Sb / Gb;

%Scale one of the vectors
MM = Sa * Sb;
Sa = Sa * (MM(1) / M(1));

disp('Two arrays found:')
Sa
Sb
disp('Sa * Sb = ');
Sa*Sb
disp('Original = ');
M

Gracias, esto es genial, estuve despierto anoche pensando en factorizar los coeficientes, etc., pero usar el GCD de esta manera es mucho más simple y elegante. Desafortunadamente, todavía hay una arruga para planchar: necesita trabajar con coeficientes positivos y negativos y esto puede conducir a casos degenerados, por ejemplo A=[-2 1 0 -1 2]; B=[2 -3 6 0 -1]; M=A'*B;. El problema aquí es que sum(A) = 0Sb = [0 0 0 0 0]. Voy a intentar modificar su algoritmo para que use la suma de valores absolutos de los coeficientes y ver si eso ayuda. De nuevo, gracias por tu ayuda.
Paul R

OK - parece que todavía puede obtener los GCDs y hacer el escalado mediante el uso abs(M), es decir, Sa=abs(M)*ones(N,1); Sb=ones(1,N)*abs(M);y luego siga las indicaciones anteriores, pero todavía no puede ver cómo restaurar las señales para Sa, Sbal final. He agregado un ejemplo patológico que ilustra el problema en la pregunta original anterior.
Paul R

Creo que ahora tengo una solución que funciona: la publiqué como una respuesta separada, pero el crédito es para usted por la idea subyacente. Gracias de nuevo !
Paul R

7

Tal vez estoy trivializando el problema, pero parece que podrías:

  • NMAaii=0,1,,N1
  • j>0

    • aja0jrj
    • rj
    • rjaja0j0x
    • aja0
  • Si se consideró que todas las filas eran múltiplos constantes de la fila 0 en las pruebas anteriores, entonces tome la lista de escalares de relación de filas x

xk,norm=xkmini=0N1xi
  • xnorm
    xscaled=Kxnorm,K=1,2,,M
    KM

No es el método más elegante, y es probable que haya una mejor manera, pero debería funcionar, es bastante simple de implementar y debería ser relativamente rápido para matrices de tamaño modesto.


Gracias. Creo que probablemente me estaba dirigiendo en algo como esta dirección antes de que me atascara en los detalles. No es 100% claro para mí que siempre llegue a una solución usando este método, pero de todos modos, probablemente debería codificar esto y probarlo con algunos ejemplos. Tengo el presentimiento de que puede ser necesario aplicar tanto en fila como en columna para ver cuál produce la "mejor" solución. Gracias por tomarse el tiempo para explicar los detalles. Me ocuparé de ello y le haré saber cómo funciona.
Paul R

¿No podría encontrar el máximo divisor común de los primeros elementos de las filas y utilizarlo para determinar su vector base?
Jim Clay

@ JimClay: Sí, eso es efectivamente lo que estás haciendo al final, si tienes esa funcionalidad disponible.
Jason R

3

xyzA|Axyz|
x y z
yzxx y z x y z ... en turno.

(De aproximado-a-convolution-as-a-sum-of-separable-convolutions on math.stackexchange).


1
Intenta no responder preguntas con enlaces inexplicables. Es mejor explicar los detalles necesarios en su respuesta e incluir el enlace solo como referencia; de esa manera, si el enlace se rompe, los detalles esenciales de la respuesta siguen ahí.
Sam Maloney

@SamMaloney: No veo ninguna razón por la que sea necesario. El enlace explica todo en detalle. Seguirá apareciendo en la búsqueda de preguntas y respuestas. ¿Entonces por qué no?
Naresh

1
@Naresh Solo lo menciono porque uno de los objetivos de los sitios de intercambio de stack es construir un repositorio de preguntas respondidas para referencia futura. Entonces, si bien entiendo que este enlace en particular es a otro sitio de SE y debería ser bastante seguro, es una práctica recomendada general no contar con enlaces que aún funcionen dentro de varios años. . Dando un esquema general de estas "dos métodos fáciles en la respuesta sería asegurar que la información se mantiene incluso si algo le sucede a la cuestión vinculada Como ya he dicho, sin embargo, esto era más de una observación general sobre las mejores prácticas en materia de eslabones de respuestas.
Sam Maloney
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.