Подтвердить что ты не робот

Два элемента в массиве, xor - максимальный

Учитывая массив целых чисел, вам нужно найти два элемента, XOR которых максимален.

Существует наивный подход - просто, выбирая каждый элемент и xoring с другими элементами, а затем сравнивая результаты, чтобы найти пару.

Кроме этого, есть ли эффективный алгоритм?

4b9b3361

Ответ 1

Я думаю, что для этого есть алгоритм O (n lg U), где U - наибольшее число. Идея похожа на user949300, но немного более подробно.

Интуиция такова. Когда вы XORing два числа вместе, чтобы получить максимальное значение, вы хотите иметь 1 в наивысшей возможной позиции, а затем пар, которые имеют 1 в этой позиции, вы хотите, чтобы соединение с 1 на следующем возможно наивысшее положение и т.д.

Итак, алгоритм выглядит следующим образом. Начните с нахождения наивысшего 1 бита в любом месте чисел (вы можете сделать это вовремя O (n lg U), выполнив O (lg U) для каждого из n чисел). Теперь разделим массив на две части - одно из чисел, у которых есть 1 в этом бите и группа с 0 в этом бите. Любое оптимальное решение должно сочетать число с 1 в первом месте с числом с 0 в этом месте, так как это поставит 1 бит как можно выше. У любого другого спаривания есть 0.

Теперь, рекурсивно, мы хотим найти спаривание чисел из 1 и 0 группы с наивысшим значением 1 в них. Чтобы сделать это, из этих двух групп разделили их на четыре группы:

  • Числа, начинающиеся с 11
  • Числа, начинающиеся с 10
  • Номера, начинающиеся с 01
  • Номера, начинающиеся с 00

Если есть числа в группе 11 и 00 или в группах 10 и 01, их XOR будет идеальным (начиная с 11). Следовательно, если любая из этих пар групп не пуста, рекурсивно вычислить идеальное решение из этих групп, тогда вернем максимум этих подзадач. В противном случае, если обе группы пусты, это означает, что все цифры должны иметь одну и ту же цифру во второй позиции. Следовательно, оптимальный XOR числа, начинающегося с 1 и числа, начинающегося с 0, закончится тем, что следующий второй бит будет отменен, поэтому мы должны просто посмотреть на третий бит.

Это дает следующий рекурсивный алгоритм, который, начиная с двух групп чисел, разделенных их MSB, дает ответ:

  • Данная группа 1 и группа 0 и бит-индекс i:
    • Если индекс бит равен числу бит, верните XOR (уникальное) число в 1 группе и (уникальный) номер в группе 0.
    • Создайте группы из групп 11, 10, 01 и 00.
    • Если группа 11 и группа 00 непусты, рекурсивно найдите максимальный XOR этих двух групп, начиная с бит я + 1.
    • Если группа 10 и группа 01 непусты, рекурсивно найдите максимальный XOR этих двух групп, начиная с бит я + 1.
    • Если было возможно любое из указанных выше пар, верните максимальную пару, найденную рекурсией.
    • В противном случае все числа должны иметь один и тот же бит в позиции i, поэтому верните найденную максимальную пару, посмотрев бит я + 1 на группы 1 и 0.

Чтобы начать алгоритм, вы можете просто разбивать числа из исходной группы на две группы - цифры с MSB 1 и номерами с MSB 0. Затем вы удаляете рекурсивный вызов вышеуказанного алгоритма с двумя группами числа.

В качестве примера рассмотрим числа 5 1 4 3 0 2. Они имеют представления

101  001  100   011   000   010

Начнем с разбиения их на 1 группу и группу 0:

101  100
001  011  000  010

Теперь применим приведенный выше алгоритм. Мы разделили это на группы 11, 10, 01 и 00:

11:
10:  101  100
01:  011  010
00:  000  001

Теперь мы не можем спаривать любые 11 элементов с 00 элементами, поэтому мы просто возвращаемся к группам 10 и 01. Это означает, что мы создаем группы 100, 101, 010 и 011:

101: 101
100: 100
011: 011
010: 010

Теперь, когда мы попадаем в ведра с одним элементом в них, мы можем просто проверить пары 101 и 010 (что дает 111) и 100 и 011 (что дает 111). Любой из этих вариантов работает здесь, поэтому мы получаем, что оптимальный ответ равен 7.

Подумайте о времени работы этого алгоритма. Обратите внимание, что максимальная глубина рекурсии равна O (lg U), так как в номерах есть только O (log U). На каждом уровне дерева каждое число отображается ровно с одним рекурсивным вызовом, и каждый из рекурсивных вызовов работает пропорционально общему числу чисел в группах 0 и 1, потому что мы должны распределять их по их битам. Следовательно, в дереве рекурсии есть уровни O (log U), и каждый уровень O (n) работает, давая полную работу O (n log U).

Надеюсь, это поможет! Это была потрясающая проблема!

Ответ 2

Игнорируя бит знака, одно из значений должно быть одним из значений с наибольшим значащим битом. Если все значения не имеют установленного бита, в этом случае вы переходите к следующему самому высокому значащему биту, который не задан во всех значениях. Таким образом, вы можете оценить возможности для 1-го значения, посмотрев на HSB. Например, если возможности

0x100000
0x100ABC
0x001ABC
0x000ABC

Первое значение пары max должно быть либо 0x100000, либо 0x10ABCD.

@Внутренняя ошибка сервера Я не думаю, что наименьшее обязательно правильно. У меня нет отличной идеи для сравнения второго значения. Просто любое значение, которое не входит в список возможных 1-го значения. В моем примере 0x001ABC или 0x000ABC.

Ответ 3

Это можно решить с помощью O(NlogN) временной сложности, используя Trie.

  • Постройте trie. Для каждого целочисленного ключа каждый node trie будет удерживать каждый бит (0 или 1), начиная с самого значимого бита.
  • Теперь для каждого элемента arr[i] arr[0, 1, ..... N]
    • Выполните запрос для получения максимального значения xor для arr[i]. Мы знаем, что xor битов разных типов (0 ^ 1 или 1 ^ 0) всегда 1. Поэтому во время запроса для каждого бита попробуйте пройти node, удерживая противоположный бит. Это приведет к тому, что этот бит 1 приведет к максимизации значения xor. Если нет node с противоположным битом, только затем пройдите тот же бит node.
    • После запроса вставьте arr[i] в trie.
    • Для каждого элемента отслеживайте максимальное значение Xor.
    • Во время прохода через каждый node создайте другой ключ, для которого максимальный размер Xor.

Для элементов N нам нужен один запрос (O(logN)) и одна вставка (O(logN)) для каждого элемента. Таким образом, общая временная сложность O(NlogN).

Вы можете найти красивое иллюстрированное объяснение того, как оно работает в этом потоке.

Ниже приведена реализация С++ алгоритма:

const static int SIZE = 2;
const static int MSB = 30;
class trie {
private:
    struct trieNode {
        trieNode* children[SIZE];
        trieNode() {
            for(int i = 0; i < SIZE; ++i) {
                children[i] = nullptr;
            }
        }
        ~trieNode() {
            for(int i = 0; i < SIZE; ++i) {
                delete children[i];
                children[i] = nullptr;
            }
        }
    };
    trieNode* root;
public:
    trie(): root(new trieNode()) {
    }
    ~trie() {
        delete root;
        root = nullptr;
    }

    void insert(int key) {
        trieNode* pCrawl = root;
        for(int i = MSB; i >= 0; --i) {
            bool bit = (bool)(key & (1 << i));
            if(!pCrawl->children[bit]) {
                pCrawl->children[bit] = new trieNode();
            }
            pCrawl = pCrawl->children[bit];
        }
    }

    int query(int key, int& otherKey) {
        int Xor = 0;
        trieNode *pCrawl = root;
        for(int i = MSB; i >= 0; --i) {
            bool bit = (bool)(key & (1 << i));
            if(pCrawl->children[!bit]) {
                pCrawl = pCrawl->children[!bit];
                Xor |= (1 << i);
                if(!bit) {
                    otherKey |= (1 << i); 
                } else {
                    otherKey &= ~(1 << i);
                }
            } else {
                if(bit) {
                    otherKey |= (1 << i); 
                } else {
                    otherKey &= ~(1 << i);
                }
                pCrawl = pCrawl->children[bit];
            }
        }
        return Xor;
    }
};

pair<int, int> findMaximumXorElements(vector<int>& arr) {
    int n = arr.size();
    int maxXor = 0;
    pair<int, int> result; 
    if(n < 2) return result;
    trie* Trie = new trie();
    Trie->insert(0); // insert 0 initially otherwise first query won't find node to traverse
    for(int i = 0; i < n; i++) {
        int elem = 0;
        int curr = Trie->query(arr[i], elem);
        if(curr > maxXor) {
            maxXor = curr;
            result = {arr[i], elem};
        }
        Trie->insert(arr[i]);
    }
    delete Trie;
    return result;
}

Ответ 4

Очень интересная проблема! Вот моя идея:

  • Сначала создайте двоичное дерево из всех чисел, используя двоичный представления и сортировки их в дерево (добавьте начальные нули, чтобы соответствовать самому длинному числу). Когда делается каждый путь от корня до любого листа представляет собой один номер из оригинала набор.
  • Пусть a и b являются указателями на дерево node и инициализируют их в корне.
  • Теперь переместите a и b вниз по дереву, пытаясь использовать противоположные ребра на каждом шаге, т.е. если a перемещается вниз по краю 0, b перемещается вниз по 1-краю, если это не возможно.

Если a и b достигают листа, то должны указывать на два числа с "очень маленькими" одинаковыми битами.

Я только что сделал этот алгоритм и не знаю, правильно ли это или как его доказать. Однако это должно быть в O (n) время выполнения.

Ответ 5

Сделайте рекурсивную функцию, которая в качестве аргументов принимает два списка целых чисел A и B. В качестве возвращаемого значения возвращается два целых числа: один из A и один из B, которые максимизируют XOR этих двух. Если все целые числа равны 0, верните (0,0). В противном случае функция выполняет некоторую обработку и вызывает себя рекурсивно дважды, но с меньшими целыми числами. В одном из рекурсивных вызовов он рассматривает выбор целочисленного числа из списка A для подачи 1 бит k, а в другом вызове он рассматривает выбор целочисленного числа из списка B для подачи от 1 до бит k.

У меня нет времени, чтобы заполнить детали, но, возможно, этого будет достаточно, чтобы увидеть ответ? Кроме того, я не уверен, будет ли время работы лучше, чем N ^ 2, но оно, вероятно, будет.

Ответ 6

Мы можем найти максимальное число в O (n) времени, а затем цикл через массив, выполняющий xor с каждым элементом. Предполагая, что стоимость операции xor равна O (1), мы можем найти max xor двух чисел в O (n) времени.