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

前端 未结 19 2109
不知归路
不知归路 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:09

    Say you have a 32 bit integer, why not just move A into the first 16 bit half and B into the other?

    def vec_pack(vec):
        return vec[0] + vec[1] * 65536;
    
    
    def vec_unpack(number):
        return [number % 65536, number // 65536];
    

    Other than this being as space efficient as possible and cheap to compute, a really cool side effect is that you can do vector math on the packed number.

    a = vec_pack([2,4])
    b = vec_pack([1,2])
    
    print(vec_unpack(a+b)) # [3, 6] Vector addition
    print(vec_unpack(a-b)) # [1, 2] Vector subtraction
    print(vec_unpack(a*2)) # [4, 8] Scalar multiplication
    
    0 讨论(0)
  • 2020-11-22 10:10

    let us have two number B and C , encoding them into single number A

    A = B + C * N

    where

    B=A % N = B

    C=A / N = C

    0 讨论(0)
  • 2020-11-22 10:11

    If A and B can be expressed with 2 bytes, you can combine them on 4 bytes. Put A on the most significant half and B on the least significant half.

    In C language this gives (assuming sizeof(short)=2 and sizeof(int)=4):

    int combine(short A, short B)
    {
        return A<<16 | B;
    }
    
    short getA(int C)
    {
        return C>>16;
    }
    
    short getB(int C)
    {
        return C & 0xFFFF;
    }
    
    0 讨论(0)
  • 2020-11-22 10:13

    It isn't that tough to construct a mapping:

       1  2  3  4  5  use this mapping if (a,b) != (b,a)
    1  0  1  3  6 10
    2  2  4  7 11 16
    3  5  8 12 17 23
    4  9 13 18 24 31
    5 14 19 25 32 40
    
       1  2  3  4  5 use this mapping if (a,b) == (b,a) (mirror)
    1  0  1  2  4  6
    2  1  3  5  7 10
    3  2  5  8 11 14
    4  4  8 11 15 19
    5  6 10 14 19 24
    
    
        0  1 -1  2 -2 use this if you need negative/positive
     0  0  1  2  4  6
     1  1  3  5  7 10
    -1  2  5  8 11 14
     2  4  8 11 15 19
    -2  6 10 14 19 24
    

    Figuring out how to get the value for an arbitrary a,b is a little more difficult.

    0 讨论(0)
  • 2020-11-22 10:13

    Check this: http://en.wikipedia.org/wiki/Pigeonhole_principle. If A, B and C are of same type, it cannot be done. If A and B are 16-bit integers, and C is 32-bit, then you can simply use shifting.

    The very nature of hashing algorithms is that they cannot provide a unique hash for each different input.

    0 讨论(0)
  • 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
    
    0 讨论(0)
提交回复
热议问题