Ranking permutations recursively in python - python

How can I format this function so that it works recursively? I want to go multiple levels deeper if possible, not just until 5.
permutations is a list with different permutations, and those individual permutation can also have permutations, etc. I want to rank them based on some calculations I do in get_permutations and return the new order of permutations. A good way to look at it is probably a large list of lists of lists of lists of lists. First I want to change the order of the first level, than one step deeper etc. But eventually I return the string based on those permutations and not the permutation itself (if that matters), with res1...res5 being the strings. I'm not smart enough to get it to work recursively, even though I know it should be possible...Any ideas?
permutations, res1 = get_permutations(str, 1)
for p2 in permutations:
permutations_2, res2 = get_permutations(p2,2)
for p3 in permutations_2:
permutations_3, res3 = get_permutations(p3,3)
for p4 in permutations_3:
permutations_4, res4 = get_permutations(p4, 4)
for p5 in permutations_4:
permutations_5, res5 = get_permutations(p5,5)
res4 += res5
res3 += res4
res2 += res3
res1 += res2
return res1
EDIT: this returns a single (best) permutation. That is what the result is for. So not a list of possible permutations, as mentioned in the answers. E.g. if we have a lists of lists of lists, if first sorts the list based on all subinformation, then sorts the multiple lists of lists based on the previous sorts and all subinformation and then sorts the lists of lists of lists based on the previous 2 sorts.

A recursive generator function that yields permutations in the expected order with regard to the original string:
def get_permutations(a):
if len(a) <= 1:
yield a
else:
for i in xrange(len(a)):
for p in get_permutations(a[:i]+a[i+1:]):
yield ''.join([a[i]])+p
>>> a = '123'
>>> list(get_permutations(a))
['123', '132', '213', '231', '312', '321']
The recursive principle here:
Base case: strings of lengthes (0, 1) have only one permutation: themselves.
Recursion: for each letter in the string, remove it and prepend it to each permutation of the string's remainder.

An example below, This method works by nesting for loops in a recursive manner repeat-times. We then accumulate the result of the sub-solutions, appending to a result list:
result = []
def permutations(alphabet, repeat, total = ''):
if repeat >= 1:
for i in alphabet:
# Add the subsolutions.
permutations(alphabet, repeat - 1, total + i)
else:
result.append(total)
return result
Sample Outputs:
permutations('ab', 3) ->
$ ['aaa', 'aab', 'aba', 'abb', 'baa', 'bab', 'bba', 'bbb']
permutations('ab', 3) ->
$ ['aaa', 'aab', 'aac', 'aba', 'abb', 'abc', 'aca', 'acb', 'acc', 'baa',
'bab', 'bac', 'bba', 'bbb', 'bbc', 'bca', 'bcb', 'bcc', 'caa', 'cab',
'cac', 'cba', 'cbb', 'cbc', 'cca', 'ccb', 'ccc']
permutations('ab', 1) ->
$ ['a', 'b']
Source: a previous answer of mine.

Related

python order words of a list

I need help in some basic python scrips, well I want to order a prayer in words from longer to shorter length and without repeating, until then everything is fine, what happens is that I do not know how to do to order words of the same length alphabetically.
Since you're asking for a case where the 2 iterators will not be of the same order, you'll have to do it differently. You can consult this question Sort by multiple keys using different orderings. But since it doesn't contain what you really wanted, I'll answer it here:
from itertools import groupby
s = ['ddd', 'bb', 'ab', 'aa', 'cc', 'dab']
l = [sorted(list(g)) for b, g in groupby(s, key=lambda x: len(x))]
l = [e for x in l for e in x]
>>> l
['dab', 'ddd', 'aa', 'ab', 'bb', 'cc']
This sort by negative order for length but positive order for words. Explanation: the first list comprehension turns the list of string into a list of list that contain sorted lists (by alphanumeric) of strings of same length. The second list comprehension unwraps the list of list into one list.

All Unequal Subsets of Sorted String in Python 3

I would like to find all subsets of a sorted string, disregarding order and which characters are next to each other. I think the best way for this to be explained is though an example. The results should also be from longest to shortest.
These are the results for bell.
bell
bel
bll
ell
be
bl
el
ll
b
e
l
I have thought of ways to do this, but none for any length of input.
Thank you!
There are generally two ways to approach such things: generate "everything" and weed out duplicates later, or create custom algorithms to avoid generating duplicates to begin with. The former is almost always easier, so that's what I'll show here:
def gensubsets(s):
import itertools
for n in reversed(range(1, len(s)+1)):
seen = set()
for x in itertools.combinations(s, n):
if x not in seen:
seen.add(x)
yield "".join(x)
for x in gensubsets("bell"):
print(x)
That prints precisely what you said you wanted, and how it does so should be more-than-less obvious.
Here is one way using itertools.combinations.
If the order for strings of same length is important, see #TimPeters' answer.
from itertools import combinations
mystr = 'bell'
res = sorted({''.join(sorted(x, key=lambda j: mystr.index(j)))
for i in range(1, len(mystr)+1) for x in combinations(mystr, i)},
key=lambda k: -len(k))
# ['bell', 'ell', 'bel', 'bll', 'be', 'll', 'bl', 'el', 'l', 'e', 'b']
Explanation
Find all combinations of length in range(1, len(mystr)+1).
Sort by original string via key argument of sorted. This step may be omitted if not required.
Use set of ''.join on elements for unique strings.
Outer sorted call to go from largest to smallest.
You can try in one line:
import itertools
data='bell'
print(set(["".join(i) for t in range(len(data)) for i in itertools.combinations(data,r=t) if "".join(i)!='']))
output:
{'bel', 'bll', 'ell', 'el', 'be', 'bl', 'e', 'b', 'l', 'll'}

Is it proper to use nested loops a lot in python?

I have a project and I am trying to do a complex cryptographic method. Is it normal to use nested loops a lot? Or did I miss something?
I intend to create a method which tries all strings to find password. For example, when I input ['A','B'] it should create each of these one by one:
['A', 'B', 'AA', 'AB', 'BA', 'BB', 'AAA', 'AAB', 'ABA', 'ABB', 'BAA', 'BAB', 'BBA', 'BBB', (triple permutations), (quadruple permutations), and it goes on ...]
My code:
def rulefinder():
global externalrul1
global externalrul2
rul2 = uniquestring1[0]
rul1 = uniquestring1[0]
for n in range (0,3):
for m in range (0, len(uniquestring1)):
for z in range(0, n+1):
for k in range (0,3):
for i in range(0, len(uniquestring1)):
for o in range(0, k+1):
for y in range (0, len(uniquestring1)):
rul2 = rul2[:-1] + uniquestring1[y]
for x in range (0, len(uniquestring1)):
rul1= rul1[:-1] + uniquestring1[x]
code=""
for cha in Genx1:
if cha==uniquestring1[0]:
code +=codereplacer(rul1)
elif cha==uniquestring1[1]:
code +=codereplacer(rul2)
print(code)
print(uniquestring1[0],rul1)
print(uniquestring1[1],rul2)
print(LastString)
if code == LastString:
axiom1=uniquestring1[0]
axiom2=uniquestring1[1]
externalrul1=rul1
externalrul2=rul2
print('rules are found')
print("First RULE:", uniquestring1[0], rul1)
print("Second RULE:", uniquestring1[1], rul2)
findsubgeneration(code, axiom1, rul1, axiom2, rul2)
return
rul1 = rul1[:o] + uniquestring1[i] + rul1[(o + 1):]
rul1 += codereplacer(uniquestring1[i])
rul2 = rul2[:z] + uniquestring1[m] + rul2[(z + 1):]
rul1 =""
rul2 += codereplacer(uniquestring1[m])
You are doing things in a very MATLAB-ic way (lots of loops, and iterating only on the index, instead of elements of iterables). Pythonic way is much more efficient (uses generators under the hood), and is much cleaner:
import itertools
l = ['A','B']
n = 5 # The size of your "expanding permutations"
res = []
for i in range(1,n):
res.extend(map(''.join, list(itertools.product(l, repeat=i))))
print res
Result:
['A', 'B', 'AA', 'AB', 'BA', 'BB', 'AAA', 'AAB', 'ABA', 'ABB', 'BAA', 'BAB', 'BBA', 'BBB',
'AAAA', 'AAAB', 'AABA', 'AABB', 'ABAA', 'ABAB', 'ABBA', 'ABBB', 'BAAA', 'BAAB', 'BABA', 'BABB', 'BBAA', 'BBAB', 'BBBA', 'BBBB']
Caution: As long as you are working with a small list, you should be fine but as l grows, the result is going to grow exponentially, and may eat out your memory. Hence, instead of doing an extend to the res list, you can write the results inside the loop into disk, on the fly.

String generation based on the other string in Python

I want to create a simple string generator and here is how it will work
I declare a pattern_string = "abcdefghijklmnopqrstuvwxyz"
My starting string lets say starting_string = "qywtx"
Now I want to generate strings as follows:
Check the last character in my starting_stringagainst the pattern string.
Last character is x. We find this character in the find it in the pattern_string:
abcdefghijklmnopqrstuvw x yz
and see that next character is y so I want output qywty.
...
However, when I reach the z, I want my string to increment second last character and set the last character to the first character of the starting_pattern so it will be qywra and so on...
Now questions:
Can I use REGEX to achieve that?
Are there any libraries out there that already handle such generation?
The following will generate the next string according to your description.
def next(s, pat):
l = len(s)
for i in range(len(s) - 1, -1, -1): # find the first non-'z' from the back
if s[i] != pat[-1]: # if you find it
# leave everything before i as is, increment at i, reset rest to all 'a's
return s[:i] + pat[pat.index(s[i]) + 1] + (l - i - 1) * pat[0]
else: # this is only reached for s == 'zzzzz'
return (l + 1) * pat[0] # and generates 'aaaaaa' (just my assumption)
>>> import string
>>> pattern = string.ascii_lowercase # 'abcde...xyz'
>>> s = 'qywtx'
>>> s = next(s, pattern) # 'qywty'
>>> s = next(s, pattern) # 'qywtz'
>>> s = next(s, pattern) # 'qywua'
>>> s = next(s, pattern) # 'qywub'
For multiple 'z' in the end:
>>> s = 'foozz'
>>> s = next(s, lower) # 'fopaa'
For all 'z', start over with 'a' of incremented length:
>>> s = 'zzz'
>>> s = next(s, lower) # 'aaaa'
To my knowledge there is no library function to do that. One that comes close is itertools.product:
>>> from itertools import product
>>> list(map(''.join, product('abc', repeat=3)))
['aaa', 'aab', 'aac', 'aba', 'abb', 'abc', 'aca', 'acb', 'acc', 'baa',
'bab', 'bac', 'bba', 'bbb', 'bbc', 'bca', 'bcb', 'bcc', 'caa', 'cab',
'cac', 'cba', 'cbb', 'cbc', 'cca', 'ccb', 'ccc']
But that doesn't not work with an arbitrary start string. This behaviour could be mimicked by combining it with itertools.dropwhile but that has the serious overhead of skipping all the combinations before the start string (which in the case of an alphabet of 26 and a start string towards the end pretty much renders that approach useless):
>>> list(dropwhile(lambda s: s != 'bba', map(''.join, product('abc', repeat=3))))
['bba', 'bbb', 'bbc', 'bca', 'bcb', 'bcc', 'caa', 'cab', 'cac', 'cba', 'cbb', 'cbc', 'cca', 'ccb', 'ccc']

Combinations with replacements

I know how to generate combinations of a set and that's a builtin in Python (what I use), anyway. But how to generate combinations with replacements?
Suppose I have a set with, say, two identical elements - for example, AABCDE.
Combinations of 3 items could be:
"AAB"
"ABC"
"CDE"
However, the program would count ABC twice - once when using the first A, and the second one using the second A.
What is a good way to generate such combinations without duplicates?
Thanks.
convert it to set, that's the easiest way to get rid of duplicates.
>>> import itertools
>>> ["".join(x) for x in (itertools.combinations(set("AABCDE"),3))]
['ACB', 'ACE', 'ACD', 'ABE', 'ABD', 'AED', 'CBE', 'CBD', 'CED', 'BED']
>>>
From your other comments, I think I misunderstood what you are asking.
>>> import itertools
>>> set("".join(x) for x in (itertools.combinations("AABCDE",3)))
set(['AAE', 'AAD', 'ABC', 'ABD', 'ABE', 'AAC', 'AAB', 'BCD', 'BCE', 'ACD', 'CDE', 'ACE', 'ADE', 'BDE'])
def stepper_w_w(l,stop):#stepper_with_while
"""l is a list of any size usually you would input [1,1,1,1...],
stop is the highest number you want to stop at so if you put in stop=5
the sequence would stop at [5,5,5,5...]
This stepper shows the first number that equals the last.
This generates combinations with replacement. """
numb1=1
while numb1<stop:
#print(numb1)
l[0]=numb1
NeL=0
while l[len(l)-1]<=numb1:
if l[NeL]==l[len(l)-1]:
l[NeL]+=1
l[(NeL+1):]=[1]*((len(l))-(NeL+1))
print(l)
"""iter_2s=NeL+1
while iter_2s<=(len(l)-1): #this is different from above
l[iter_2s]=2
iter_2s+=1
print(l)"""
NeL=-1
NeL+=1
numb1+=1

Categories