将具有不同位长的两个大整数可逆编码为一个整数

时间:2019-01-12 22:09:52

标签: python integer bit-manipulation uniqueidentifier

我想将两个可能具有不同最大位长的大整数编码为一个整数。第一个整数是有符号的(可以是负数),而第二个是无符号的(总是非负数)。如果位长分别为mn,则返回整数的位长应小于或等于m + n

n(而不是m)是预先已知的并且是固定的。作为示例,将使用该解决方案将61+位的带符号nanosecond timestamp与256位的无符号随机性组合在一起,以形成一个317+位的带符号唯一标识符。

我正在使用最新的Python。 special case when m == n中有一个相关的先前存在的问题可以解决这个问题。

3 个答案:

答案 0 :(得分:2)

因为 n 是固定的,所以这个问题很简单:将( a b )编码为 a • 2 n + b

如果 m n 不固定,则该问题是不可能的,因为它要求我们同时对这两者进行编码(两位 a 位( b )和(一位 a ,两位 b )分成三位,这意味着我们必须对十二种可能进行编码( 0,0),(0,1),(0,2),(0,3),(1,0),(1,1),(1,2),(1,3),(2,三个位的八个组合中的0),(2,1),(3,0)和(3,1),这是不可能的。

答案 1 :(得分:0)

此解决方案使用基本的位移位和位提取。使用位运算应比使用指数运算和乘法等更高级别的运算更快。

基本解决方案与特殊情况非常相似,因为在任何一种情况下都只需要一个整数的最大位长。但是,测试不是。

from typing import Tuple
import unittest


class IntMerger:
    """Reversibly encode two integers into a single integer.

    Only the first integer can be signed (possibly negative). The second
    integer must be unsigned (always non-negative).

    In the merged integer, the left bits are of the first input integer, and
    the right bits are of the second input integer.
    """
    # Ref: https://stackoverflow.com/a/54164324/
    def __init__(self, num_bits_int2: int):
        """
        :param num_bits_int2: Max bit length of second integer.
        """
        self._num_bits_int2: int = num_bits_int2
        self._max_int2: int = self._max_int(self._num_bits_int2)

    @staticmethod
    def _max_int(num_bits: int) -> int:
        return (1 << num_bits) - 1

    def merge(self, int1: int, int2: int) -> int:
        return (int1 << self._num_bits_int2) | int2

    def split(self, int12: int) -> Tuple[int, int]:
        int1 = int12 >> self._num_bits_int2
        int2 = int12 & self._max_int2
        return int1, int2


class TestIntMerger(unittest.TestCase):
    def test_intmerger(self):
        max_num_bits = 8
        for num_bits_int1 in range(max_num_bits + 1):
            for num_bits_int2 in range(max_num_bits + 1):
                expected_merged_max_num_bits = num_bits_int1 + num_bits_int2
                merger = IntMerger(num_bits_int2)
                maxint1 = (+1 << num_bits_int1) - 1
                minint1 = (-1 << num_bits_int1) + 1
                for int1 in range(minint1, maxint1 + 1):
                    for int2 in range(1 << num_bits_int2):
                        int12 = merger.merge(int1, int2)
                        # print(f'{int1} ({num_bits_int1}b), {int2} ({num_bits_int2}b) = {int12} ({int12.bit_length()}b)')
                        self.assertLessEqual(int12.bit_length(), expected_merged_max_num_bits)
                        self.assertEqual((int1, int2), merger.split(int12))
                self.assertEqual(int12.bit_length(), expected_merged_max_num_bits)


if __name__ == '__main__':
    unittest.main()

用法示例:

>>> merger = IntMerger(12)

>>> merger.merge(13, 8)
53256
>>> merger.split(_)
(13, 8)

>>> merger.merge(-13, 8)
-53240
>>> merger.split(_)
(-13, 8)

答案 2 :(得分:0)

如果您绝对必须具有完全的可逆性,则需要放松至少一个隐含的初始条件(因为如果您不分别记住这些数字中的任何一个,并且响应位长R小于m + n,您只是不可挽回地丧失了全部可逆性):

  • 您应该使R完全等于m + n,在这种情况下,最简单的方法是将m长度左移n位,然后加上n位数字(以反向方式进行复制,向右移n位以得到m长度的1,向左移n位并从/与编码数相减/按位异或,得到n长度的1),
  • 或者,您应该分别记住某个地方的数字之一(或某种方式)(希望对用户来说是常见的?),然后对这些数字进行按位XOR(相反,对存储的数字进行按位XOR结果);奖励积分,如果对于用户来说是通用的,则每个用户超出第一个的任何额外编码ID只会将max(m,n)位数据添加到存储需求中。