Dado un conjunto de enteros (por ejemplo, {1000000, 20000000, 1234000, 1200000}), quiero aplicar a todos ellos una función tal que:

  1. la magnitud de un número se reduce lo más posible
  2. todos los números siguen siendo enteros
  3. sus proporciones relativas permanecen constantes

En otras palabras, quiero eliminar tantos ceros como sea posible sin perder ninguna información que no sea de magnitud absoluta, para que el conjunto se convierta en {1000, 20000, 1234, 1200}

¿Hay un término para esta operación y hay una función eficiente de Python, o debería codificar esto rápidamente?

Editar: Esta solución no es un duplicado porque trata con números singulares: en mi caso, el número de ceros depende del conjunto particular.

Edición 2: Green Cloak Guy proporcionó una solución para mis requisitos exactos, e Illmora, que hace lo que debería haber conceptualizado en primer lugar.

1
Zubo 9 oct. 2019 a las 00:02

6 respuestas

La mejor respuesta

Dado que todo lo que le importa aquí es la disminución de la magnitud, ¿ha considerado simplemente representar sus números como Decimal sy luego imprimirlos en notación científica?

from decimal import Decimal

nums = {Decimal(1000000), Decimal(20000000), Decimal(1234000), Decimal(1200000)}
print({str(num.normalize()) for num in nums})
# {'1E+6', '1.2E+6', '2E+7', '1.234E+6'}

Si eso no es razonable para su caso de uso, entonces otra cosa que puede hacer es esencialmente determinar la magnitud máxima por la que puede reducir, y luego reducir esa cantidad. Para magnitudes de 10, esto es bastante simple y puede usar cadenas para hacerlo:

nums = {1000000, 20000000, 1234000, 1200000}
div_factor = 10 ** min(len(str(num)) - len(str(num).rstrip('0')) for num in nums)
reduced_nums = {num / div_factor for num in nums}
# {1000.0, 1234.0, 20000.0, 1200.0}
# you can use integer division `//` instead of true division `/` if you want

Para magnitudes no estándar (por ejemplo, magnitudes de 3), necesitaría ser más creativo y encontrar una manera de calcular de manera eficiente la magnitud más grande por la que puede dividir. Mi ejemplo anterior toma un atajo aquí al verificar cuántos dígitos desaparecen cuando recortamos los ceros finales (que es equivalente a verificar el mayor exponente de 10 que puede dividirse en números enteros). Dado que Python no tiene una forma integrada de imprimir en bases que no son 2, 8, 10 o 16, tendrá que encontrar su propia solución.

2
Green Cloak Guy 8 oct. 2019 a las 21:23

Mirando sus requisitos, lo que busca puede hacerse fácilmente dividiendo cada número de entrada por el MCD (mayor denominador común) de todos los números de entrada.

#!/usr/bin/env python3

import math
from functools import reduce

numbers = [1000000, 20000000, 1234000, 1200000]

# Find the greatest common denominator
gcd = reduce(lambda x,y: math.gcd(x,y), numbers)

# Divide each number by the GCD
minimum_numbers = map(lambda x: int(x/gcd), numbers)

print(*minimum_numbers, sep=',')

Con sus números de entrada, produce este resultado:

500,10000,617,600

Debido a las propiedades del GCD, se garantiza que la salida sea el número entero más bajo posible que aún mantiene las proporciones relativas entre cada número.

3
llmora 8 oct. 2019 a las 21:40

No sé si hay una manera más eficiente. Yo usaría:

import numpy as np

def reduce(array):

    mult = [0.1]*len(array)

    while all(item%10 == 0 for item in array):
        array = np.multiply(array, mult)

    return array

Resultados:

intgrs = (1000000, 20000000, 1234000, 1200000)
print(reduce(intgrs))

Devolverá una matriz numpy con los siguientes valores: [1000 20000 1234 1200]

0
Ferd 8 oct. 2019 a las 21:32

No tiene bucle anidado o dos bucles que contienen algo de trabajo

from numpy import multiply

intgr = [1000000, 20000000, 1234000, 1200000]
total = str( sum(intgr) )
mgntd = 10 ** ( len(total) - len(total.rstrip('0') ))
reslt = multiply(intgr, [1/mgntd]*len(intgr)).astype(int)
0
b m gevariya 12 oct. 2019 a las 04:32

En caso de que no se preocupe por el orden de los elementos establecidos.

 sets = {1000000, 20000000, 1234000, 1200000}
 max = max([len("".join(map(str, str(i))).rstrip('0')) for i in sets])
 new_set = {int(str(i)[:max]) for i in sets}  # gives {1000, 1234, 1200, 2000}
0
Rubel Ahmed 8 oct. 2019 a las 21:55

No es tan amable, pero funciona.

def trim_zeros(nums):
  while 1:
    for i in nums:
        if str(i)[-1] != "0":
            return(nums)                 
    nums=[int(i/10) for i in nums]
0
armstrove 8 oct. 2019 a las 21:49
58294043