¿Cómo me las arreglaría para realizar math.ceil de manera que se asigne un número a la siguiente potencia más alta de 10?
# 0.04 -> 0.1 # 0.7 -> 1 # 1.1 -> 10 # 90 -> 100 # ...Mi solución actual es un diccionario que verifica el rango del número de entrada, pero está codificado y preferiría una solución de una sola línea. ¿Quizás me estoy perdiendo un truco matemático simple o una función numérica correspondiente aquí?
Puede usar math.ceil con math.log10 para hacer esto:
>>> 10 ** math.ceil(math.log10(0.04)) 0.1 >>> 10 ** math.ceil(math.log10(0.7)) 1 >>> 10 ** math.ceil(math.log10(1.1)) 10 >>> 10 ** math.ceil(math.log10(90)) 100 log10(n) te da la solución x que satisface 10 ** x == n , así que si redondeas x te da el exponente de la siguiente potencia más alta de 10.
Tenga en cuenta que para un valor n donde x ya es un número entero, la "siguiente potencia más alta de 10" será n :
>>> 10 ** math.ceil(math.log10(0.1)) 0.1 >>> 10 ** math.ceil(math.log10(1)) 1 >>> 10 ** math.ceil(math.log10(10)) 10Su problema está subespecificado, necesita dar un paso atrás y hacer algunas preguntas.
En otra respuesta, se propuso tomar el logaritmo, luego redondear (función de techo) y luego exponenciar.
def nextpow10(n): return 10 ** math.ceil(math.log10(n))Desafortunadamente, esto adolece de errores de redondeo. En primer lugar, n se convierte de cualquier tipo de datos que tenga en un número de coma flotante de doble precisión, lo que podría introducir errores de redondeo, luego se calcula el logaritmo, lo que podría introducir más errores de redondeo tanto en sus cálculos internos como en su resultado.
Como tal, no me llevó mucho tiempo encontrar un ejemplo en el que diera un resultado incorrecto.
>>> import math >>> from numpy import nextafter >>> n = 1 >>> while (10 ** math.ceil(math.log10(nextafter(n,math.inf)))) > n: ... n *= 10 ... >>> n 10 >>> nextafter(n,math.inf) 10.000000000000002 >>> 10 ** math.ceil(math.log10(10.000000000000002)) 10También es teóricamente posible que falle en la otra dirección, aunque esto parece mucho más difícil de provocar.
Entonces, para una solución robusta para flotantes e enteros, debemos asumir que el valor de nuestro logaritmo es solo aproximado y, por lo tanto, debemos probar un par de posibilidades. Algo en la línea de
def nextpow10(n): p = round(math.log10(n)) r = 10 ** p if r < n: r = 10 ** (p+1) return r;Creo que este código debería dar resultados correctos para todos los argumentos en un rango sensible de magnitudes del mundo real. Se interrumpirá para cantidades muy pequeñas o muy grandes de tipos que no sean enteros ni de coma flotante debido a problemas para convertirlos a coma flotante. Python aplica argumentos enteros especiales a la función log10 en un intento de evitar el desbordamiento, pero aun así, con un entero lo suficientemente masivo, es posible forzar resultados incorrectos debido a errores de redondeo.
Para probar las dos implementaciones, utilicé el siguiente programa de prueba.
n = -323 # 10**-324 == 0 while n < 1000: v = 10 ** n if v != nextpow10(v): print(str(v)+" bad") try: v = min(nextafter(v,math.inf),v+1) except: v += 1 if v > nextpow10(v): print(str(v)+" bad") n += 1Esto encuentra muchas fallas en la implementación ingenua, pero ninguna en la implementación mejorada.
¡Mira esto!
>>> i = 0.04123 >>> print i, 10 ** len(str(int(i))) if int(i) > 1 else 10 if i > 1.0 else 1 if i > 0.1 else 10 ** (1 - min([("%.100f" % i).replace('.', '').index(k) for k in [str(j) for j in xrange(1, 10) if str(j) in "%.100f" % i]])) 0.04123 0.1 >>> i = 0.712 >>> print i, 10 ** len(str(int(i))) if int(i) > 1 else 10 if i > 1.0 else 1 if i > 0.1 else 10 ** (1 - min([("%.100f" % i).replace('.', '').index(k) for k in [str(j) for j in xrange(1, 10) if str(j) in "%.100f" % i]])) 0.712 1 >>> i = 1.1 >>> print i, 10 ** len(str(int(i))) if int(i) > 1 else 10 if i > 1.0 else 1 if i > 0.1 else 10 ** (1 - min([("%.100f" % i).replace('.', '').index(k) for k in [str(j) for j in xrange(1, 10) if str(j) in "%.100f" % i]])) 1.1 10 >>> i = 90 >>> print i, 10 ** len(str(int(i))) if int(i) > 1 else 10 if i > 1.0 else 1 if i > 0.1 else 10 ** (1 - min([("%.100f" % i).replace('.', '').index(k) for k in [str(j) for j in xrange(1, 10) if str(j) in "%.100f" % i]])) 90 100 Este código se basa en el principio de la potencia de diez en len(str(int(float_number))) .
Hay 4 casos:
int(i) > 1 .
Número Float : convertido a int , a partir de ahí, la cadena str() nos dará una string con la length que estamos buscando exactamente. Entonces, la primera parte, para la entrada i > 1.0 , es diez 10 en potencia de esta longitud.
i > 1.0 e i > 0.1 <=> es 10 y 1 respectivamente.i < 0.1 : Aquí, diez estará en potencia negativa. Para obtener el primer elemento distinto de cero después de la coma, he usado dicha construcción ("%.100f" % i).replace('.', '').index(k) , donde k se ejecuta en el intervalo [1:10] . A partir de entonces, tome el mínimo de la lista de resultados. Y disminuir en uno, es el primer cero, que se contará. Además, aquí el index() puede fallar, si no encuentra al menos uno de los elementos distintos de cero del intervalo [1:10] , es por eso que al final debo "filtrar" la lista por ocurrencia: if str(j) in "%.100f" % i . Además, para obtener una precisión más profunda, %.100f puede tomarse de manera diferente.Parece que desea más bien la próxima potencia más baja de 10 ... Aquí hay una forma de usar matemáticas puras y sin registro, sino recursividad.
def ceiling10(x): if (x > 10): return ceiling10(x / 10) * 10 else: if (x <= 1): return ceiling10(10 * x) / 10 else: return 10 for x in [1 / 1235, 0.5, 1, 3, 10, 125, 12345]: print(x, ceiling10(x))y = math.ceil(x) z = y + (10 - (y % 10))¿Algo como esto tal vez? Está fuera de mi cabeza, pero funcionó cuando probé algunos números en la terminal.
Creo que la forma más sencilla es:
import math number = int(input('Enter a number: ')) next_pow_ten = round(10 ** math.ceil(math.log10(number))) print(str(10) + ' power ' + str(round(math.log10(number))) + ' = '\ + str(next_pow_ten))Espero que esto te ayude.