您可以使用质数来实现此操作。为前66个质数保留一个质数表,并使用数组的元素(偏移+2)索引到质数表中。
然后,数组的标识仅是由数组中表示的质数的乘积。
不幸的是,必须用至少67位表示该乘积:
- 第66个质数是317,317的8次方=101,970,394,089,246,452,641
- log2(101,970,394,089,246,452,641)= 66.47(向上取整)是67位
以下是使用示例伪代码(假设存在int128数据类型):
int primes[] =
{
2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
283, 293, 307, 311, 313, 317
};
int128 identity(int xs[], int length)
{
int128 product = 1;
for (int i = 0; i < length; ++i)
{
product *= primes[xs[i] + 2];
}
return product;
}
bool equal(int a[], int b[], int size)
{
return identity(a, size) == identity(b, size);
}
您可以尝试在GCC上使用
long double
来存储乘积,因为它被定义为80位数据类型,但我不确定浮点数乘法误差是否会导致列表之间的冲突。 我还没有验证过这一点。
我的先前解决方案不起作用,请参见下面的注释。
对于每个列表:
- 计算所有元素的和
- 计算所有元素的乘积
- 存储列表的长度(在您的情况下,由于两个列表的长度保证相同,因此可以完全忽略它)
在计算总和和乘积时,需要将每个元素调整为+3,因此您的范围现在是[1, 66]。
(总和,乘积,长度)元组是列表的标识符。任何具有相同标识符的列表都是相等的。
您可以将此(总和,乘积,长度)元组适配到单个64位数字中:
- 对于乘积:668 = 360,040,606,269,696,log2(360,040,606,269,696) = 48.36(向上取整)即 49位
- 对于总和:66 * 8 = 528,log2(528) = 9.04(向上取整)即 10位
- 长度在[1,8]范围内,log2(8) = 3位
- 49 + 10 + 3 = 表示标识符的62位
然后,您可以进行直接的64位比较来确定相等性。
运行时间与数组大小成线性关系,每个数组仅需单次通过。 内存使用量为O(1)
。
示例代码:
#include <cstdint>
#include <stdlib.h>
uint64_t identity(int xs[], int length)
{
uint64_t product = 1;
uint64_t sum = 0;
for (int i = 0; i < length; ++i)
{
int element = xs[i] + 3;
product *= element;
sum += element;
}
return (uint64_t)length << 59 | (sum << 49) | product;
}
bool equal(int a[], int b[], int size)
{
return identity(a, size) == identity(b, size);
}
void main()
{
int a[] = { 23, 0, -2, 6, 3, 23, -1 };
int b[] = { 0, -1, 6, 23, 23, -2, 3 };
printf("%d\n", equal(a, b, _countof(a)));
}