Manejo del uso de memoria para grandes cálculos en Python

Estoy tratando de hacer algunos cálculos con Python, donde me quedé sin memoria. Por lo tanto, quiero leer / escribir un archivo para liberar memoria. Necesito algo así como un objeto de lista muy grande, así que pensé en escribir una línea para cada objeto en el archivo y leer / escribir en esas líneas en lugar de en la memoria. El orden de las líneas es importante para mí, ya que usaré números de línea como índice. Entonces me preguntaba cómo puedo reemplazar líneas en Python, sin moverme por otras líneas (en realidad, está bien mover líneas, siempre que regresen a donde espero que estén).

Editar

Estoy tratando de ayudar a un amigo, que es peor o igual que yo en Python. Se supone que este código encuentra el mayor número primo, que divide un número no primo dado. Este código funciona para números hasta números como 1 millón, pero después de muerto, mi memoria se agota al intentar hacer una lista de números.

# a comes from a user input
primes_upper_limit = (a+1) / 2
counter = 3L
numbers = list()
while counter <= primes_upper_limit:
    numbers.append(counter)
    counter += 2L

counter=3
i=0
half = (primes_upper_limit + 1) / 2 - 1
root = primes_upper_limit ** 0.5
while counter < root:
    if numbers[i]:
        j = int((counter*counter - 3) / 2)
        numbers[j] = 0
        while j < half:
            numbers[j] = 0
            j += counter
    i += 1
    counter = 2*i + 3
primes = [2] + [num for num in numbers if num]
for numb in reversed(primes):
    if a % numb == 0:
        print numb
        break
Otra edición

¿Qué hay de escribir archivos diferentes para cada índice? por ejemplo, mil millones de archivos con nombres de archivo enteros largos y solo un número dentro del archivo?

preguntado el 27 de agosto de 11 a las 19:08

Resolvamos la causa de la falta de memoria: cuéntenos sobre el cálculo o publique el código. También puede probar NumPy. -

Es posible que obtenga mejores respuestas si explica qué problema real está tratando de resolver. -

Agregué más explicación y mi código en mi pregunta original. -

Aconsejaría no incluir el sistema de archivos en esto. ¿Realmente quieres mil millones de archivos? Será muy lento. Si realmente desea aumentar su memoria con el disco, puede crear un archivo de intercambio. -

¿Necesita almacenar una lista de todos los factores primos y luego elegir el más grande (como lo hace)? o simplemente encontrar los factores primos más grandes directamente? -

4 Respuestas

Desea encontrar el divisor primo más grande de a. (Proyecto Euler Pregunta 3) Su elección actual de algoritmo e implementación haga esto de la siguiente manera:

  1. Genera una lista numbers de todos los primos candidatos en el rango (3 <= n <= sqrt (a), o (a + 1) / 2 como lo hace actualmente)
  2. Tamizar el numbers list para obtener una lista de primos {p} <= sqrt (a)
  3. División de prueba: prueba la divisibilidad de a por cada p. Almacene todos los divisores primos {q} de a.
  4. Imprime todos los divisores {q}; solo queremos el más grande.

Mis comentarios sobre este algoritmo están a continuación. El tamizado y la división de prueba son algoritmos seriamente no escalables, como comentamos Owen y yo. Para grandes a (mil millones o trillones) realmente debería usar NumPy. De todos modos algunos comentarios sobre la implementación de este algoritmo:

  1. Te conocías solo es necesario probar hasta √a, int(math.sqrt(a)), no (a + 1) / 2 como lo haces?
  2. No hay no es necesario crear una gran lista de candidatos numbersy, a continuación, crílelo para determinar la primacía: la lista de números no es escalable. Solo construye la lista primes directamente. Puede usar while / for-loops y xrange(3,sqrt(a)+2,2) (que le da un iterador). Como mencionas, xrange () se desborda en 2**31L, pero combinado con la observación sqrt, aún puede factorizar con éxito hasta 2**62
  3. En general, esto es inferior a obtener la descomposición prima de a, es decir, cada vez que se encuentra un divisor primo p | a, solo necesitas continuar tamizar el factor restante a / p o a / p² o a / p³ o lo que sea). Excepto por el raro caso de números primos muy grandes (o pseudoprimos), esto reducirá en gran medida la magnitud de los números con los que está trabajando.
  4. También, trabaja para solo necesitas generar la lista de números primos {p} una vez; a partir de entonces, almacenarlo y realizar búsquedas, no regenerarlo. Entonces me separaría generate_primes(a) desde find_largest_prime_divisor(a). La descomposición ayuda mucho.

Aquí está mi reescritura de su código, pero el rendimiento aún cae en miles de millones (a> 10 ** 11 +1) debido a mantener la lista tamizada. Nosotros podemos usar colecciones.deque en lugar de la lista de números primos, para obtener una operación append () de O (1) más rápida, pero esa es una optimización menor.

# Prime Factorization by trial division

from math import ceil,sqrt
from collections import deque

# Global list of primes (strictly we should use a class variable not a global)
#primes = deque()
primes = []

def is_prime(n):
    """Test whether n is divisible by any prime known so far"""
    global primes
    for p in primes:
         if n%p == 0:
             return False #  n was divisible by p
    return True # either n is prime, or divisible by some p larger than our list    
def generate_primes(a):
    """Generate sieved list of primes (up to sqrt(a)) as we go"""
    global primes
    primes_upper_limit = int(sqrt(a))
    # We get huge speedup by using xrange() instead of range(), so we have to seed the list with 2
    primes.append(2)
    print "Generating sieved list of primes up to", primes_upper_limit, "...",
    # Consider prime candidates 2,3,5,7... in increasing increments of 2
    #for number in [2] + range(3,primes_upper_limit+2,2):
    for number in xrange(3,primes_upper_limit+2,2):
        if is_prime(number): # use global 'primes'
            #print "Found new prime", number
            primes.append(number) # Found a new prime larger than our list
    print "done"    
def find_largest_prime_factor(x, debug=False):
    """Find all prime factors of x, and return the largest."""
    global primes
    # First we need the list of all primes <= sqrt(x)    
    generate_primes(x)
    to_factor = x # running value of the remaining quantity we need to factor
    largest_prime_factor = None
    for p in primes:
        if debug: print "Testing divisibility by", p
        if to_factor%p != 0:
            continue
        if debug: print "...yes it is"
        largest_prime_factor = p
        # Divide out all factors of p in x (may have multiplicity)
        while to_factor%p == 0:
            to_factor /= p
        # Stop when all factors have been found
        if to_factor==1:
            break
    else:
        print "Tested all primes up to sqrt(a), remaining factor must be a single prime > sqrt(a) :", to_factor
    print "\nLargest prime factor of x is", largest_prime_factor
    return largest_prime_factor

Respondido 28 ago 11, 03:08

Creo que el propósito de numbers es que almacena primos, es como el tamiz de eratóstenes. - Owen

@Owen: seguro, pero no es necesario almacenarlo todo, ciertamente como una lista en lugar de una planificadas. ¿Deberíamos darle una implementación literal de un algoritmo realmente malo (y no escalable), o ayudarlo a entender qué lo convertiría en un algoritmo mejor? - smci

Lo siento, olvidé mencionar que el problema que comenzó con range / xrange no acepta objetos largos. Y como estoy calculando números primos con tamiz, no hacer una lista no funciona. Y no usar tamiz es realmente lento para este tipo de cálculo. - vive

@yasar: pero como señalé, solo necesitas probar factores primos hasta sqrt (a), no (a + 1) / 2. Solo necesitas largos si a> = 2 ** 31 - smci

@ yasar11732 Supongo que dependería de qué tan grande a puede conseguir - ¿sólo va a subir a los miles de millones? Porque entonces deberías estar bien. Si vas a los billones, será peor, pero incluso entonces tendrías un colador de un billón de largo. - Owen

Si te entiendo correctamente, esta no es una tarea fácil. De la forma en que lo interpreté, desea mantener un identificador de archivo abierto y usar el archivo como un lugar para almacenar datos de caracteres.

Digamos que tenías un archivo como,

a
b
c

y deseaba reemplazar 'b' con 'bb'. Eso va a ser un fastidio, porque el archivo en realidad se parece a a\nb\nc - no puedes simplemente sobrescribir el b, necesita otro byte.

Mi consejo sería intentar encontrar una manera de hacer que su algoritmo funcione sin usar un archivo para almacenamiento adicional. Si tiene un desbordamiento de la pila, lo más probable es que no se haya quedado sin memoria, haya invadido la pila de llamadas, que es mucho más menor.

Podría intentar reelaborar su algoritmo para que no sea recursivo. A veces puedes usar un list para sustituir la pila de llamadas, pero hay muchas cosas que podría hacer y no creo que pueda dar muchos consejos generales sin ver su algoritmo.

editar

Ah, veo lo que quieres decir ... cuando la lista

while counter <= primes_upper_limit:
    numbers.append(counter)
    counter += 2L

crece realmente grande, podría quedarse sin memoria. Así que supongo que básicamente estás haciendo un colador, y por eso tienes la gran lista numbers? Que tiene sentido. Si desea seguir haciéndolo de esta manera, puede intentar un numpy bool matriz, porque utilizará sustancialmente menos memoria por celda:

import numpy as np

numbers = np.repeat(True, a/2)

O (y tal vez esto no sea atractivo) podría optar por un enfoque completamente diferente que no use una lista grande, como factorizar el número por completo y elegir el factor más grande.

Algo como:

factors = [ ]
tail = a

while tail > 1:
    j = 2
    while 1:
        if tail % j == 0:
            factors.append(j)
            tail = tail / j
            print('%s %s' % (factors, tail))
            break
        else:
            j += 1

es decir, di que estabas factorizando 20: tail comienza como 20, entonces encuentras 2 tail se convierte en 10, entonces se convierte en 5.

Esto no es terriblemente eficiente y se convertirá en mucho demasiado lento para un número primo grande (miles de millones), pero está bien para números con factores pequeños.

Me refiero a que tu colador también es bueno, hasta que empieces a quedarte sin memoria;). Podrías dar numpy un disparo.

Respondido 28 ago 11, 00:08

Agregué algoritmo a mi pregunta original. - vive

No soy un hablante nativo de inglés, no entendí muy bien lo que querías decir con factorizar el número por completo y elegir el factor más grande. - vive

Oh, lo siento. Quise decir, empezar con a, factorizarlo en b * c, factor c dentro d * e, y así. - Owen

¿Puede vincularse a un algoritmo de muestra para factorizar el número por completo? No soy un gurú de los algoritmos y no podía imaginarme cómo haría eso. - vive

Eso se llama encontrar el Factorización entera o descomposición prima de a. En este caso, está utilizando el método de División de prueba (ese es el método más simple). - smci

Pytables es excelente para trabajar y almacenar grandes cantidades de datos. Pero primero comience implementando los comentarios en la respuesta de smci para minimizar la cantidad de números que necesita almacenar.

Respondido 28 ago 11, 02:08

Para un número con solo doce dígitos, como en el Proyecto Euler # 3, no se necesita un método sofisticado de factorización de enteros y no es necesario almacenar resultados intermedios en el disco. Utilice este algoritmo para encontrar los factores de n:

  1. Establezca f = 2.
  2. Si n = 1, deténgase.
  3. Si f * f> n, imprima ny deténgase.
  4. Divida n por f, manteniendo tanto el cociente q como el resto r.
  5. Si r = 0, imprima q, divida n por q y vaya al Paso 2.
  6. De lo contrario, aumente f en 1 y vaya al paso 3.

Esto solo hace una división de prueba por cada entero hasta que alcanza la raíz cuadrada, lo que indica que el cofactor restante es primo. Cada factor se imprime tal como se encuentra.

respondido 14 nov., 11:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.