Mapping two integers to one, in a unique and deterministic way

前端 未结 19 2405
不知归路
不知归路 2020-11-22 09:35

Imagine two positive integers A and B. I want to combine these two into a single integer C.

There can be no other integers D and E which combine to C. So combining

19条回答
  •  自闭症患者
    2020-11-22 10:14

    Here is an extension of @DoctorJ 's code to unbounded integers based on the method given by @nawfal. It can encode and decode. It works with normal arrays and numpy arrays.

    #!/usr/bin/env python
    from numbers import Integral    
    
    def tuple_to_int(tup):
        """:Return: the unique non-negative integer encoding of a tuple of non-negative integers."""
        if len(tup) == 0:  # normally do if not tup, but doesn't work with np
            raise ValueError('Cannot encode empty tuple')
        if len(tup) == 1:
            x = tup[0]
            if not isinstance(x, Integral):
                raise ValueError('Can only encode integers')
            return x
        elif len(tup) == 2:
            # print("len=2")
            x, y = tuple_to_int(tup[0:1]), tuple_to_int(tup[1:2])  # Just to validate x and y
    
            X = 2 * x if x >= 0 else -2 * x - 1  # map x to positive integers
            Y = 2 * y if y >= 0 else -2 * y - 1  # map y to positive integers
            Z = (X * X + X + Y) if X >= Y else (X + Y * Y)  # encode
    
            # Map evens onto positives
            if (x >= 0 and y >= 0):
                return Z // 2
            elif (x < 0 and y >= 0 and X >= Y):
                return Z // 2
            elif (x < 0 and y < 0 and X < Y):
                return Z // 2
            # Map odds onto negative
            else:
                return (-Z - 1) // 2
        else:
            return tuple_to_int((tuple_to_int(tup[:2]),) + tuple(tup[2:]))  # ***speed up tuple(tup[2:])?***
    
    
    def int_to_tuple(num, size=2):
        """:Return: the unique tuple of length `size` that encodes to `num`."""
        if not isinstance(num, Integral):
            raise ValueError('Can only encode integers (got {})'.format(num))
        if not isinstance(size, Integral) or size < 1:
            raise ValueError('Tuple is the wrong size ({})'.format(size))
        if size == 1:
            return (num,)
        elif size == 2:
    
            # Mapping onto positive integers
            Z = -2 * num - 1 if num < 0 else 2 * num
    
            # Reversing Pairing
            s = isqrt(Z)
            if Z - s * s < s:
                X, Y = Z - s * s, s
            else:
                X, Y = s, Z - s * s - s
    
            # Undoing mappint to positive integers
            x = (X + 1) // -2 if X % 2 else X // 2  # True if X not divisible by 2
            y = (Y + 1) // -2 if Y % 2 else Y // 2  # True if Y not divisible by 2
    
            return x, y
    
        else:
            x, y = int_to_tuple(num, 2)
            return int_to_tuple(x, size - 1) + (y,)
    
    
    def isqrt(n):
        """":Return: the largest integer x for which x * x does not exceed n."""
        # Newton's method, via http://stackoverflow.com/a/15391420
        x = n
        y = (x + 1) // 2
        while y < x:
            x = y
            y = (x + n // x) // 2
        return x
    

提交回复
热议问题