Определение, содержат ли два списка одинаковые числовые элементы без сортировки - PullRequest
3 голосов
/ 08 октября 2010

У меня есть два списка, и мне нужно определить, содержат ли они одинаковые значения без сортировки (т. Е. Порядок значений не имеет значения). Я знаю, что сортировка работала бы, но это часть критической производительности.

Значения элементов находятся в диапазоне [-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.

Ответы [ 7 ]

7 голосов
/ 08 октября 2010

Предполагая, что вы знаете диапазон заранее, вы можете использовать вариацию на счетчике.Просто просмотрите каждый массив и отследите, сколько раз встречается каждое целое число.

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))

3 голосов
/ 08 октября 2010

Вы можете сделать это с помощью простых чисел.Сохраняйте таблицу простых чисел для первых 66 простых чисел и используйте элементы ваших массивов (смещение +2) для индексации в таблицу простых чисел.

Идентификатор массива - это просто произведение простых чисел, представленныхэлементы в массиве.

К сожалению, продукт должен быть представлен как минимум 67 битами:

  • 66 th штрих - 317, а 317 8 = 101 970 394 089 246 452 641
  • log 2 (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
};

// 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);
}

Вы можете использовать long double в GCC для хранения продукта, поскольку он определен как 80-битовый тип данных, но я не уверен, приведет ли ошибка умножения с плавающей точкой к конфликтам между списками.Я не проверял это.


Мое предыдущее решение ниже не работает, см. Комментарии ниже.

Для каждого списка:

  • Вычислитьсумма всех элементов
  • Вычисляет произведение всех элементов
  • Сохраните длину списка (в вашем случае, поскольку длина гарантированно будет одинаковой для двух списков, вы можете игнорировать ееполностью)

Когда вы вычисляете сумму и произведение, каждый элемент должен быть скорректирован на +3, поэтому ваш диапазон теперь равен [1, 66].

(сумма,продукт, длина) кортеж - это личность вашего списка.Любые списки с одинаковыми идентификаторами равны.

Вы можете поместить этот кортеж (сумма, произведение, длина) в одно 64-битное число:

  • Для продукта: 66 8 = 360,040,606,269,696, log 2 (360,040,606,269,696) = 48,36 (округлено) равно 49 битов
  • Для суммы: 66 * 8 = 528, log 2 (528) = 9,04 (округлено) составляет 10 бит
  • Длина находится в диапазоне [1, 8], log 2 (8) = 3 бита
  • 49 + 10 + 3 = 62 бита для представления идентификатора

Затем можно выполнить прямое 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 голосов
/ 08 октября 2010

Поскольку у вас есть только 66 возможных чисел, вы можете создать битовый вектор (3 32-битных слова или 2 64-битных слова) и сравнить их.Вы можете сделать все это только с помощью смен и добавлений.Поскольку не требуется никаких сравнений до конца (чтобы выяснить, равны ли они), он может выполняться быстро, потому что не будет много ветвей.

0 голосов
/ 08 октября 2010

Если в вашем списке всего 8 элементов, сортировка вряд ли скажется на производительности.Если вы хотите сделать это без сортировки, вы можете сделать это, используя hashmap.

  1. Итерация по первому массиву и для каждого значения N в массиве Hash (N) = 1.
  2. Итерация по второму массиву и для каждого значения M, Hash (M)= Hash (M) + 1.
  3. Переберите хеш и найдите все ключи K, для которых Hash (K) = 2.
0 голосов
/ 08 октября 2010

Как быстро вам нужно обрабатывать 8 целых чисел?Сортировка 8 элементов в любом современном процессоре займет почти мгновенное время.

Легко просто использовать массив размером 66, где индекс 0 представляет значение -2.Затем вы просто увеличиваете счетчики в обоих массивах, а затем просто повторяете их.

0 голосов
/ 08 октября 2010

Учитывая 2 одинаковых размера списков.Если произведения (умножая все значения вместе) списков равны, то списки содержат одинаковые значения, если значения являются целыми числами больше 0.

Нет.Рассмотрим следующие списки

(9, 9)
(3, 27)

Они имеют одинаковый размер, а произведение элементов одинаково.

0 голосов
/ 08 октября 2010

Сделайте копию первого списка. Затем переберите второй и удалите каждый элемент из копии. Если вы прошли весь второй список и нашли все элементы в копии, то списки имеют одинаковые элементы. Это много циклов, но при наличии в списке не более 8 элементов вы не получите выигрыша в производительности, если будете использовать другой тип коллекции.

Если у вас было намного больше элементов, то есть словарь / хэш-таблица для копии. Сохраните уникальный ключ значений со счетчиком того, сколько раз они были найдены в первом списке. Это даст вам повышение производительности в больших списках.

...