Deux éléments dans un tableau dont le xor est maximum

Étant donné un tableau d'entiers, vous devez trouver deux éléments dont le XOR est maximum.

Il y a une approche naïve-juste en choisissant chaque élément et en xoring avec d'autres éléments, puis en comparant les résultats pour trouver la paire.

Autre que cela, y a-t-il un algorithme efficace?

38
demandé sur templatetypedef 2012-02-17 02:30:47

6 réponses

Je pense avoir un algorithme O(n LG U) pour cela, où U est le plus grand nombre. L'idée est similaire à user949300, mais avec un peu plus de détails.

L'intuition est la suivante. Lorsque vous Xorez deux nombres ensemble, pour obtenir la valeur maximale, vous voulez avoir un 1 à la position la plus élevée possible, puis des appariements qui ont un 1 à cette position, vous voulez un appariement avec un 1 à la prochaine position la plus élevée possible, etc.

, Donc l'algorithme est le suivant. Commencer par trouver le bit 1 le plus élevé n'importe où dans les nombres (vous pouvez le faire dans le temps O(n lg U) en faisant o(LG U) travail pour chacun des N nombres). Maintenant, divisez le tableau en deux morceaux - l'un des nombres qui ont un 1 dans ce bit et le groupe avec 0 dans ce bit. Toute solution optimale doit combiner un nombre avec un 1 au premier endroit avec un nombre avec un 0 à cet endroit, car cela mettrait un bit 1 aussi haut que possible. Tout autre appariement a un 0 là-bas.

Maintenant, récursivement, nous voulons trouver le appariement des nombres du Groupe 1 et 0 qui a le plus haut 1 en eux. Pour ce faire, de ces deux groupes, les diviser en quatre groupes:

  • nombres commençant par 11
  • nombres commençant par 10
  • Numéros commençant par 01
  • Numéros commençant par 00

S'il y a des nombres dans le groupe 11 et 00 ou dans les groupes 10 et 01, leur XOR serait idéal (en commençant par 11). Par conséquent, si l'une de ces paires de groupes n'est pas vide, calculez récursivement la solution idéale à partir de ces groupes, puis renvoyez le maximum de ces solutions de sous-problème. Sinon, si les deux groupes sont vides, cela signifie que tous les nombres doivent avoir le même chiffre dans leur deuxième position. Par conséquent, le XOR optimal d'un nombre commençant par 1 et un nombre commençant par 0 finira par avoir le deuxième bit suivant annulé, donc nous devrions simplement regarder le troisième bit.

Cela donne l'algorithme récursif suivant qui, en commençant par les deux groupes de nombres partitionnés par leur MSB, donne la réponse:

  • étant donné le groupe 1 et le groupe 0 et un indice de bits i:
    • Si l'indice de bits est égal au nombre de bits, renvoyez le XOR du nombre (unique) dans le groupe 1 et le nombre (unique) dans le groupe 0.
    • construisez les groupes 11, 10, 01 et 00 à partir de ces groupes.
    • si le groupe 11 et le groupe 00 ne sont pas vides, trouvez récursivement le XOR maximum de ces deux groupes à partir du bit i + 1.
    • Si groupe 10 et le groupe 01 sont non vides, trouvent récursivement le XOR maximum de ces deux groupes, en commençant par le bit i + 1.
    • Si l'un ou l'autre des appariements ci-dessus était possible, alors renvoyez la paire maximale trouvée par la récursivité.
    • sinon, tous les nombres doivent avoir le même bit en position i, donc retournez la paire maximale trouvée en regardant le bit i + 1 sur les groupes 1 et 0.

Pour commencer l'algorithme, vous pouvez simplement partitionner les nombres du groupe initial en deux groupes-nombres avec MSB 1 et nombres avec MSB 0. Vous lancez ensuite un appel récursif à l'algorithme ci-dessus avec les deux groupes de nombres.

À titre d'exemple, considérons les nombres 5 1 4 3 0 2. Ceux-ci ont des représentations

101  001  100   011   000   010

Nous commençons par les diviser en groupe 1 et en groupe 0:

101  100
001  011  000  010

Maintenant, nous appliquons l'algorithme ci-dessus. Nous divisons cela en groupes 11, 10, 01 et 00:

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

Maintenant, nous ne pouvons pas jumeler 11 éléments avec 00 éléments, donc nous venons de revenir sur les groupes 10 et 01. Cela signifie que nous construisons les groupes 100, 101, 010 et 011:

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

Maintenant que nous sommes à des seaux avec un seul élément, nous pouvons simplement vérifier les paires 101 et 010 (ce qui donne 111) et 100 et 011 (ce qui donne 111). L'une ou l'autre option fonctionne ici, donc nous obtenons que la réponse optimale est 7.

Pensons à la durée de fonctionnement de cet algorithme. Notez que la profondeur de récursivité maximale est O (lg U), car il n'y a que des bits O(log U) dans nombre. A chaque niveau de l'arbre, chaque nombre apparaît exactement dans un appel récursif, et chacun des appels récursifs fonctionne proportionnellement au nombre total de nombres dans les Groupes 0 et 1, car nous devons les distribuer par leurs bits. Par conséquent, il y a des niveaux O(log U) dans l'arbre de récursivité, et chaque niveau fonctionne O(n), ce qui donne un travail total de O(N log U).

Espérons que cela aide! Ce était un jeu génial de problème!

38
répondu templatetypedef 2013-10-24 03:00:12

Ignorer le bit de signe, l'une des valeurs doit être l'une des valeurs les plus importantes ensemble de bits. sauf si toutes les valeurs ont ce bit défini , auquel cas vous passez au bit significatif le plus élevé qui n'est pas défini dans toutes les valeurs. Ainsi, vous pouvez réduire les possibilités pour la valeur 1st en regardant le HSB. Par exemple, si les possibilités sont

0x100000
0x100ABC
0x001ABC
0x000ABC

La 1ère valeur de la paire max doit être 0x100000 ou 0x10ABCD.

@ Erreur interne du serveur I ne pensez pas que le plus petit est nécessairement correct. Je n'ai pas une bonne idée pour éplucher la 2ème valeur. Juste n'importe quelle valeur qui n'est pas dans la liste des 1ère valeurs possibles. Dans mon exemple, 0x001ABC ou 0x000ABC.

4
répondu user949300 2012-02-16 22:52:28

Cela peut être résolu dans O(NlogN) complexité temporelle en utilisant Trie .

  • construire un trie. Pour chaque clé entière, chaque nœud du trie contiendra chaque bit (0 ou 1) à partir du bit le plus significatif.
  • maintenant pour chaque élément {[2] } de arr[0, 1, ..... N]
    • effectuez une requête pour récupérer la valeur xor maximale possible pour arr[i]. Nous savons xor de différents types de bits(0 ^ 1 ou 1 ^ 0) est toujours 1. Donc, pendant la requête pour chaque bit, essayez de traverser le nœud contenant le bit opposé. Cela rendra ce bit particulier 1 résultat en maximisant la valeur xor. S'il n'y a pas de nœud avec bit opposé, alors seulement traverser le même nœud de bit.
    • après la requête, insérez arr[i] dans trie.
    • pour chaque élément, Gardez une trace de la valeur Xor maximale possible.
    • en parcourant chaque nœud, construisez l'autre clé pour laquelle le Xor est maximisé.

Pour N éléments, nous avons besoin d'une requête(O(logN)) et un point d'insertion(O(logN)) pour chaque élément. Donc, la complexité globale du temps est O(NlogN).

Vous pouvez trouver une belle explication picturale sur la façon dont cela fonctionne dans ce fil .

Voici l'implémentation C++ de l'algorithme ci-dessus:

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
répondu Kaidul 2016-10-17 13:45:36

Un problème très intéressant! Voici mon idée:

  • construisez D'abord un arbre binaire à partir de tous les nombres en utilisant le binaire représentation et les trier dans l'arbre bit le plus significatif en premier (ajoutez des zéros en tête pour correspondre au nombre le plus long). Lorsque vous avez terminé chaque chemin de la racine à n'importe quelle feuille représente un nombre de l'original définir.
  • Laissez a et b être des pointeurs vers un nœud d'arbre et initialisez-les à la racine.
  • Déplacez maintenant a et b dans l'arbre, en essayant d'utiliser des bords opposés a chaque étape, c'est-à-dire si a descend un bord 0, b descend un bord 1 sauf si ce n'est pas possible.

Si a et b atteignent une feuille, le devrait pointer vers deux nombres avec "très peu" de bits identiques.

Je viens de faire cet algorithme et je ne sais pas si c'est correct ou comment le prouver. Cependant, il devrait être en O (N) temps d'exécution.

3
répondu Nick 2012-02-16 22:55:16

Crée une fonction récursive qui prend deux listes d'entiers, A et B, comme arguments. Comme valeur de retour, il renvoie deux entiers, un de A et un de B, qui maximisent le XOR des deux. Si tous les entiers sont 0, return (0,0). Sinon, la fonction effectue un traitement et s'appelle récursivement deux fois, mais avec des entiers plus petits. Dans l'un des appels récursifs, il considère prendre un entier de la liste A pour fournir un 1 au bit k, et dans l'autre appel, il considère prendre un entier de la liste B pour fournir un 1 au bit K.

Je n'ai pas le temps maintenant de remplir les détails, mais peut-être que ce sera assez pour voir la réponse? En outre, Je ne suis pas sûr si le temps d'exécution sera meilleur que N^2, mais ce sera probablement le cas.

1
répondu David Grayson 2012-02-16 22:55:14

Nous pouvons trouver le nombre maximum dans le temps O(N) puis parcourir le tableau en faisant xor avec chaque élément. En supposant que le coût d'opération xor est O (1), Nous pouvons trouver max xor de deux nombres dans le temps O(n).

-2
répondu user4131265 2016-02-02 17:31:10