-1

I am trying to program an algorithm that scrambles and "unscrambles" integer numbers.

I need two functions forward and backward

  • backward(number): return a "random" number between 0 and 9, the same input number always returns the same output
  • forward(number): return the input to backward that returns number

I managed to solve the problem like this:

from random import randint

class Scrambler:

    def __init__(self):

        self.mapping = [i for i in range(10)]

        # scramble mapping
        for i in range(1000):
            r1 = randint(0, len(self.mapping) - 1)
            r2 = randint(0, len(self.mapping) - 1)
            temp = self.mapping[r1]
            self.mapping[r1] = self.mapping[r2]
            self.mapping[r2] = temp

    def backward(self, num):
        return self.mapping[num]

    def forward(self, num):
        return self.mapping.index(num)

if __name__ == '__main__':

    s = Scrambler()
    print(s.mapping)
    for i in range(len(s.mapping)):
        print(i, s.forward(i), s.backward(i), s.forward(s.backward(i)), s.backward(s.forward(i)))

Is there a way to do this without using the mapping list? Can i calculate the return value of the functions forward and backward?

The "randomness" of the numbers does not need to be perfect.

Jonas
  • 1,838
  • 4
  • 19
  • 35
  • 1
    Well yes, it's possible. You'd have to generate a reversible bijective function on the integer range R = [0, 9]. For example f(x) = x + 1 mod 10, f^-1(y) = x -1 mod 10. However, these do not appear very random. Finding the functions f, f^-1 which appear random is not a simple task in general. I don't see anything wrong with your current approach (although `self.mapping[r1], self.mapping[r2] = self.mapping[r2], self.mapping[r1]` looks cleaner than using `temp`). – FHTMitchell Aug 07 '18 at 09:29
  • You need to improve this question, is not clear what you want to achieve. If you say that `backward(number): return a "random" number between 0 and 9`, why you need a `number` argument in the first place? seems not related. *Just what do you think you're doing, Jonas?* – Gsk Aug 07 '18 at 09:29
  • 1
    You can use [`random.shuffle`](https://docs.python.org/3/library/random.html#random.shuffle) to scramble your list. Also you can do `self.mapping[r1], self.mapping[r2] = self.mapping[r2], self.mapping[r1]` for swapping. As for the question, you can design several different bijections from [0, 9] to [0, 9] (that is, formulas to go in one direction and backwards) although the "randomness" is most likely not going to be nearly as good as that of RNG routines. – jdehesa Aug 07 '18 at 09:30
  • mapping list can be set up easy by `self.mapping = list(range(10))` – guidot Aug 07 '18 at 09:42
  • @Gsk I meant random in the way, that the same input always produces the same output, but a random one. FHTMitchells formulation using the term bijective function is probably more unambiguous. – Jonas Aug 07 '18 at 10:29
  • @jdehesa How do I design these functions? Could you point me in the right direction for further research? Do these functions have a special name? – Jonas Aug 07 '18 at 10:30
  • @Jonas In this case you'd be looking at [permutations](https://en.wikipedia.org/wiki/Permutation). However designing a "simple" mathematical formula (that is, one that takes less memory and computation that working with a shuffled list) which expresses a permutation with no obvious patterns (e.g. sequential or evenly spaced mappings, like `f(x) = (A * x + B) mod 10`, with `A` [coprime](https://en.wikipedia.org/wiki/Coprime_integers) with 10) is not easy; the fact that you use a formula (which is, in the end, the pattern for your mappings) will always make it predictable to an extent. – jdehesa Aug 07 '18 at 10:56
  • You may want to read a bit about cryptography. – n. m. could be an AI Aug 07 '18 at 12:17
  • This is quite easy using a [modular multiplicative inverse](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse), which is pretty simple to build. See my answer here: https://stackoverflow.com/questions/34418358/given-a-number-produce-another-random-number-that-is-the-same-every-time-and-di/34420445#34420445. Also see my comment at https://stackoverflow.com/questions/44765539/any-algorithm-that-use-a-number-as-a-feed-for-generating-random-string, and the answer. – Jim Mischel Aug 07 '18 at 14:11

1 Answers1

0

I think your current solution is better than coming up with a function each time. It is a good solution.

Here is a generic solution for a generic key. You'd make your version using the Cipher.random_range method I've stuck on.

import random

class Cipher:

    def __init__(self, key):
        """
        key is a dict of unique values (i.e. bijection)
        """
        if len(set(key.values())) != len(key):
            raise ValueError('key values are not unique')
        self._encoder = key.copy()
        self._decoder = {v: k for k, v in key.items()}

    @classmethod
    def random_range(cls, max):
        lst = list(range(max))
        random.shuffle(lst)
        return cls(dict(enumerate(lst)))

    def encode(self, num):
        return self._encoder[num]

    def decode(self, num):
        return self._decoder[num]
FHTMitchell
  • 11,793
  • 2
  • 35
  • 47