Performance 确定两个列表是否包含相同的数字项而不进行排序

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 我认为一个可能的解决方案是比较两个列表的乘积(将所有值相乘),但

我有两个列表,我需要确定它们是否包含相同的值而无需排序(即,值的顺序无关)我知道排序是可行的,但这是性能关键部分的一部分。

项目值在[-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)
}
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

  • 迭代第一个数组,对于数组散列(N)=1中的每个值N
  • 迭代第二个数组,对于每个值M,Hash(M)=Hash(M)+1
  • 迭代散列并找到散列(K)=2的所有键K

  • 你可以用素数来做。为前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)));
    }