Search code examples
pythonbit

create strings that are very similar (only differ in one bit) in python


example in the picture below

this is what I tried but it output different strings

import string
import random
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
   return ''.join(random.choice("abcdefghijklmnopqrstuvwxyz") for _ in range(5))
print(id_generator())

Solution

  • For this problem randomizing things doesn't sound a good idea.

    Because

    1. Random can reproduce already existing ID.
    2. You need to keep track of already given IDs to make sure you didn't regenerate the same ID.

    We should take a look to real example of this kind of problems. Real examples has rules to make sure

    1. An ID is real with small effort.
    2. Make easy to generate.

    I am living in Turkey and Turkish ID numbers has some (those of I am aware of) rules:

    1. All numbers should be 11 digits.
    2. All ID numbers must be even. End with an even digit.
    3. The last digit must be equal to the reminder of first 10 digits divided by 10.

    In the university that I work, every student is given a number. The rule:

    1. The first 2 digits of this number is the year that the student joined the university. (21 for 2021)
    2. Next 3 number is the identifier of the faculty/institute that student is a member of.
    3. 3 number is the department
    4. The reminding numbers are the order that the student is accepted to the department.

    Now coming to your question. The image you showed is just the permutation of a given list (alphabet).

    For this you should understand the number of permutation grows fast. n!/(n-r)!

    where n is number of elements (in your example the alphabet) and r is the length of string (in your example the ID).

    Here Python's generators come to rescue.

    We need a function to generate permutation. And if it give a state, it should start from there:

    def nex_permutation(list_of_elements, r, start_from):
        res = ""
        if len(start_from) != r:
            raise ValueError(f"start_from length and r are not equal")
    
        carry = 1
        for i in start_from[::-1]:
            ind = list_of_elements.index(i) + carry
            carry = ind // len(list_of_elements)
            res += list_of_elements[ind % len(list_of_elements)]
    
        return res[::-1]
    

    Notice: with this code

    1. We did not check if the given start_from contains only elements from list_of_elements
    2. We going to the first permutation if we try to continue from last possible permutation.

    I leave these problems to you.

    Now we can have a function using nex_permutation to generate next one. Here We will use yield instead of return. Thus this function become a generator. And with generators you can calculate the next element as long as you want.

    def permuataion(list_of_elements, r, start_from=None):
        if start_from is None:
            start_from = list_of_elements[0] * r
            yield start_from
        while True:
            start_from = nex_permutation(list_of_elements, r, start_from=start_from)
            yield start_from
    

    Now let's to use it:

    a = permuataion("abcd", 4)
    
    for _ in range(3):
        print(next(a))
    

    output:

    aaaa
    aaab
    aaac
    

    Test if it continues:

    a = permuataion("abcd", 4, start_from="aabd")
    
    for _ in range(3):
        print(next(a))
    

    output:

    aaca
    aacb
    aacc