Pregunta:
¿Cuál es la implementación más eficiente de map (x, 0,1023,50,250)?
Cybergibbons
2014-03-26 04:33:33 UTC
view on stackexchange narkive permalink

Recientemente noté cómo la función map () en Arduino era voluminosa tanto en términos de espacio flash utilizado como en tiempo de ejecución, en gran parte porque trata con long e implica una división y multiplicación.

Muchas veces puedes reemplazar mapas con funciones más básicas, es decir,

  output = map (input, 0,1023,0,255); 

puede convertirse en

  output = input >> 2;  

o

  output = map (input, 0,1023,1023,0);  

puede convertirse en

  output = 1023 - input;  

Tengo una línea en un código que dice:

  backlight = map (LDRreading, 0,1023,50,250);  

¿Cómo podría simplificarse esto para que sea eficiente tanto en el espacio como en el tiempo?

Permitiré ligeras diferencias en los valores de salida si resulta en una solución mucho mejor.

Cuatro respuestas:
#1
+9
Ignacio Vazquez-Abrams
2014-03-26 05:10:14 UTC
view on stackexchange narkive permalink

Scaling calculation

La captura de pantalla muestra un resultado en base 2 (es decir, binario) del cálculo de la parte constante de un mapeo. Puede aproximar esta multiplicación por una constante sumando un número de turnos. Primero debes desglosar cada multiplicación en el resultado:

x * 0.001 2 = x >> 3

x * 0.0001 2 = x >> 4

x * 0.0000001 2 = x >> 7

Luego súmelos. Básicamente, lo está dividiendo en convenientes multiplicaciones de base 2 (que siempre se pueden representar por cambios) y sumas, casi siempre más eficiente.

Esto no lo lleva directamente a 250, pero bastante cerca:

  >>> para i en el rango (0, 1024, 34): ... print (i >> 3) + (i >>weg 4) + (p. [snip] 235241247>>> print (1023 >> 3) + (1023 >> 4) + (1023 Ailiwexnpqnpqgt print (1023 >> 3) + (1023 >> 4) + (1023 Ailiwexnpqt code> ( ADLAR  = 1) sin embargo, para minimizar los errores. El ADC devuelve un resultado de 10 bits y ADLAR elige si está alineado a la izquierda o la derecha de los dos registros de resultados. 
Gran respuesta. He aclarado cómo pasó del resultado de la calculadora al código, espero que esté bien.
Esta es una técnica excelente en general, pero el rango original era 1024, no 1023 (los 0 recuentos).
#2
+4
microtherion
2014-03-26 17:10:17 UTC
view on stackexchange narkive permalink

¿Qué tal esto, mapear a 50..249?

  output = ((input * 25) >> 7) +50;  

Su el rango de entrada era 1024 (0..1023). El rango de salida es 200 (en la especificación original, habría sido 201, que no se divide tan claramente). Estos tienen un mcd de 8, por lo que output = (input * (200/8)) / (1024/8) + 50 servirá, y la división por una potencia de 2 se puede expresar con un cambio. Lo bueno es que 1024 * 25 todavía encaja en un entero de 16 bits, por lo que no se necesitan largos.

Si desea un rango completo, puede intentar redondear

  salida = ((entrada * 25 + 64) >> 7) +50;  
En realidad, diría que el rango de entrada es 201 ya que se incluyen ambos límites.
@jfoilpret, Estoy de acuerdo, por eso dije que mi solución (al menos sin el redondeo) solo cubre 50..249.
Ah, vale, lo siento, no entendí bien tu declaración "el rango de salida es 200"; tal vez debería agregar "(en lugar del esperado 201)" que aclararía su respuesta.
#3
+2
Peter Bloomfield
2014-03-26 04:58:23 UTC
view on stackexchange narkive permalink

Podrías aproximarlo bastante usando solo dos operaciones enteras:

  backlight = (LDRreading / 5) + 46;  

Eso mapea efectivamente la entrada rango 0 - 1023 en el rango de salida 46 - 250, por lo que es bastante preciso. Si LDRreading es un tipo entero de 2 bytes, entonces debería ser significativamente más eficiente (hablando comparativamente) que cualquier cosa que use long .

El problema con la división por un número que no es una potencia de 2 es que el AVR no tiene instrucciones para ello, por lo tanto, el compilador tiene que generar un montón de instrucciones para este cálculo, lo que hace que la fórmula sea más larga de calcular.
#4
+1
Tonny
2014-03-26 20:15:43 UTC
view on stackexchange narkive permalink

Si el rango de entrada es de tamaño limitado y tienes la memoria para ello, nada mejor que una simple tabla de búsqueda.

como
unsigned char map [1024] = {50, 50, << más entradas aquí >>, 250};

Puede precalcular el contenido en una parte del programa que no es crítica en el tiempo o simplemente calcular todo el contenido de la tabla de antemano y ponerlo literalmente en el código fuente.

Hacer un mapa es solo una lectura de map [] con el valor original como índice.

Si ingresa el rango comienza con un número distinto de cero, simplemente disminuya todas las entradas por el valor del límite inferior para tiene un índice base 0 para la matriz de mapas. (¡Eso también funciona para límites inferiores negativos!)

Como máximo 1 resta (para ajustar un límite inferior distinto de cero) y 1 adición para calcular el índice base de matriz +.
Y un buen compilador podrá optimizarlo aún más.

No puede ser más eficiente que eso, pero necesita el espacio para guardar la mesa en algún lugar.

Sin embargo, esto es extremadamente ineficiente en cuanto al espacio y solo ligeramente más rápido que el cambio / adición que se acerca mucho.
@Cybergibbons Es sólo marginalmente sí, pero la pregunta era sobre la eficiencia. Si cada ciclo cuenta ... La eficiencia del espacio podría ser un problema para tablas más grandes, pero ya lo mencioné en mi respuesta. Publiqué esto principalmente como una respuesta más generalizada porque muchas personas no se dan cuenta de que esta técnica existe en absoluto. Y tiene una aplicación mucho más amplia que la de Arduino. (Verifique la implementación de su biblioteca C de isspace (), isalpha (). La mayoría de las implementaciones usan esta técnica ampliamente).
"tanto en el espacio como en el tiempo" era la pregunta :). isalpha () ciertamente no usa una tabla de búsqueda en avr-libc. Tiene mucha memoria: 1 Kbyte de RAM (1/2) o flash (1/32).
@Cybergibbons Embedded Libc hace (principalmente) las cosas de manera diferente, pero las genéricas a menudo hacen un uso extensivo de las tablas de búsqueda. Tengo que admitir que me perdí la palabra "espacio" en mi lectura inicial de la pregunta.


Esta pregunta y respuesta fue traducida automáticamente del idioma inglés.El contenido original está disponible en stackexchange, a quien agradecemos la licencia cc by-sa 3.0 bajo la que se distribuye.
Loading...