8 votos

La descomposición de una señal discreta en una suma de funciones rectángulo

Hola matemáticas@stackexchange de la comunidad !

Tengo una simple pregunta que parece no tiene una respuesta trivial.

Dada una discreta unidimensional de la señal de $w(x)$ definido en un rango finito, y el vagón de carga (rectangular) de la función de $r(x)$

$$r(x)=\begin{cases} 1 & \mbox{if }0\leq x \leq 1; \\ 0 & \mbox{elsewhere} \end{casos}$$

Me gustaría encontrar los coeficientes $a_i,\ b_i,\ c_i $ de la suma

$$w' = \sum_{i=0}^{N}\ { a_i \cdot r\left(\frac{x}{b_i} - c_i\right)}$$

("la suma de $N$ rectángulos en cualquier rango y de cualquier altura") como $\sum_i\ \left| w_i - w_i'\right|$ es mínimo (para un determinado $N$).

Este problema parece estar relacionado con:

  1. Transformada wavelet discreta
  2. $l_1$ regularización de la solución de un sobredeterminada sistema lineal
  3. Máximo subarray problema

Sin embargo, a mi entender, no encaja en ninguno de estos casos:

  1. $r(x)$ no es un wavelet base,
  2. el problema no puede ser resuelto (en la práctica) como un sistema lineal debido a que el finito () $a_i,\ b_i,\ c_i $ valores es demasiado grande para calcular de forma explícita (longitud de $w$ en el orden de $10^4$),
  3. Desde $a_i$ es indefinido, que no encaja, como máximo, el subarray problema.

Ahora mismo tengo una solución aproximada (de forma iterativa resolver el problema a través de la máxima subarray de la formulación de la fuerza bruta de la exploración de un subconjunto de posibles $a_i$ valores), sin embargo, la idea de "la descomposición de una señal como una suma de rectángulos" parece lo suficientemente general como para pensar que alguien ya ha abordado en el pasado.

¿Alguno de ustedes tiene alguna sugerencia sobre cómo hacer frente a este problema ?

Ha sido ya resuelto en el pasado, por un método que yo no soy consciente de que ?

Muchas gracias por sus respuestas.

3voto

alex Puntos 131

Como se discute en los comentarios, he aquí una idea para un algoritmo voraz. Básicamente, el objetivo es encontrar de forma iterativa el rectángulo que se minimice $|w-w'|$ en esa situación en particular. Una combinación de un máximo de subarray y mínimo subarray algoritmo encuentra un rectángulo que viene bastante cerca, si se utiliza el promedio de la resultante subarray como $a_i$. El único problema es que los valores por debajo de la media (o por encima, por la negativa rectángulos) ya debería contar como negativo (positivo); no sé si hay una manera simple de resolver esto (y es un poco difícil de explicar, también). Una forma de reducir este problema es recortar el rectángulo si que mejora el resultado. Otra es limitar el rectángulo de longitud, como el tiempo de rectángulos tienden a tener un mayor porcentaje de los valores por debajo de la media. Puede haber mejores opciones, posiblemente dependiendo de la forma de la señal.

De todos modos, necesitaba experimentar con esto de averiguar si realmente funciona, así que aquí está el código (en C, pero debe ser fácil de puerto):

/* Type of index into signal. */
typedef unsigned int Index;

/* This should be a floating-point type. If you decide to use integers, change
   the division to round away from zero instead of towards zero. */
typedef double Value;

/* Represents a rectangle at [start,end). */
typedef struct {
    Index start, end;
    Value height;
} Rectangle;

/* Trims the rectangle by removing samples whose absolute value will increase
   when preliminaryHeight is subtracted. */
static Index optimizeRectangle(const Value signal[], Rectangle *rectangle, Value preliminaryHeight)
{
    Index result = 0;

    /* Divided into two cases for better efficiency. */
    if (preliminaryHeight >= 0) {
        while (rectangle->start + 1 < rectangle->end) {
            Value value = signal[rectangle->start];
            if (preliminaryHeight - value < value) {
                break;
            }
            rectangle->start++;
            rectangle->height -= value;
            result++;
        }
        while (rectangle->start + 1 < rectangle->end) {
            Value value = signal[rectangle->end - 1];
            if (preliminaryHeight - value < value) {
                break;
            }
            rectangle->end--;
            rectangle->height -= value;
            result++;
        }
    } else {
        while (rectangle->start + 1 < rectangle->end) {
            Value value = signal[rectangle->start];
            if (preliminaryHeight - value > value) {
                break;
            }
            rectangle->start++;
            rectangle->height -= value;
            result++;
        }
        while (rectangle->start + 1 < rectangle->end) {
            Value value = signal[rectangle->end - 1];
            if (preliminaryHeight - value > value) {
                break;
            }
            rectangle->end--;
            rectangle->height -= value;
            result++;
        }
    }

    return result;
}

/* Finds the rectangle that seems most appropriate at the moment, and whose
   length is at most maxResultLength.
   Returns the original length of the rectangle, before optimization. This
   value should be used for maxResultLength in the next iteration. If it is
   zero, the entire signal was zero. */
static Index findRectangle(const Value signal[], Index signalLength, Rectangle *result, Index maxResultLength)
{
    result->start = result->end = 0;
    result->height = 0;
    Value resultHeightAbs = 0;

    /* Start index and height of current maximum and minimum subarrays. */
    Index posStart = 0, negStart = 0;
    Value posHeight = 0, negHeight = 0;

    for (Index index = 0; index < signalLength; index++) {
        /* If the length of a subarray exceeds maxResultLength, backtrack
           to find the next best start index. */
        Index nextIndex = index + 1;
        if (nextIndex - posStart > maxResultLength && index > 0) {
            Index oldStart = posStart;
            posStart = index;
            posHeight = 0;
            Value newHeight = 0;
            for (Index newStart = index - 1; newStart > oldStart; newStart--) {
                newHeight += signal[newStart];
                if (newHeight > posHeight) {
                    posStart = newStart;
                    posHeight = newHeight;
                }
            }
        }
        if (nextIndex - negStart > maxResultLength && index > 0) {
            Index oldStart = negStart;
            negStart = index;
            negHeight = 0;
            Value newHeight = 0;
            for (Index newStart = index - 1; newStart > oldStart; newStart--) {
                newHeight += signal[newStart];
                if (newHeight < negHeight) {
                    negStart = newStart;
                    negHeight = newHeight;
                }
            }
        }

        /* Extend the subarrays. */
        Value value = signal[index];
        posHeight += value;
        negHeight += value;

        /* Throw away the entire maximum subarray if it is negative or zero. */
        if (posHeight <= 0) {
            posStart = nextIndex;
            posHeight = 0;
        } else {
            /* Update result if current maximum subarray is better. */
            if (posHeight > resultHeightAbs) {
                result->start = posStart;
                result->end = nextIndex;
                result->height = posHeight;
                resultHeightAbs = posHeight;
            }
        }
        /* Throw away the entire minimum subarray if it is positive or zero. */
        if (negHeight >= 0) {
            negStart = nextIndex;
            negHeight = 0;
        } else {
            /* Update result if current minimum subarray is better. */
            Value negHeightAbs = -negHeight;
            if (negHeightAbs > resultHeightAbs) {
                result->start = negStart;
                result->end = nextIndex;
                result->height = negHeight;
                resultHeightAbs = negHeightAbs;
            }
        }
    }

    Index resultLength = result->end - result->start;
    if (!resultLength) {
        /* Return now to avoid division by zero. */
        return 0;
    }

    /* Trim rectangle. */
    Value normalizedHeight;
    Index trimLength;
    do {
        normalizedHeight = result->height / (result->end - result->start);
        trimLength = optimizeRectangle(signal, result, normalizedHeight);
    } while (trimLength);

    /* Normalize height. */
    result->height = normalizedHeight;

    return resultLength;
}

/* Subtracts a rectangle from a signal. */
static void subtractRectangle(Value signal[], const Rectangle *rectangle)
{
    for (Index index = rectangle->start; index < rectangle->end; index++) {
        signal[index] -= rectangle->height;
    }
}

/* Decomposes a signal into rectangles. Stores at most resultLength rectangles
   in result, and returns the actual number of rectangles written. All
   rectangles are subtracted from the signal. */
unsigned int extractRectangles(Value signal[], Index signalLength, Rectangle result[], unsigned int resultLength)
{
    Index rectangleLength = signalLength;
    for (unsigned int resultIndex = 0; resultIndex < resultLength; resultIndex++) {
        Rectangle *rectangle = &(result[resultIndex]);
        rectangleLength = findRectangle(signal, signalLength, rectangle, rectangleLength);
        if (!rectangleLength) {
            return resultIndex;
        }
        subtractRectangle(signal, rectangle);
    }
    return resultLength;
}

La función principal es la extractRectangles. Devuelve el rectángulos como una matriz, pero esto se puede adaptar fácilmente. La diversión.

2voto

maksim-s Puntos 6

Queridos Raskolnikov, Brian y Sebastián, muchas gracias por compartir tus ideas.

Cruzo publicado mi pregunta en dos foros, así que usted puede comprobar la discusión completa, mirando aquí y aquí.

A partir de la discusión que tiene tres elementos principales:

  1. Haar wavelet de dar una solución a mi problema, aunque muy subóptima uno
  2. Este problema podría ser resuelto a través de un enfoque similar coincidencia de búsqueda (pero la enumeración completa de el diccionario de bases debe evitarse, ya que es demasiado grande en mi problema)
  3. Sebastián Reichelt código proporcionado por un enfoque basado en la máxima y la mínima subarray de búsqueda

Todas y todos los que terminó con cuatro diferentes soluciones para el problema presentado:

  1. explore_a indica mi original de la solución propuesta en cuestión (cuantización de la $a_i$ los valores y la resolución de múltiples máximo subarray problemas)
  2. brute_force indica que el enfoque basado en la aproximación de $\left|w' - w\right|$$\left(w' - w\right)^2$. En este enfoque, resulta que cada rectángulo posibilidad puede ser evaluado con sólo dos lecturas de memoria, una multiplicación y una división, haciendo que la fuerza bruta de exploración manejable.
  3. reichelt indica un puerto de Sebastián del código de python (que he utilizado para poner a prueba las ideas)
  4. abs_max indica un enfoque en el que en cada iteración de un rectángulo de un solo elemento, se coloca en el valor absoluto máximo de la señal, es seleccionado.

Todo el código de la aplicación de estos métodos y algunos ejemplos de los resultados están disponibles en http://lts.cr/Hzg

En cada uno de ejecutar el código generará un nuevo "al azar" de la señal (con un ruidoso paso). Un ejemplo de la señal (con la etiqueta a[0:n]) y el primer rectángulo seleccionado por cada método puede ser visto a continuación. Example signal

A continuación, cada método se ejecuta de forma recursiva a la aproximación de la señal de entrada, hasta la aproximación de error es muy baja o el número máximo de iteraciones que se han alcanzado.

En el resultado típico se puede ver a continuación (y otro aquí) Example result 1

Después de ejecutar el código de tiempo múltiples (para diferentes señales aleatorias), el comportamiento parece coherente:

  1. Como era de esperar, abs_max alcanza la convergencia en un número de iteraciones igual a la longitud de la señal. Lo hace bastante rápido (decaimiento exponencial).
  2. explore_a disminución de la energía rápida al principio y luego se tiende a estancarse.
  3. reichelt es consistentemente peor que explore_ade quedarse atascado en un nivel de energía más alto. Espero que no tuve que hacer ningún tonto error en el puerto de la C a Python. Mediante inspección visual el primer rectángulo seleccionado parece razonable.
  4. brute_force está constantemente el método que disminuye la energía de la forma más rápida. También es constantemente se cruza con el abs_max solución, lo que indica que una mejor estrategia sería la de cambiar de un método a otro.

Obviamente el comportamiento exacto de los cambios de ejecutar para ejecutar y ciertamente cambiaría dependiendo del método utilizado para generar el "azar" de la señal. Sin embargo creo que estos resultados iniciales son un buen indicador. Yo creo que es razonable ahora a proceder a generar mis datos reales, y evaluar qué tan bien/fast brute_force y explore_a de ejecución no.

Siéntase libre de añadir comentarios o jugar con el código.

De nuevo, muchas gracias por sus aportaciones y puntos de vista !

2voto

alex Puntos 131

Experimenté con su código un poco, y encontré tantas cosas que pensé que debería acaba de publicar otra respuesta:

  • Como se dijo en un comentario, usted cometió un error cuando portado mi código. (Por CIERTO, me refería a la "convergencia" en lugar de "conversión", por supuesto).

  • El explore_a función no hace lo que dice en la lata (así que se las arregló para romper su propio código, supongo :-)). Ya que se utiliza directamente el valor de la máxima subarray como la puntuación, sólo las dos instancias de a_value más cercano a cero se ha tomado alguna vez. Es necesario calcular el efecto en $|w-w'|$ lugar.

  • No muy a la altura de su descripción: El verdadero efecto de la a_value es que convierte las muestras positivas por debajo de a_value en las muestras negativas (y niega la matriz si es negativo). Sin embargo, esto es realmente una buena solución para el problema que he tratado de describir en mi primera respuesta. Así que me decidí a corregir el código; ahora parece que funciona realmente bien.

  • Yo también creo que el cero siempre debe ser tratado como un a_value, con y sin negar la señal (que es equivalente a encontrar el máximo y el mínimo subarray, al igual que en mi primer algoritmo).

  • Esto se puede combinar tanto con restricción de longitud y de recorte, como se describe en mi primera respuesta. Restricción de longitud resulta que produce peores resultados, y también es bastante caro. El único efecto positivo es que se degrada hasta el "máximo valor absoluto" algoritmo una vez que la longitud máxima se convierte en 1, por lo que se llega a una convergencia más rápida. El recorte tiende a ayudar un poco, pero (como en cada algoritmo greedy) un mejor resultado en un solo paso puede conducir a peores resultados en los pasos siguientes.

Aquí está un gráfico actualizado (de una señal diferente, ya que desafortunadamente no usar fija semillas aleatorias, y con diferente orden y los colores, porque ellos son los elegidos automáticamente): Updated graph, signal length 100explore_a está oculto detrás de explore_a_opt (es decir, con recorte), y explore_a_len (con restricción de longitud) está oculto detrás de explore_a_len_opt.

Mirando este gráfico, un par de cosas más vienen a la mente:

  • El fijo explore_a parece producir mejores resultados que brute_force (que en realidad es un nombre engañoso porque es realmente un algoritmo voraz, sólo con la fuerza bruta en cada paso). Supongo que esto significa que brute_force no acaba de hacer lo que se supone que es para cualquiera.

  • La señal de prueba no es en particular muy adecuado para la aproximación a la utilización de rectángulos. Esencialmente, es ruido blanco con dos rectángulos añadido. Una vez que los algoritmos que se han "encontrado" estos rectángulos, que no pueden hacer mucho mejor que abs_max más.

  • Probablemente tiene más sentido para comparar los algoritmos en un número de pasos que es significativamente menor que la longitud de la señal. (De lo contrario, ¿cuál es el punto?)

Así que aquí es un gráfico que pertenecen a una señal de longitud 500 con más contenido de baja frecuencia, mostrando los 100 primeros pasos: New graph, signal length 500

Por último, dado que todos los algoritmos son codiciosos, creo que se debe comparar a algo como el recocido simulado (como lo he mencionado en los comentarios).

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X