0

Here's the code

from itertools import combinations, product

string = "abcd012345"

char = "01268abc"

for i, j in combinations(tuple(range(len(string))), 2):
    for char1, char2 in product(char, char):
        print(string[:i] + char1 + string[i+1:j] + char2 + string[j+1:])

We have a string abcd012345 , we change two places in order to get all possible combinations. In this example we use char a set of characters that can be used.

The goal is to change 2 for 3 in order to replace 3 places with replacement and find all possible combinations by changing 3 places.

Will enjoy to learn from your solution, thank you.

permpyt
  • 43
  • 6
  • 1
    I don't get it what you're trying to achieve and what part of it is not working. Maybe you could edit the question to make it clearer (unless it's just me who doesn't get it). – lucidbrot Dec 14 '20 at 14:21
  • @lucidbrot sorry, my bad. Well, the current script changes 2 places and generate all possible combinations, but I need to find a solution where it'll change **3** places at a time. – permpyt Dec 14 '20 at 14:26
  • So something like [`itertools.product(char, repeat=3)`](https://docs.python.org/3/library/itertools.html#itertools.product)? – lucidbrot Dec 14 '20 at 14:46
  • Would it be correct to say that you want to compute all possible ways you could replace exactly three characters in `string` with characters in `char`? – lucidbrot Dec 14 '20 at 14:48
  • Yes, exactly @lucidbrot – permpyt Dec 14 '20 at 14:52

2 Answers2

2

Let us first discuss what you are doing right now and then we build a similar solution. As you clarified in a comment, your end goal is to

compute all possible ways you could replace exactly three characters in string with characters in char

Outer Loop

You do combinations(tuple(range(len(string))), 2). So first you make a tuple containing all the values from 0 to len(string) and then you generate all the possible combinations consisting of two values from that tuple. It's not necessary to use tuple() here. According to the docs the second parameter of combinations is how many times it should pick a value for building one single result. Also in the docs we can find the information that combinations will not reuse values for the resulting tuples.

So we can modify this to

for i, j, k in combinations(range(len(string)), 3)
# generates tuples like this:
# >>> list(combinations(range(4), 3))
# [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)]

Inner Loop

You pick two characters from char. This time, according to the docs it's equivalent to ((x,y) for x in A for y in B). Notice that the docs show that there is a second, optional, parameter:

To compute the product of an iterable with itself, specify the number of repetitions with the optional repeat keyword argument. For example, product(A, repeat=4) means the same as product(A, A, A, A).

So we can use for char1, char2, char3 in product(char, repeat=3). As you can see in the following example, this time there are repetitions.

list(product([1,2,3,4], repeat=3))
[(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 1, 4), (1, 2, 1), (1, 2, 2), (1, 2, 3), (1, 2, 4), (1, 3, 1), (1, 3, 2), (1, 3, 3), (1, 3, 4), (1, 4, 1), (1, 4, 2), (1, 4, 3), (1, 4, 4), (2, 1, 1), (2, 1, 2), (2, 1, 3), (2, 1, 4), (2, 2, 1), (2, 2, 2), (2, 2, 3), (2, 2, 4), (2, 3, 1), (2, 3, 2), (2, 3, 3), (2, 3, 4), (2, 4, 1), (2, 4, 2), (2, 4, 3), (2, 4, 4), (3, 1, 1), (3, 1, 2), (3, 1, 3), (3, 1, 4), (3, 2, 1), (3, 2, 2), (3, 2, 3), (3, 2, 4), (3, 3, 1), (3, 3, 2), (3, 3, 3), (3, 3, 4), (3, 4, 1), (3, 4, 2), (3, 4, 3), (3, 4, 4), (4, 1, 1), (4, 1, 2), (4, 1, 3), (4, 1, 4), (4, 2, 1), (4, 2, 2), (4, 2, 3), (4, 2, 4), (4, 3, 1), (4, 3, 2), (4, 3, 3), (4, 3, 4), (4, 4, 1), (4, 4, 2), (4, 4, 3), (4, 4, 4)]

Loop Content

What you finally do here is printing the string except for the positions where you want to replace characters. You use the characters from the inner loop and the positions from the outer loop. So effectively, for every two characters (including repetitions) you print a string for every possible way of positioning them (without repetitions, because you can't place them both at the same place).

Since we have now modified the loops to generate three positions and three characters, we only need to add the third modification.

It seems like we're in luck: combinations only generates the position tuples in ascending order. So we can assume that i < j < k.

print(string[:i] + char1 + string[i+1:j] + char2 + string[j+1:k] + char3 + string[k+1:])

Putting It All Together

# same as before
from itertools import combinations, product
string = "abcd012345"
char = "01268abc"

# modified as explained above
for i, j, k in combinations(range(len(string)), 3):
  for char1, char2, char3 in product(char, repeat=3):
    print(string[:i] + char1 + string[i+1:j] + char2 + string[j+1:k] + char3 + string[k+1:])
lucidbrot
  • 5,378
  • 3
  • 39
  • 68
  • @permpyt Happy to help! Btw, in case you aren't aware of that, you can "accept" an answer as the one that helped you so that it is visible from the question list that you don't need new ansers. If you want, it's of course legitimate to wait a bit with that to get more answers and then choose the best one – lucidbrot Dec 14 '20 at 18:53
  • @permpyt there should be a grey checkmark under the vote arrows of every answer. You can click one to make it green. Some more information can be found [here](https://stackoverflow.com/help/someone-answers) – lucidbrot Dec 14 '20 at 20:32
  • Can't you figure that out based on the example with three changes I gave? Shouldn't be that hard. I'll gladly explain specifics to you but it's not useful to code for you whatever you need when you then don't understand more afterwards – lucidbrot Dec 15 '20 at 14:55
  • 1
    What I have so far is: for i in combinations(range(len(string)), 1): for char1 in product(char, repeat=1): print(string[:i] + char1 + string[i+1:j]), but see the error: slice indices must be integers or None or have an __index__ method – permpyt Dec 15 '20 at 16:06
  • @permpyt The reason for your error message is that `combinations(range(len(string)), 1)` does not return `[1, 2, 3, 4, ...]` - it returns a list of single-value tuples instead. Try it in the python shell and you'll see what I mean. – lucidbrot Dec 15 '20 at 16:14
  • There's also a problem in your "will do the job, am I correct?" code. It's almost correct. Except that you're saying you only want the tail of the string up to ` j` but I think you want the whole rest of the string. So you'd want to use ` string[i+1:]` instead. – lucidbrot Dec 15 '20 at 16:15
  • I recommend you have a look at what `range(len(string))` gives as result when you run it in the shell. Think about whether you can use this directly - you don't need the call to `combinations` anymore when you have just one index – lucidbrot Dec 15 '20 at 16:16
  • Couldn't resolve this issue, perhaps you can help me with that, here's what I have so far: from itertools import combinations, product string = "abcd012345" char = "01268abc" for i in string: for char1 in product(char, repeat=1): print(string[i+1:] + char1) – permpyt Dec 16 '20 at 11:59
  • @permpyt Do you understand the meaning of the [slice notation](https://stackoverflow.com/questions/509211/understanding-slice-notation)? if you want to take the first three characters of `string`, then your `char1` and then the remaining characters of `string` you need to do `string[:i] + char1 + string[i+1:]`. The colon without anything before/after it stands for "everything from the start"/"everything from the end" – lucidbrot Dec 16 '20 at 20:52
0

This is going to print thousands of strings. You may also want to consider if you're looking for distinct output strings or just raw combinations:

The solution is merely a generalization of your 2-character approach where fixed sized logic is made variable based on a parameter.

from itertools import combinations,product

def replaceCombo(string,char,count):
    for positions in combinations(range(len(string)),count):
        for characters in product(char,repeat=count):
            newString=list(string)
            for p,c in zip(positions,characters):
                newString[p]=c
            yield "".join(newString)


string = "abcd012345"
char   = "01268abc"
for newString in replaceCombo(string,char,2): print(newString)

00cd012345
01cd012345
02cd012345
06cd012345
... total 2,880 strings

print(len(set(replaceCombo(string,char,2)))) # 2,538 distinct strings

for newString in replaceCombo(string,char,3): print(newString)
000d012345
001d012345
002d012345
006d012345
008d012345
00ad012345
... total 61,440 strings

print(len(set(replaceCombo(string,char,3)))) # 51,094 distinct strings
Alain T.
  • 40,517
  • 4
  • 31
  • 51