想象两个正整数A和B。我想将这两个整数合并为一个整数C。
不能有其他整数D和E组合成C。因此,将它们与加法运算符组合起来是行不通的。例如,30 + 10 = 40 = 40 + 0 = 39 + 1叠加都不起作用。例如,“ 31” +“ 2” = 312 =“ 3” +“ 12”
此组合运算还应该是确定性的(始终在相同的输入下产生相同的结果),并且应始终在整数的正或负侧产生整数。
10,001*A + B
呢?
想象两个正整数A和B。我想将这两个整数合并为一个整数C。
不能有其他整数D和E组合成C。因此,将它们与加法运算符组合起来是行不通的。例如,30 + 10 = 40 = 40 + 0 = 39 + 1叠加都不起作用。例如,“ 31” +“ 2” = 312 =“ 3” +“ 12”
此组合运算还应该是确定性的(始终在相同的输入下产生相同的结果),并且应始终在整数的正或负侧产生整数。
10,001*A + B
呢?
Answers:
您正在寻找双射NxN -> N
映射。这些用于例如燕尾加工。查看此PDF,了解所谓的配对功能。Wikipedia引入了特定的配对功能,即Cantor配对功能:
三句话:
ZxZ -> N
映射。Cantor函数仅适用于非负数。但是,这不是问题,因为定义双射很容易f : Z -> N
,就像这样:
考虑到Cantor配对功能的简单,快速和节省空间的特性,它确实是其中最好的配对功能,但是Matthew Szudzik在Wolfram上发表的文章甚至更好。Cantor配对函数的局限性(相对)是,2N
如果输入是两位整数,则编码结果的范围并不总是保持在一位整数的范围内N
。也就是说,如果我的输入是16
从到的两位整数0 to 2^16 -1
,则2^16 * (2^16 -1)
可能存在输入的组合,因此根据显而易见的Pigeonhole原理,我们需要输出的大小至少为2^16 * (2^16 -1)
,等于2^32 - 2^16
,或者换句话说,是32
理想地,位数应该是可行的。这在编程世界中可能并非没有什么实际意义。
Cantor配对功能:
(a + b) * (a + b + 1) / 2 + a; where a, b >= 0
两个最大的最多16位整数(65535,65535)的映射将为8589803520,如您所见,它不能适合32位。
输入Szudzik的函数:
a >= b ? a * a + a + b : a + b * b; where a, b >= 0
现在(65535,65535)的映射将是4294967295,这是一个32位(0到2 ^ 32 -1)的整数。这是该解决方案的理想之选,它只是利用了该空间中的每个点,因此没有什么可以提高空间效率的。
现在考虑到我们通常处理语言/框架中各种大小的数字的带符号实现这一事实,让我们考虑signed 16
范围为的位整数-(2^15) to 2^15 -1
(稍后我们将看到如何甚至扩展输出以跨越符号范围)。既然a
且b
必须是肯定的,它们的范围是0 to 2^15 - 1
。
Cantor配对功能:
两个最大的最多16位带符号整数(32767、32767)的映射将为2147418112,这刚好小于带符号的32位整数的最大值。
现在 Szudzik的功能是:
(32767,32767)=> 1073741823,要小得多。
让我们考虑负整数。这超出了我所知道的原始问题,但仅是为了帮助将来的访问者。
Cantor配对功能:
A = a >= 0 ? 2 * a : -2 * a - 1;
B = b >= 0 ? 2 * b : -2 * b - 1;
(A + B) * (A + B + 1) / 2 + A;
(-32768,-32768)=> 8589803520,它是Int64。16位输入的64位输出可能是不可原谅的!!
Szudzik的功能:
A = a >= 0 ? 2 * a : -2 * a - 1;
B = b >= 0 ? 2 * b : -2 * b - 1;
A >= B ? A * A + A + B : A + B * B;
(-32768,-32768)=> 4294967295,对于无符号范围是32位,对于有符号范围是64位,但仍然更好。
现在,所有这些输出始终为正。在带符号的世界中,如果我们可以将输出的一半转移到负轴,则将节省更多空间。对于Szudzik,您可以这样做:
A = a >= 0 ? 2 * a : -2 * a - 1;
B = b >= 0 ? 2 * b : -2 * b - 1;
C = (A >= B ? A * A + A + B : A + B * B) / 2;
a < 0 && b < 0 || a >= 0 && b >= 0 ? C : -C - 1;
(-32768, 32767) => -2147483648
(32767, -32768) => -2147450880
(0, 0) => 0
(32767, 32767) => 2147418112
(-32768, -32768) => 2147483647
我的工作:在2
输入上加权后,通过函数,然后将输出除以二,然后乘以将其中一些乘以负轴-1
。
查看结果,对于带符号的16
位数字范围内的任何输入,输出都位于带符号的32
整数整数的范围内,该整数很酷。我不确定如何对Cantor配对功能使用相同的方法,但是并没有尝试那么多,因为效率不高。此外,Cantor配对函数涉及的更多计算也意味着其速度较慢。
这是一个C#实现。
public static long PerfectlyHashThem(int a, int b)
{
var A = (ulong)(a >= 0 ? 2 * (long)a : -2 * (long)a - 1);
var B = (ulong)(b >= 0 ? 2 * (long)b : -2 * (long)b - 1);
var C = (long)((A >= B ? A * A + A + B : A + B * B) / 2);
return a < 0 && b < 0 || a >= 0 && b >= 0 ? C : -C - 1;
}
public static int PerfectlyHashThem(short a, short b)
{
var A = (uint)(a >= 0 ? 2 * a : -2 * a - 1);
var B = (uint)(b >= 0 ? 2 * b : -2 * b - 1);
var C = (int)((A >= B ? A * A + A + B : A + B * B) / 2);
return a < 0 && b < 0 || a >= 0 && b >= 0 ? C : -C - 1;
}
由于中间计算可能会超出有2N
符号整数的限制,因此我使用了4N
整数类型(最后的除法2
将结果带回2N
)。
我在替代解决方案上提供的链接很好地描绘了利用空间中每个点的函数图。令人惊讶的是,您可以将一对坐标唯一地可逆编码为单个数字!数字魔术世界!
(0,0)
到(65535,65535)
单个数字,则a<<16 + b
基本上在每种方式上都更好(更快,更简单,更易于理解,更明显)。如果你想(-32768,-32768)
给(327687,327687)
代替,只是受到32768首位。
如果A和B可以用2个字节表示,则可以将它们组合成4个字节。将A放在最重要的一半,B放在最不重要的一半。
在C语言中,这给出了(假设sizeof(short)= 2和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;
}
combine()
应该return (unsigned short)(A<<16) | (unsigned short)(B);
这样,以便负数可以正确打包。
A<<16
会越界。应该是return (unsigned int)(A<<16) | (unsigned short)(B);
这有可能吗?
您正在组合两个整数。它们都在-2,147,483,648到2,147,483,647之间,但是您只会取正数。这使得2147483647 ^ 2 = 4,61169E + 18组合。由于每个组合必须是唯一的并且要产生一个整数,因此您需要某种可以包含此数量数字的神奇整数。
还是我的逻辑有缺陷?
正整数的标准数学方法是使用素数分解的唯一性。
f( x, y ) -> 2^x * 3^y
不利的一面是图像倾向于跨越很大范围的整数,因此在计算机算法中表达映射时,您可能会在为结果选择合适的类型时遇到问题。
您可以修改此值以处理负数,x
并y
用5和7项的幂对标志进行编码。
例如
f( x, y ) -> 2^|x| * 3^|y| * 5^(x<0) * 7^(y<0)
设数字a
为第一个,b
第二个。让p
是a+1
个质数,q
是b+1
个质数
然后,结果为pq
if a<b,
或2pq
if a>b
。如果a=b
是的话p^2
。
构造映射并不难:
1 2 3 4 5如果(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如果(a,b)==(b,a)(镜像),请使用此映射 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如果需要负/正,请使用此选项 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
弄清楚如何获得任意a,b的值要困难一些。
f(a, b) = s(a+b) + a
,在哪里 s(n) = n*(n+1)/2
s(a+b+1)-s(a+b) = a+b+1
< a
。 我不明白您的意思是:
应该总是在整数的正数或负数上产生一个整数
如何在此论坛中写(大于),(小于)字符?
backtick escapes
。
尽管Stephan202的答案是唯一真正通用的答案,但对于有界范围内的整数,您可以做得更好。例如,如果您的范围是0..10,000,则可以执行以下操作:
#define RANGE_MIN 0
#define RANGE_MAX 10000
unsigned int merge(unsigned int x, unsigned int y)
{
return (x * (RANGE_MAX - RANGE_MIN + 1)) + y;
}
void split(unsigned int v, unsigned int &x, unsigned int &y)
{
x = RANGE_MIN + (v / (RANGE_MAX - RANGE_MIN + 1));
y = RANGE_MIN + (v % (RANGE_MAX - RANGE_MIN + 1));
}
结果可以适合单个整数,其范围最大为整数类型的基数的平方根。这比Stephan202的通用方法稍微有效。解码也相当简单。不需要平方根,对于初学者:)
检查此:http : //en.wikipedia.org/wiki/Pigeonhole_principle。如果A,B和C是同一类型,则无法完成。如果A和B是16位整数,而C是32位整数,则可以简单地使用shift。
哈希算法的本质是无法为每个不同的输入提供唯一的哈希。
这是@DoctorJ的代码根据@nawfal给定的方法扩展为无界整数的方法。它可以编码和解码。它适用于普通数组和numpy数组。
#!/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
更简单的方法是什么:给定两个数字,A和B让str为串联:'A'+';' +'B'。然后让输出为hash(str)。我知道这不是一个数学答案,但是一个简单的python(具有内置的哈希函数)脚本应该可以完成这项工作。
假设您有一个32位整数,为什么不将A移到前16位的一半,而B移到另一半?
def vec_pack(vec):
return vec[0] + vec[1] * 65536;
def vec_unpack(number):
return [number % 65536, number // 65536];
除了这样做尽可能节省空间和计算便宜之外,一个非常酷的副作用是您可以对打包数字进行矢量数学运算。
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
如果要进行更多控制,例如为第一个数字分配X位,为第二个数字分配Y位,则可以使用以下代码:
class NumsCombiner
{
int num_a_bits_size;
int num_b_bits_size;
int BitsExtract(int number, int k, int p)
{
return (((1 << k) - 1) & (number >> (p - 1)));
}
public:
NumsCombiner(int num_a_bits_size, int num_b_bits_size)
{
this->num_a_bits_size = num_a_bits_size;
this->num_b_bits_size = num_b_bits_size;
}
int StoreAB(int num_a, int num_b)
{
return (num_b << num_a_bits_size) | num_a;
}
int GetNumA(int bnum)
{
return BitsExtract(bnum, num_a_bits_size, 1);
}
int GetNumB(int bnum)
{
return BitsExtract(bnum, num_b_bits_size, num_a_bits_size + 1);
}
};
我总共使用32位。这里的想法是,例如,如果您想要第一个数字最大为10位,第二个数字最大为12位,则可以执行以下操作:
NumsCombiner nums_mapper(10/*bits for first number*/, 12/*bits for second number*/);
现在,您可以存储num_a
的最大数量为2^10 - 1 = 1023
,最大存储num_b
值为2^12 - 1 = 4095
。
设置num A和num B的值:
int bnum = nums_mapper.StoreAB(10/*value for a*/, 12 /*value from b*/);
现在bnum
是所有位(总共32位。您可以修改代码以使用64位)以获取num a:
int a = nums_mapper.GetNumA(bnum);
要获取num b:
int b = nums_mapper.GetNumB(bnum);
编辑:
bnum
可以存储在类内部。我之所以没有这样做,是因为我自己的需要,所以我分享了代码,并希望它会有所帮助。
感谢您的消息来源:https :
//www.geeksforgeeks.org/extract-k-bits-given-position-number/
用于提取位的功能,也感谢您mouviciel
在本文中的回答。使用这些来源,我可以找出更高级的解决方案