Search code examples
pythonalgorithmpython-3.xpermutationpython-itertools

MemoryError while trying to using itertools.permutations, how use less memory?


I'm loading from a text document containing so random strings and I'm trying to print every possible permutation of the characters in that string.

If the notepad contains for example:

123
abc

I want my output to be

123,132,213,231,312,321
abc,acb,bac,bca,cab,cba

The text file contains some pretty large strings so I can see why I am getting this MemoryError.

My first attempt I used this:

import sys
import itertools
import math

def organize(to_print):
    number_list = []
    upper_list = []
    lower_list = []
    for x in range(0,len(to_print)):
        if str(to_print[x]).isdigit() is True:
            number_list.append(to_print[x])
        elif to_print[x].isupper() is True:
            upper_list.append(to_print[x])
        else:
            lower_list.append(to_print[x])
    master_list = number_list + upper_list + lower_list
    return master_list

number = open(*file_dir*, 'r').readlines()

factorial = math.factorial(len(number))
complete_series = ''

for x in range(0,factorial):
    complete_string = ''.join((list(itertools.permutations(organize(number)))[x]))

    complete_series += complete_string+','
edit_series = complete_series[:-1]
print(edit_series)

The reason for def organize is if I have a string 1aB I would need to preorder it by number,uppercase,lowercase before I start the permutations.

I got the memory error here: complete_string = ''.join((list(itertools.permutations(organize(number)))[x])) so my initial attempt was to bring it out of the for-loop.


My second attempt is this:

import sys
import itertools
import math

def organize(to_print):
    number_list = []
    upper_list = []
    lower_list = []
    for x in range(0,len(to_print)):
        if str(to_print[x]).isdigit() is True:
            number_list.append(to_print[x])
        elif to_print[x].isupper() is True:
            upper_list.append(to_print[x])
        else:
            lower_list.append(to_print[x])
    master_list = number_list + upper_list + lower_list
    return master_list

number = open(*file_dir*, 'r').readlines()

factorial = math.factorial(len(number))
complete_series = ''
the_permutation = list(itertools.permutations(organize(number)))

for x in range(0,factorial):
    complete_string = ''.join((the_permutation[x]))

    complete_series += complete_string+','
edit_series = complete_series[:-1]
print(edit_series)

But I am still getting a memory error. I don't necessarily need or want the answer directly as this is good learning practice to reduce my inefficiencies, so hints in the right direction would be nice.


Added 3rd attempt:

import sys
import itertools
import math

def organize(to_print):
    number_list = []
    upper_list = []
    lower_list = []
    for x in range(0,len(to_print)):
        if str(to_print[x]).isdigit() is True:
            number_list.append(to_print[x])
        elif to_print[x].isupper() is True:
            upper_list.append(to_print[x])
        else:
            lower_list.append(to_print[x])
    master_list = number_list + upper_list + lower_list
    return master_list

number = open(*file_dir*, 'r').readlines()

factorial = math.factorial(len(number))
complete_series = ''
the_permutation = itertools.permutations(organize(number))
for x in itertools.islice(the_permutation,factorial):
    complete_string = ''.join(next(the_permutation))
    complete_series += complete_string+','
edit_series = complete_series[:-1]
print(edit_series)

Solution

  • Don't call list, just iterate over the permutations:

    the_permutation = itertools.permutations(organize(number))
    
    for x in the_permutation:
        complete_string = ''.join(the_permutation)
    

    list(itertools.permutations(organize(number))) stores all the permutations in memory then you store all the permutations in a string in your loop, there is no guarantee that you will be able to store all the data even using this approach depending on how much data is in the_permutation

    If you only want a certain amount of the permutations you can call next om the permutations object:

    the_permutation = itertools.permutations(organize(number))
    for x in range(factorial):
        complete_string = ''.join(next(the_permutation))
    

    Or use itertools.islice:

    for x in itertools.islice(the_permutation,factorial):
        complete_string = ''.join(next(the_permutation))