You could do this with primes. Keep a prime table for the first 66 primes and use the elements of your arrays (offset by +2) to index into the prime table.
The identity of an array is then just the product of the primes represented by the elements in the array.
Unfortunately, the product must be represented with at least 67 bits:
- The 66th prime is 317, and 3178 = 101,970,394,089,246,452,641
- log2(101,970,394,089,246,452,641) = 66.47 (rounded up) is 67 bits
Example pseudocode for doing this (assuming the existence of an int128
data type):
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);
}
You might be able to use a long double
on GCC to store the product since it is defined as an 80-bit data type, but I'm not sure if the floating-point multiplication error would cause collisions between lists. I haven't verified this.
My previous solution below does not work, see the comments below.
For each list:
- Compute the sum of all elements
- Compute the product of all elements
- Store the length of the list (in your case, since the length is guaranteed to be the same for two lists, you can ignore it entirely)
As you compute the sum and product, each element needs to be adjusted by +3, so your range is now [1, 66].
The (sum, product, length) tuple is the identity for your list. Any lists with the same identity are equal.
You can fit this (sum, product, length) tuple into a single 64-bit number:
- For the product: 668 = 360,040,606,269,696, log2(360,040,606,269,696) = 48.36 (rounded up) is 49 bits
- For the sum: 66 * 8 = 528, log2(528) = 9.04 (rounded up) is 10 bits
- Length is in the range [1, 8], log2(8) = 3 bits
- 49 + 10 + 3 = 62 bits for representing the identity
Then, you can do direct 64-bit comparisons to determine equality.
Running-time is linear in the size of the arrays with a single pass over each. Memory usage is O(1)
.
Example code:
#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)));
}