I'm trying to understand how to think a recursive method iteratively. For example, I have the following backtracking method:
def bitStr(n, s):
if n == 1:
return s
return [digit + bits for digit in bitStr(1, s) for bits in bitStr(n - 1, s)]
I'm practicing how to do accomplish a similar iteratively or explicitly using double for-loop.
I started something like this which I understand is incorrect; however, unable to fix it:
def bitStr2(n, s):
if n == 1:
return [c for c in s]
for bits in bitStr2(n - 1, s):
for digit in bitStr2(1, s):
return digit + bits
Thank You
There are two issues in your code.
First, as pointed out by #MisterMiyagi, you switched the loops. In a list comprehension, loops are read from left to right. You should write the regular loops like this:
for digit in bitStr2(1, s):
for bits in bitStr2(n - 1, s):
...
Second, a list comprehension produces... a list. You have to store the elements in a list:
...
result = []
for digit in bitStr2(1, s):
for bits in bitStr2(n - 1, s):
result.append(digit + bits)
return result
(Conversely: never use a list comprehension if you don't want to produce a list.) And you don't have to handle differently the n = 1 case. Full code:
def bitStr2(n, s):
if n == 1:
return s
result = []
for digit in bitStr2(1, s):
for bits in bitStr2(n - 1, s):
result.append(digit + bits)
return result
Note that for digit in bitStr(1, s) is equivalent to for digit in s. I don't see why you call the method bitStr in this case, since you already know the result.
Related
This is a fairly straight forward programming problem in Python and I am looking for suggestions for further optimization. I am successfully processing in time except for very large strings. I am not looking for code rather areas that I should research for optimization improvements. I have already identified that I can skip even numbers reducing the loop operation and given the nature of the operations the pattern eventually repeats which is why I track when repeat occurs. This allows me break out if n > repeat. I am not positive if converting the string to a list is the most effective.
Problem:
We have a string s and we have a number n that indicates the number of times to run the function. Here is a function that takes your string, concatenates the even-indexed chars to the front, odd-indexed chars to the back. You perform this operation n times.
Example:
example where s = "qwertyuio" and n = 2:
after 1 iteration s = "qetuowryi"
after 2 iterations s = "qtorieuwy"
return "qtorieuwy"
def jumbled_string(s, n):
sl = list(s)
repeat = 0
for y in range(0,n):
for i in range(1, (len(sl)//2)+1):
sl.append(sl.pop(i))
if repeat == 0 and ''.join(sl) == s:
repeat = y+1
break
if repeat != 0:
afterrepeat = n%repeat
for y in range(0,afterrepeat):
for i in range(1, (len(sl)//2)+1):
sl.append(sl.pop(i))
return ''.join(sl)
I don't know what you mean by "pattern repeats". But if we stick to the problem statement, it's a one liner in Python:
s='abecidofug'
from itertools import chain
s2 = ''.join(chain([s[c] for c in range(0, len(s), 2)],[s[c] for c in range(1, len(s), 2)]))
s2
'aeioubcdfg'
In python 3.8+ (due to := operator) you could do it like this:
import collections
def jumbled_string(s: str, n: int) -> str:
generator = (s:=s[::2]+s[1::2] for _ in range(n))
collections.deque(generator, maxlen=0)
return s
Using collections.deque as this is the Fastest (most Pythonic) way to consume an iterator.
Though, for small n I'm finding it faster to use:
def jumbled_string(s: str, n: int) -> str:
for _ in (s:=s[::2]+s[1::2] for _ in range(n)):
pass
return s
Test:
jumbled_string("qwertyuio", 2)
Output:
'qtorieuwy'
You don't explain what n does. The statement is this:
def jumbled_string(s: str) -> str:
even = s[::2]
odd = s[1::2]
return even+odd
print(jumbled_string("0123456789"))
>>>0246813579
here is my code:
def string_match(a, b):
count = 0
if len(a) < 2 or len(b) < 2:
return 0
for i in range(len(a)):
if a[i:i+2] == b[i:i+2]:
count = count + 1
return count
And here are the results:
Correct me if I am wrong but, I see that it didn't work probably because the two string lengths are the same. If I were to change the for loop statement to:
for i in range(len(a)-1):
then it would work for all cases provided. But can someone explain to me why adding the -1 makes it work? Perhaps I'm comprehending how the for loop works in this case. And can someone tell me a more optimal way to write this because this is probably really bad code. Thank you!
But can someone explain to me why adding the -1 makes it work?
Observe:
test = 'food'
i = len(test) - 1
test[i:i+2] # produces 'd'
Using len(a) as your bound means that len(a) - 1 will be used as an i value, and therefore a slice is taken at the end of a that would extend past the end. In Python, such slices succeed, but produce fewer characters.
String slicing can return strings that are shorter than requested. In your first failing example that checks "abc" against "abc", in the third iteration of the for loop, both a[i:i+2] and b[i:i+2] are equal to "c", and therefore count is incremented.
Using range(len(a)-1) ensures that your loop stops before it gets to a slice that would be just one letter long.
Since the strings may be of different lengths, you want to iterate only up to the end of the shortest one. In addition, you're accessing i+2, so you only want i to iterate up to the index before the last item (otherwise you might get a false positive at the end of the string by going off the end and getting a single-character string).
def string_match(a: str, b: str) -> int:
return len([
a[i:i+2]
for i in range(min(len(a), len(b)) - 1)
if a[i:i+2] == b[i:i+2]
])
(You could also do this counting with a sum, but this makes it easy to get the actual matches as well!)
You can use this :
def string_match(a, b):
if len(a) < 2 or len(b) < 0:
return 0
subs = [a[i:i+2] for i in range(len(a)-1)]
occurence = list(map(lambda x: x in b, subs))
return occurence.count(True)
Here is the problem I'm trying to solve:
Given an int, ops, n, create a function(int, ops, n) and slot operators between digits of int to create equations that evaluates to n. Return a list of all possible answers. Importing functions is not allowed.
For example,
function(111111, '+-%*', 11) => [1*1+11/1-1 = 11, 1*1/1-1+11 =11, ...]
The question recommended using interleave(str1, str2) where interleave('abcdef', 'ab') = 'aabbcdef' and product(str1, n) where product('ab', 3) = ['aaa','aab','abb','bbb','aba','baa','bba'].
I have written interleave(str1, str2) which is
def interleave(str1,str2):
lsta,lstb,result= list(str1),list(str2),''
while lsta and lstb:
result += lsta.pop(0)
result += lstb.pop(0)
if lsta:
for i in lsta:
result+= i
else:
for i in lstb:
result+=i
return result
However, I have no idea how to code the product function. I assume it has to do something with recursion, so I'm trying to add 'a' and 'b' for every product.
def product(str1,n):
if n ==1:
return []
else:
return [product(str1,n-1)]+[str1[0]]
Please help me to understand how to solve this question. (Not only the product it self)
General solution
Assuming your implementation of interleave is correct, you can use it together with product (see my suggested implementation below) to solve the problem with something like:
def f(i, ops, n):
int_str = str(i)
retval = []
for seq_len in range(1, len(int_str)):
for op_seq in r_prod(ops, seq_len):
eq = interleave(int_str, op_seq)
if eval(eq) == n:
retval.append(eq)
return retval
The idea is that you interleave the digits of your string with your operators in a varying order. Basically I do that with all possible sequences of length seq_len which varies from 1 to max, which will be the number of digits - 1 (see assumptions below!). Then you use the built-in function eval to evaluate the expression returned by inteleave for a specific sequence of the operators and compare the result with the desired number, n. If the expression evaluates to n you append it to the return array retval (initially empty). After you evaluated all the expressions for all possible operator sequences (see assumptions!) you return the array.
Assumptions
It's not clear whether you can use the same operator multiple times or if you're allowed to omit using some. I assumed you can use the same operator many times and that you're allowed to omit using an operator. Hence, the r_prod was used (as suggested by your question). In case of such restrictions, you will want to use permutations (of possibly varying length) of the group of operators.
Secondly, I assumed that your implementation of the interleave function is correct. It is not clear if, for example, interleave("112", "*") should return both "1*12" and "11*2" or just "1*12" like your implementation does. In the case both should be returned, then you should also iterate over the possible ways the same ordered sequence of operators can be interleaved with the provided digits. I omitted that, because I saw that your function always returns a single string.
Product implementation
If you look at the itertools docs you can see the equivalent code for the function itertools.product. Using that you'd have:
def product(*args, repeat=1):
pools = [tuple(pool) for pool in args] * repeat
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
a = ["".join(x) for x in product('ab', repeat=3)]
print(a)
Which prints ['aaa', 'aab', 'aba', 'abb', 'baa', 'bab', 'bba', 'bbb'] -- what I guess is what you're after.
A more specific (assuming iterable is a string), less efficient, but hopefully more understandable solution would be:
def prod(string, r):
if r < 1:
return None
retval = list(string)
for i in range(r - 1):
temp = []
for l in retval:
for c in string:
temp.append(l + c)
retval = temp
return retval
The idea is simple. The second parameter r gives you the length of the strings you want to produce. The characters in the string give you the elements from which you build the string. Hence, you first generate a string of length 1 that starts with each possible character. Then for each of those strings you generate new strings by concatenating the old string with all of the possible characters.
For example, given a pool of characters "abc", you'll first generate strings "a", "b", and "c". Then you'll replace string "a" with strings "aa", "ab", and "ac". Similarly for "b" and "c". You repeat this process n-times to get all possible strings of length r generated by drawing with replacement from the pool "abc".
I'd think it would be a good idea for you to try to implement the prod function recursively. You can see my ugly solution below, but I'd suggest you stop reading this now and try to do it without looking at my suggestion first.
SPOILER BELOW
def r_prod(string, r):
if r == 1:
return list(string)
else:
return [c + s for c in string for s in r_prod(string, r - 1)]
I have done this code but the output is not like what I want
def replace(s,p,n):
return "".join("{}".format(p) if not i % n else char for i, char in enumerate(s,1))
print(replace("university","-",3))
the output that I get is un-ve-si-y
I must get it like :
uni-ver-sit-y
This is one approach. using str slicing.
Demo:
def replace(s,p,n):
return p.join([s[i:i+n] for i in range(0, len(s), n)])
print(replace("university","-",3))
Output:
uni-ver-sit-y
If you extend the code out over multiple lines:
chars_to_join = []
for i, char in enumerate(s,1):
if not i % n:
chars_to_join.append("{}".format(p))
else:
chars_to_join.append(char)
You'll see that when the if statement is true it'll just replace the character rather than include the replacement character after the given character, so just modify the format string to include the currently iterated character aswell
"{}{}".format(char, p)
Alternatively you can do it functionally like this:
from itertools import repeat
def take(s, n):
""""take n characters from s"""
return s[:n]
def skip(s, n):
""""skip n characters from s"""
return s[n:]
def replace(s, p, n):
# create intervals at which to prefix
intervals = range(0, len(s), n)
# create the prefix for all chunks
prefix = map(skip, repeat(s), intervals)
# trim prefix for n characters each
chunks = map(take, prefix, repeat(n))
return p.join(chunks)
And now:
replace('university', '-', 3)
Will give you:
'uni-ver-sit-y'
Note: this is sample code, if this is meant to be efficient you probably should use lazy evaluated functions (like islice) which can take a lot less memory for bigger inputs.
For this question, I think the list-comprehension is not a very good idea. It's not clearly understood. Maybe we can make it clearer by following:
def replace(s,p,n):
new_list = []
for i, c in enumerate(s, 1):
new_list.append(c)
if i % n == 0:
new_list.append(p)
return "".join(new_list)
print(replace("university","-",3))
I would like to scramble a word with a factor. The bigger the factor is, the more scrambled the word will become.
For example, the word "paragraphs" with factor of 1.00 would become "paaprahrgs", and it will become "paargarphs" with a factor of 0.50.
The distance from the original letter position and the number of scrambled letters should be taken into consideration.
This is my code so far, which only scrambles without a factor:
def Scramble(s):
return ''.join(random.sample(s, len(s)))
Any ideas?
P.S. This isn't an homework job - I'm trying to make something like this: http://d24w6bsrhbeh9d.cloudfront.net/photo/190546_700b.jpg
You could use the factor as a number of shuffling chars in the string around.
As the factor seem's to be between 0 and 1, you can multiply the factor with the string's length.
from random import random
def shuffle(string, factor):
string = list(string)
length = len(string)
if length < 2:
return string
shuffles = int(length * factor)
for i in xrange(shuffles):
i, j = tuple(int(random() * length) for i in xrange(2))
string[i], string[j] = string[j], string[i]
return "".join(string)
x = "computer"
print shuffle(x, .2)
print shuffle(x, .5)
print shuffle(x, .9)
coupmter
eocpumtr
rpmeutoc
If you want the first and the last characters to stay in place, simply split them and add them later on.
def CoolWordScramble(string, factor = .5):
if len(string) < 2:
return string
first, string, last = string[0], string[1:-1], string[-1]
return first + shuffle(string, factor) + last
You haven't defined what your "factor" should mean, so allow me to redefine it for you: A scrambling factor N (an integer) would be the result of swapping two random letters in a word, N times.
With this definition, 0 means the resulting word is the same as the input, 1 means only one pair of letters is swapped, and 10 means the swap is done 10 times.
You can make the "factor" roughly correspond to the number of times two adjacent letters of the word switch their positions (a transposition).
In each transposition, choose a random position (from 0 through the length-minus-two), then switch the positions of the letter at that position and the letter that follows it.
It could be implemented many ways, but here is my solution:
Wrote a function that just changes a letter's place:
def scramble(s):
s = list(s) #i think more easier, but it is absolutely performance loss
p = s.pop(random.randint(0, len(s)-1))
s.insert(random.randint(0, len(s)-1), p)
return "".join(s)
And wrote a function that apply to a string many times:
def scramble_factor(s, n):
for i in range(n):
s = scramble(s)
return s
Now we can use it:
>>> s = "paragraph"
>>> scramble_factor(s, 0)
'paragraph'
>>> scramble_factor(s, 1)
'pgararaph'
>>> scramble_factor(s, 2)
'prahagrap'
>>> scramble_factor(s, 5)
'pgpaarrah'
>>> scramble_factor(s, 10)
'arpahprag'
Of course functions can be combined or nested, but it is clear I think.
Edit:
It doesn't consider distance, but the scramble function easily replaced just for swapping adjacent letters. Here is one:
def scramble(s):
if len(s)<=1:
return s
index = random.randint(0, len(s)-2)
return s[:index] + s[index + 1] + s[index] + s[index+2:]
You could do a for-loop that counts down to 0.
Convert the String into a Char-Array and use a RNG to choose 2 letters to swap.