Performance 确定两个列表是否包含相同的数字项而不进行排序
我有两个列表,我需要确定它们是否包含相同的值而无需排序(即,值的顺序无关)我知道排序是可行的,但这是性能关键部分的一部分。 项目值在[-2,63]范围内,我们总是比较大小相同的列表,但列表大小在[1,8]范围内 示例列表:Performance 确定两个列表是否包含相同的数字项而不进行排序,performance,Performance,我有两个列表,我需要确定它们是否包含相同的值而无需排序(即,值的顺序无关)我知道排序是可行的,但这是性能关键部分的一部分。 项目值在[-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 我认为一个可能的解决方案是比较两个列表的乘积(将所有值相乘),但
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)
}
bool相等(int A[],int B[],int size)
{
int-sumA=1;
int-sumB=1;
对于(int i=0;i
但是,无论列表的顺序/内容如何,这是否始终有效?换句话说,下面的数学公式是正确的吗?因此,我真正想问的是以下问题(除非有其他解决问题的方法):
给定两个大小相同的列表。如果列表的乘积(将所有值相乘)相等,则列表包含相同的值,只要值是大于0的整数。复制第一个列表。然后循环第二个项目,并从副本中删除每个项目。如果一直浏览第二个列表,并在副本中找到所有元素,则这些列表具有相同的元素。这是一个很大的循环,但是列表中最多只有8个元素,使用不同类型的集合不会获得性能提升 如果您有更多的项目,那么就有一个字典/哈希表用于副本。保留值的唯一键,并记录在第一个列表中找到值的次数。这将在较大的列表中提高性能 给定两个大小相同的列表。如果列表的乘积(将所有值相乘)相等,则列表包含相同的值,只要值是大于0的整数 不考虑下面的列表
(9, 9)
(3, 27)
它们的大小相同,元素的乘积也相同。处理8个整数需要多快?在任何现代处理器中排序8件东西几乎不需要时间
简单的方法是使用一个大小为66的数组,其中索引0表示值-2。然后在两个数组中增加计数,然后在它们之间迭代。因为只有66个可能的数字,所以可以创建一个位向量(3个32位字或2个64位字)并比较它们。您只需变换和添加即可完成所有操作。因为在结束之前不需要进行比较(以确定它们是否相等),所以它可以运行得很快,因为不会有很多分支。假设您提前知道范围,您可以使用计数排序的变体。只需扫描每个数组并跟踪每个整数出现的次数
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"
程序比较列表(A、B、最小值、最大值)
域:=最大-最小
计数:=新整数[域]
对于我来说,在一个:
计数[i-min]+=1
对于B中的i:
计数[i-最小]-=1
如果计数[i-min]<0:
//有些东西在B区,但不是A区
返回“不同”
对于我来说,在一个:
如果计数[i-min]>0:
//有些东西在A区,但不是B区
返回“不同”
返回“相同”
这在
O(len(A)+len(B))中是线性的
如果您的列表只有8项,那么排序几乎不会影响性能。如果您想在不排序的情况下执行此操作,可以使用hashmap
你可以用素数来做。为前66个素数保留一个素数表,并使用数组的元素(偏移+2)索引到素数表中 然后,数组的标识就是数组中元素表示的素数的乘积 不幸的是,产品必须至少用67位表示:
- 第66个素数是317,3178=1019703948924652641
- log2(1019703940924652641)=66.47(向上舍入)是67位
int128
数据类型):
乘法不起作用[1,12]==[3,4]确定它们是否相同将花费与排序相同的算法时间。你也许能做一个O(n),但如果你能,你也可以把它排序得那么快。正如@Anon所说,乘法肯定行不通。请注意,如果
Count
为负数,您可以在B
部分提前停止。@schnaader Good call;我已经添加了这一点。难道我们不能将计数{…}中k的更改为A{…}中I的,B{…}中I的更改为大型域
值和小数组,如OP的示例吗?这样,我们将跳过Count
中未触及的条目,这将给出O((len(A)+len(B))*2)
。扫描每个数组两次?听起来很理智。事实上,您可以循环通过A
,B
,然后再循环A
。运行时是O(2*len(A)+len(B))对吗?内存将是O(n)。在这一点上,我认为仅仅排序会更快?如果产品不同,那么列表就不同。@TEACH-是的,但是如果产品相同,你不知道列表是否相同。这不是我担心的单一情况。这是因为我需要它来处理超过100k的列表。这不起作用。你可以亲眼看到1*6*6=2*2*9和1+6+6=2+2+9。因此,您的算法将声明列表{-2,3,3,-2,-2,-2}和{-1,-1,6,
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);
}
#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)));
}