我有两个列表,我需要确定它们是否包含相同的值而不进行排序(即,值的顺序无关紧要)。 我知道排序可行,但这是性能关键部分的一部分。
项目值在[-2,63]范围内,我们总是在比较相同大小的列表,但列表大小的范围是[1,8]。
示例列表:
A = (0, 0, 4, 23, 10)
B = (23, 10, 0, 4, 0)
C = (0, 0, 4, 27, 10)
A == B is true
A == C is false
我认为可能的解决方案是比较两个列表的乘积(将所有值相乘),但此解决方案存在问题。如何处理零和负数。解决方法是在乘法之前为每个值添加4。这是我到目前为止的代码。
bool equal(int A[], int B[], int size)
{
int sumA = 1;
int sumB = 1;
for (int i = 0; i < size; i++) {
sumA *= A[i] + 4;
sumB *= B[i] + 4;
}
return (sumA == sumB)
}
但是,无论列表的顺序/内容是什么,这总是有效吗?换句话说,以下数学上是真的吗?所以我真正要问的是以下(除非有另一种方法来解决问题):
给出2个相同大小的列表。如果列表中的乘积(将所有值相乘)相等,则列表包含相同的值,只要这些值是大于0的整数。
答案 0 :(得分:7)
假设您提前知道范围,则可以使用计数排序的变体。只需扫描每个数组并跟踪每个整数的出现次数。
Procedure Compare-Lists(A, B, min, max)
domain := max - min
Count := new int[domain]
for i in A:
Count[i - min] += 1
for i in B:
Count[i - min] -= 1
if Count[i - min] < 0:
// Something was in B but not A
return "Different"
for i in A:
if Count[i - min] > 0:
// Something was in A but not B
return "Different"
return "Same"
这在O(len(A) + len(B))
答案 1 :(得分:3)
你可以用素数做到这一点。保留前66个素数的素数表,并使用数组的元素(偏移+2)来索引素数表。
数组的标识就是数组中元素所代表的素数的乘积。
不幸的是,产品必须至少包含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
};
// Assumes:
// Each xs[i] is [-2, 63]
// length is [1, 8]
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]。
(sum,product,length)元组是列表的标识。任何具有相同身份的列表都是相同的。
您可以将此(总和,产品,长度)元组合并为一个64位数字:
然后,您可以直接进行64位比较以确定相等性。
运行时间是数组的大小是线性的,每次都有一次传递。内存使用量为O(1)
。
示例代码:
#include <cstdint>
#include <stdlib.h>
// Assumes:
// Each xs[i] is [-2, 63]
// length is [1, 8]
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)));
}
答案 2 :(得分:2)
由于您只有66个可能的数字,因此可以创建一个位向量(3个32位字或2个64位字)并进行比较。只需轮班和添加即可完成所有操作。由于在结束之前不需要进行比较(以确定它们是否相等),因此它可以快速运行,因为不会有很多分支。
答案 3 :(得分:0)
制作第一个清单的副本。然后遍历第二个,并从副本中删除每个项目。如果您完成第二个列表并找到副本中的所有元素,则列表具有相同的元素。这是很多循环,但是列表中只有最多8个元素,使用不同类型的集合不会获得性能提升。
如果您有更多项目,则为该副本提供Dictionary / Hashtable。保留唯一的值键,并计算在第一个列表中找到它们的次数。这将使您在更大的列表上获得性能提升。
答案 4 :(得分:0)
给出2个相同大小的列表。如果列表中的乘积(将所有值相乘)相等,则列表包含相同的值,只要这些值是大于0的整数。
没有。请考虑以下列表
(9, 9)
(3, 27)
它们的大小相同,元素的乘积相同。
答案 5 :(得分:0)
您需要多快处理8个整数?在任何现代处理器中排序8件事几乎不需要时间。
简单的方法是使用大小为66的数组,其中索引0表示值-2。然后你只需在两个数组中递增计数,然后你就可以在之后迭代它们。
答案 6 :(得分:0)
如果您的列表只有8个项目,那么排序几乎不会影响性能。如果你想在没有排序的情况下这样做,你可以使用hashmap这样做。