Python permutation using backtracking - python

I'm just trying to learn permutation using backtracking. I've written the following code but it stops after first output.
def permutations(str_in, soFar):
if len(str_in) != 0:
for c in str_in:
soFar.append(c)
temp_str = str_in
temp_str.remove(c)
print temp_str, soFar
permutations(temp_str, soFar)
else:
print soFar
inp = "ABCD"
str_in = list(inp)
permutations(str_in, [])
This is the output I'm getting for this:
['B', 'C', 'D'] ['A']
['C', 'D'] ['A', 'B']
['D'] ['A', 'B', 'C']
[] ['A', 'B', 'C', 'D']
['A', 'B', 'C', 'D']
I'm sure this is something simple, but I'm not able to understand what mistake I'm making here.

Here is Geeksforgeeks method, by Bhavya Jain, of permuting a string. The missing logic in your code is the recursive step on the sublists and a swapping behavior. Here is their visualization.
def permute(a, l, r):
if l==r:
print toString(a)
else:
for i in xrange(l,r+1):
a[l], a[i] = a[i], a[l]
permute(a, l+1, r)
a[l], a[i] = a[i], a[l] # backtrack
# Driver program to test the above function
string = "ABC"
n = len(string)
a = list(string)
permute(a, 0, n-1)
Output
['A', 'B', 'C']
['A', 'C', 'B']
['B', 'A', 'C']
['B', 'C', 'A']
['C', 'B', 'A']
['C', 'A', 'B']

I rewrote it again and with some print commands in between I was able to reach the desired output. But still not entirely sure of the way it's working. I think it's mainly how python modifies a list with each call to the function. I had to assign temporary lists twice to make sure when tracing back the original lists are not modified. Anyway the following code is working.
def permute(String, SoFar):
TempString = list(String)
TempSoFar = list(SoFar)
#print TempString, TempSoFar
if TempString != []:
for c in String:
TStr = list(TempString)
TSF = list(TempSoFar)
TStr.remove(c)
TSF.append(c)
#print String, TStr, TSF
permute(TStr, TSF)
else:
print "\nOut: ", TempSoFar, "\n"
permute(list("ABCD"),list(""))
Second solution using strings rather than lists, as mentioned in my comment below.
def permute(String, SoFar):
#print "New Func Call: ", "SoFar: ", SoFar,"String: ", String, "\n"
if String != "":
for c in String:
TS = String.replace(c,"")
TSF = SoFar+c
permute(TS, TSF)
#print "A Call Finished", "SoFar: ", SoFar,"String: ", String, "TSF: ", TSF, "TS: ", TS
else:
print SoFar
permute("ABC","")

Related

Variable value is changing between print and append

The variable is changing and prints different value and it saves another
If i run this code
def swap(string,x,y):
string[y], string[x] = string[x], string[y]
def permutations(string ,i=0):
if i == len(string):
yield string
for x in range(i, len(string)):
perm = string
swap(perm,x,i)
yield from permutations(perm, i+1)
swap(perm,i,x)
result = []
test = permutations(['a','b','c'])
for x in test:
print(x)
result.append(x)
print(result)
It prints this and i dont know why:
['a', 'b', 'c']
['a', 'c', 'b']
['b', 'a', 'c']
['b', 'c', 'a']
['c', 'b', 'a']
['c', 'a', 'b']
[['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c']]
You're mutating the same x in place, so only the final version of it is printed after the loop.
result.append(x) does not copy the object (x in this case), it just places a reference to it into the result list.
Do e.g. result.append(x[:]) or result.append(list(x)) to put copies of x into the result list.
That's why the yielded list has the same references, so whenever you change it, the previous referenced value will be changed, too. The quick fix is to return a copy instance of the list.
def swap(string,x,y):
string[y], string[x] = string[x], string[y]
def permutations(string ,i=0):
if i == len(string):
yield string.copy()
for x in range(i, len(string)):
perm = string
swap(perm,x,i)
yield from permutations(perm, i+1)
swap(perm,i,x)
result = []
test = permutations(['a','b','c'])
for x in test:
print(x)
result.append(x)
print(result)

Find all substrings in a string in python 3 with brute-force

I want to find all substrings 'A' to 'B' in L = ['C', 'A', 'B', 'A', 'A', 'X', 'B', 'Y', 'A'] with bruteforce, this is what i've done:
def find_substring(L):
t = 0
s = []
for i in range(len(L) - 1):
l = []
if ord(L[i]) == 65:
for j in range(i, len(L)):
l.append(L[j])
if ord(L[j]) == 66:
t = t + 1
s.append(l)
return s, t
Now I want the output:
[['A','B'], ['A','B','A','A','X','B'], ['A','A','X','B'], ['A','X','B']]
But i get:
[['A','B','A','A','X','B','Y','A'],['A','B','A','A','X','B','Y','A'],['A','A','X','B','Y','A'],['A','X','B','Y','A']]
Can someone tell me what I'm doing wrong?
The problem is that the list s, holds references to the l lists.
So even though you are appending the correct l lists to s, they are changed after being appended as the future iterations of the j loop modify the l lists.
You can fix this by appending a copy of the l list: l[:].
Also, you can compare strings directly, no need to convert to ASCII.
def find_substring(L):
s = []
for i in range(len(L) - 1):
l = []
if L[i] == 'A':
for j in range(i, len(L)):
l.append(L[j])
if L[j] == 'B':
s.append(l[:])
return s
which now works:
>>> find_substring(['C', 'A', 'B', 'A', 'A', 'X', 'B', 'Y', 'A'])
[['A', 'B'], ['A', 'B', 'A', 'A', 'X', 'B'], ['A', 'A', 'X', 'B'], ['A', 'X', 'B']]
When you append l to s, you are adding a reference to a list which you then continue to grow. You want to append a copy of the l list's contents at the time when you append, to keep it static.
s.append(l[:])
This is a common FAQ; this question should probably be closed as a duplicate.
You would be better first finding all indices of 'A' and 'B', then iterating over those, avoiding brute force.
def find_substrings(lst)
idx_A = [i for i, c in enumerate(lst) if c == 'A']
idx_B = [i for i, c in enumerate(lst) if c == 'B']
return [lst[i:j+1] for i in idx_A for j in idx_B if j > i]
You can reset l to a copy of the string after l is appended l = l[:] right after the last append.
So, you want all the substrings that start with 'A' and end with 'B'?
When you use #Joeidden's code you can change need the for i in range(len(L) - 1): to for i in range(len(L)): because only strings that end with 'B' will be appended to s.
def find_substring(L):
s = []
for i in range(len(L)):
l = []
if L[i] == 'A':
for j in range(i, len(L)):
l.append(L[j])
if L[j] == 'B':
s.append(l[:])
return s
Another slightly different approach would be this:
L = ['C', 'A', 'B', 'A', 'A', 'X', 'B', 'Y', 'A']
def find_substring(L):
output = []
# Start searching for A.
for i in range(len(L)):
# If you found one start searching all B's until you reach the end.
if L[i]=='A':
for j in range(i,len(L),1):
# If you found a B, append the sublist from i index to j+1 index (positions of A and B respectively).
if L[j]=='B':
output.append(L[i:j+1])
return output
result = find_substring(L)
print(result)
Output:
[['A', 'B'], ['A', 'B', 'A', 'A', 'X', 'B'], ['A', 'A', 'X', 'B'], ['A', 'X', 'B']]
In case you need a list comprehension of the above:
def find_substring(L):
output = [L[i:j+1] for i in range(len(L)) for j in range(i,len(L),1) if L[i]=='A' and L[j]=='B']
return output

Python - For Loop - Print only if the above line is equal

I've the following code:
characters = ['a', 'b', 'b', 'c','d', 'b']
for i in characters:
if i[0] == i[-1]:
print(i)
Basically I only want to extract the characters that are equal from the line above. For example, in my case I only want to extract the b from 1 and 2 position.
How can I do that?
Thanks!
a = ['a', 'b', 'b', 'c', 'd', 'b']
b = ['a', 'b', 'b', 'c', 'd', 'b', 'd']
import collections
print([item for item, count in collections.Counter(a).items() if count > 1])
print([item for item, count in collections.Counter(b).items() if count > 1])
output
['b']
['b', 'd']
Without iterating multiple times over the same list.
characters = ['a', 'b', 'b', 'c','d', 'b']
last_char = None
output = []
for char in characters:
if char == last_char:
output.append(char)
last_char = char
print(output)
To extract the characters form the list which matches only the last char from list you can do the following:
characters = ['a', 'b', 'b', 'c','d', 'b']
for i in range(0, len(characters) - 1):
if characters[i] == characters[-1]:
print(characters[i])
In you snippet i when you are looping is the individual chars from your list, and it looks you were trying to access last, and first item from the list.
equal = [a for a in characters[0:-1] if a == characters[-1]]
Unless you also want the last character which will always be equal to itself, then do:
equal = [a for a in characters if a == characters[-1]]
little modification in your code
characters = ['a', 'b', 'b', 'c','d', 'b']
ch= (characters[-1])
for i in characters:
if i == ch:
print(i

More pythonic implementation for continuously looping through list

I have a list of tokens that I want to use for accessing an API. I'd like to always be able to select the next token in the list for use, and when the end of the list is reached, start over.
I have this now, which works, but I find it to be pretty messy and unreadable.
class tokenz:
def __init__(self):
self.tokens = ['a', 'b', 'c', 'd', 'e']
self.num_tokens = len(tokens)
self.last_token_used = 0
def select_token(self):
if self.last_token_used == 0:
self.last_token_used += 1
return self.tokens[0]
elif self.last_token_used < (self.num_tokens - 1):
self.last_token_used += 1
return self.tokens[self.last_token_used - 1]
elif self.last_token_used == (self.num_tokens -1):
self.last_token_used = 0
return self.tokens[self.num_tokens - 1]
Any thoughts on making this more pythonic?
Use itertools.cycle() to get a generator that repeats a list of items infinitely.
In [13]: tokens = ['a', 'b', 'c', 'd', 'e']
In [14]: import itertools
In [15]: infinite_tokens = itertools.cycle(tokens)
In [16]: [next(infinite_tokens) for _ in range(13)]
Out[16]: ['a', 'b', 'c', 'd', 'e', 'a', 'b', 'c', 'd', 'e', 'a', 'b', 'c']
If you really want to make your posted code simpler, use modular arithmetic.
self.last_token_used = (self.last_token_used + 1) % len(self.tokens)
Also, you can use negative indexes in Python lists, so your if statements are unnecessary:
In [26]: for n in range(len(tokens)):
...: print('{}: tokens[{}] = {}'.format(n, n-1, tokens[n-1]))
...:
0: tokens[-1] = e
1: tokens[0] = a
2: tokens[1] = b
3: tokens[2] = c
4: tokens[3] = d
And then your code becomes:
class tokenz:
def __init__(self):
self.tokens = ['a', 'b', 'c', 'd', 'e']
self.num_tokens = len(self.tokens)
self.last_token_used = 0
def select_token(self):
self.last_token_used = (self.last_token_used + 1) % self.num_tokens
return self.tokens[self.last_token_used - 1]

How can I group a list of objects by continuity?

Given a very large (gigabytes) list of arbitrary objects (I've seen a similar solution to this for ints), can I either group it easily into sublists by equivalence? Either in-place or by generator which consumes the original list.
l0 = [A,B, A,B,B, A,B,B,B,B, A, A, A,B] #spaces for clarity
Desired result:
[['A', 'B'], ['A', 'B', 'B'], ['A', 'B', 'B', 'B', 'B'], ['A'], ['A'], ['A', 'B']]
I wrote a looping version like so:
#find boundaries
b0 = []
prev = A
group = A
for idx, elem in enumerate(l0):
if elem == group:
b0.append(idx)
prev = elem
b0.append(len(l0)-1)
for idx, b in enumerate(b0):
try:
c = b0[idx+1]
except:
break
if c == len(l0)-1:
l1.append(l0[b:])
else:
l1.append(l0[b:c])
Can this be done as a generator gen0(l) that will work like:
for g in gen(l0):
print g
....
['A', 'B']
['A', 'B', 'B']
['A', 'B', 'B', 'B', 'B']
....
etc?
EDIT: using python 2.6 or 2.7
EDIT: preferred solution, mostly based on the accepted answer:
def gen_group(f, items):
out = [items[0]]
while items:
for elem in items[1:]:
if f(elem, out[0]):
break
else:
out.append(elem)
for _i in out:
items.pop(0)
yield out
if items:
out = [items[0]]
g = gen_group(lambda x, y: x == y, l0)
for out in g:
print out
Maybe something like this:
def subListGenerator(f,items):
i = 0
n = len(items)
while i < n:
sublist = [items[i]]
i += 1
while i < n and not f(items[i]):
sublist.append(items[i])
i += 1
yield sublist
Used like:
>>> items = ['A', 'B', 'A', 'B', 'B', 'A', 'B', 'B', 'B', 'B', 'A', 'A', 'A', 'B']
>>> g = subListGenerator(lambda x: x == 'A',items)
>>> for x in g: print(x)
['A', 'B']
['A', 'B', 'B']
['A', 'B', 'B', 'B', 'B']
['A']
['A']
['A', 'B']
I assume that A is your breakpoint.
>>> A, B = 'A', 'B'
>>> x = [A,B, A,B,B, A,B,B,B,B, A, A, A,B]
>>> map(lambda arr: [i for i in arr[0]], map(lambda e: ['A'+e], ''.join(x).split('A')[1:]))
[['A', 'B'], ['A', 'B', 'B'], ['A', 'B', 'B', 'B', 'B'], ['A'], ['A'], ['A', 'B']]
Here's a simple generator to perform your task:
def gen_group(L):
DELIMETER = "A"
out = [DELIMETER]
while L:
for ind, elem in enumerate(L[1:]):
if elem == DELIMETER :
break
else:
out.append(elem)
for i in range(ind + 1):
L.pop(0)
yield out
out = [DELIMETER ]
The idea is to cut down the list and yield the sublists until there is nothing left. This assumes the list starts with "A" (DELIMETER variable).
Sample output:
for out in gen_group(l0):
print out
Produces
['A', 'B']
['A', 'B', 'B']
['A', 'B', 'B', 'B', 'B']
['A']
['A']
['A', 'B']
['A']
Comparitive Timings:
timeit.timeit(s, number=100000) is used to test each of the current answers, where s is the multiline string of the code (listed below):
Trial 1 Trial 2 Trial 3 Trial 4 | Avg
This answer (s1): 0.08247 0.07968 0.08635 0.07133 0.07995
Dilara Ismailova (s2): 0.77282 0.72337 0.73829 0.70574 0.73506
John Coleman (s3): 0.08119 0.09625 0.08405 0.08419 0.08642
This answer is the fastest, but it is very close. I suspect the difference is the additional argument and anonymous function in John Coleman's answer.
s1="""l0 = ["A","B", "A","B","B", "A","B","B","B","B", "A", "A", "A","B"]
def gen_group(L):
out = ["A"]
while L:
for ind, elem in enumerate(L[1:]):
if elem == "A":
break
else:
out.append(elem)
for i in range(ind + 1):
L.pop(0)
yield out
out = ["A"]
out =gen_group(l0)"""
s2 = """A, B = 'A', 'B'
x = [A,B, A,B,B, A,B,B,B,B, A, A, A,B]
map(lambda arr: [i for i in arr[0]], map(lambda e: ['A'+e], ''.join(x).split('A')[1:]))"""
s3 = """def subListGenerator(f,items):
i = 0
n = len(items)
while i < n:
sublist = [items[i]]
i += 1
while i < n and not f(items[i]):
sublist.append(items[i])
i += 1
yield sublist
items = ['A', 'B', 'A', 'B', 'B', 'A', 'B', 'B', 'B', 'B', 'A', 'A', 'A', 'B']
g = subListGenerator(lambda x: x == 'A',items)"""
The following works in this case. You could change the l[0] != 'A' condition to be whatever. I would probably pass it as an argument, so that you can reuse it somewhere else.
def gen(l_arg, boundary):
l = l_arg.copy() # Optional if you want to save memory
while l:
sub_list = [l.pop(0)]
while l and l[0] != boundary: # Here boundary = 'A'
sub_list.append(l.pop(0))
yield sub_list
It assumes that there is an 'A' at the beginning of your list. And it copies the list, which isn't great when the list is in the range of Gb. you could remove the copy to save memory if you don't care about keeping the original list.

Categories

Resources