45 votes

Trouvez la médiane de la somme des tableaux.

Deux tableaux triés de longueur n sont donnés et la question est de trouver, en O( n ), la médiane de leur tableau de sommes, qui contient toutes les sommes possibles par paire entre chaque élément du tableau A et chaque élément du tableau B.

Par exemple : Soit A[2,4,6] et B[1,3,5] les deux tableaux donnés. Le tableau de somme est [2+1,2+3,2+5,4+1,4+3,4+5,6+1,6+3,6+5] . Trouvez la médiane de ce tableau en O( n ).

Résoudre la question en O( n^2 ) est assez simple, mais y a-t-il des O( n ) solution à ce problème ?

Note : Il s'agit d'une question d'entretien posée à un de mes amis et l'interviewer était tout à fait sûr qu'elle pouvait être résolue en O( n ) temps.

14voto

i Code 4 Food Points 1767

La solution O(n) correcte est assez compliquée et nécessite une quantité importante de texte, de code et de compétences pour l'expliquer et la prouver. Plus précisément, il faut 3 pages pour le faire de façon convaincante, comme on peut le voir en détail ici http://www.cse.yorku.ca/~andy/pubs/X+Y.pdf (trouvé par simonzack dans les commentaires).

Il s'agit essentiellement d'un algorithme astucieux de division et de conquête qui, entre autres choses, tire parti du fait que, dans une matrice triée n par n, on peut trouver en O(n) la quantité d'éléments qui sont plus petits/grands qu'un nombre donné k . Il décompose récursivement la matrice en sous-matrices plus petites ( en ne prenant que les lignes et colonnes impaires, ce qui donne une sous-matrice qui a n/2 colums et n/2 rangées ) qui, combiné à l'étape ci-dessus, donne une complexité de O(n) + O(n/2) + O(n/4)... = O(2*n) = O(n) . C'est fou !

Je ne peux pas l'expliquer mieux que le journal, c'est pourquoi je vais vous expliquer plus simplement, O(n logn) à la place :) .


Solution O(n * logn) :

C'est une interview ! Tu ne peux pas obtenir ça O(n) solution en temps voulu. Alors, pourquoi ne pas proposer une solution qui, même si elle n'est pas optimale, montre que vous pouvez faire mieux que l'autre solution évidente ? O(n²) candidats ?

Je vais me servir de la O(n) algorithme mentionné ci-dessus, pour trouver le nombre de nombres qui sont plus petits/grands qu'un nombre donné k dans un classement n-by-n matrice. Gardez à l'esprit que nous n'avons pas besoin d'une matrice réelle ! La somme cartésienne de deux tableaux de taille n tel qu'il est décrit par le PO, donne un résultat trié. n-by-n que nous pouvons simuler en considérant les éléments du tableau comme suit :

a[3] = {1, 5, 9};
b[3] = {4, 6, 8};
//a + b:
{1+4, 1+6, 1+8,
 5+4, 5+6, 5+8,
 9+4, 9+6, 9+8}

Ainsi, chaque ligne contient des nombres non décroissants, de même que chaque colonne. Maintenant, supposons que l'on vous donne un nombre k . Nous voulons trouver dans O(n) combien de nombres dans cette matrice sont plus petits que k et combien sont supérieurs. De toute évidence, si les deux valeurs sont inférieures à (n²+1)/2 c'est-à-dire k est notre médiane !

L'algorithme est assez simple :

int smaller_than_k(int k){
    int x = 0, j = n-1;
    for(int i = 0; i < n; ++i){
        while(j >= 0 && k <= a[i]+b[j]){
            --j;
        }
        x += j+1;
    }
    return x;
}

Il s'agit essentiellement de compter combien d'éléments répondent à la condition à chaque rangée. Puisque les lignes et les colonnes sont déjà triées comme indiqué ci-dessus, le résultat sera correct. Et comme les deux i et j itérer au maximum n fois chacun, l'algorithme est O(n) [ Notez que j n'est pas réinitialisée dans le cadre de la for boucle ]. Le site greater_than_k L'algorithme est similaire.

Maintenant, comment choisir k ? C'est la logn partie. Recherche binaire ! Comme cela a été mentionné dans d'autres réponses/commentaires, la médiane doit être une valeur contenue dans ce tableau :

candidates[n] = {a[0]+b[n-1], a[1]+b[n-2],... a[n-1]+b[0]}; .

Il suffit de trier ce tableau [également O(n*logn) ], et lancer la recherche binaire sur celui-ci. Puisque le tableau est maintenant dans un ordre non décroissant, il est facile de remarquer que la quantité de nombres plus petits que chaque nombre candidate[i] est également une valeur non décroissante (fonction monotone), ce qui la rend adaptée à la recherche binaire. Le plus grand nombre k = candidate[i] dont le résultat smaller_than_k(k) est plus petit que (n²+1)/2 est la réponse, et s'obtient en log(n) itérations :

int b_search(){
    int lo = 0, hi = n, mid, n2 = (n²+1)/2;
    while(hi-lo > 1){
        mid = (hi+lo)/2;
        if(smaller_than_k(candidate[mid]) < n2)
            lo = mid;
        else
            hi = mid;
    }
    return candidate[lo]; // the median
}

1voto

Khanh Nguyen Points 2414

Disons que les tableaux sont A = {A[1] ... A[n]} et B = {B[1] ... B[n]} et le tableau de la somme par paire est C = {A[i] + B[j], where 1 <= i <= n, 1 <= j <= n} qui a n^2 éléments et nous devons trouver sa médiane.

Médiane de C doit être un élément du tableau D = {A[1] + B[n], A[2] + B[n - 1], ... A[n] + B[1]} : si vous réparez A[i] et considérer toutes les sommes A[i] + B[j] vous verrez que le uniquement A[i] + B[j = n + 1 - i] (qui est l'un des D ) pourrait être la médiane. C'est-à-dire qu'il peut ne pas être la médiane, mais si ce n'est pas le cas, alors tous les autres éléments de l'échantillon doivent être pris en compte. A[i] + B[j] ne sont pas non plus médianes.

Ceci peut être prouvé en considérant tous les B[j] et compter les nombre de valeurs qui sont inférieur et nombre de valeurs qui sont plus grand que A[i] + B[j] (nous pouvons le faire de manière assez précise car les deux tableaux sont triés - le calcul est un peu désordonné). Vous verrez que pour A[i] + B[n + 1 - j] ces deux comptes sont les plus "équilibrés".

Le problème se réduit alors à trouver la médiane de D qui n'a que n éléments. Un algorithme tel que Hoare's fonctionnera.

UPDATE Réponse : cette réponse est fausse. La véritable conclusion ici est que le médiane est l'un des D mais alors D médiane de l'entreprise n'est pas la même chose que C La médiane de l'entreprise.

0voto

tmyklebu Points 7075

Ça ne marche pas ?

Vous pouvez calculer le rang d'un nombre en temps linéaire tant que A et B sont triés. La technique que vous utilisez pour calculer le rang peut également être utilisée pour trouver toutes les choses dans A+B qui sont entre une certaine limite inférieure et une certaine limite supérieure en temps linéaire de la taille de la sortie plus |A|+|B| .

Echantillon aléatoire n des choses de A+B . Prenez la médiane, disons foo . Calculer le rang de foo . Avec une probabilité constante, foo Le rang de l'entreprise est dans n du rang de la médiane. Continuez à faire cela (un nombre constant de fois) jusqu'à ce que vous obteniez des limites inférieures et supérieures de la médiane qui sont dans les limites suivantes 2n l'un de l'autre. (Tout ce processus prend un temps linéaire attendu, mais il est évidemment lent).

Il ne vous reste plus qu'à énumérer tout ce qui se trouve entre les limites et à effectuer une sélection en temps linéaire sur une liste de taille linéaire.

(Sans rapport avec cela, je n'excuserais pas l'intervieweur d'avoir posé une question d'entretien aussi minable. Des trucs comme ça n'indiquent en rien votre capacité à coder).

EDIT : Vous pouvez calculer le rang d'un nombre x en faisant quelque chose comme ça :

Set i = j = 0.
While j < |B| and A[i] + B[j] <= x, j++.
While i < |A| {
  While A[i] + B[j] > x and j >= 0, j--.
  If j < 0, break.
  rank += j+1.
  i++.
}

ÉDITER ENCORE : En fait, l'astuce ci-dessus ne fait que réduire l'espace des candidats à environ n log(n) membres de A+B . Vous avez alors un problème de sélection générale dans un univers de taille n log(n) ; vous pouvez faire essentiellement le même tour une fois de plus et trouver une plage de taille proportionnelle à sqrt(n) log(n) où vous faites la sélection.

Voici pourquoi : Si vous échantillonnez k éléments dans un ensemble de n éléments et que vous prenez la médiane, alors l'ordre de la médiane de l'échantillon se situe entre le (1/2 - sqrt(log(n) / k))ème et le (1/2 + sqrt(log(n) / k))ème élément avec une probabilité au moins constante. Lorsque n = |A+B|, on voudra prendre k = sqrt(n) et on obtiendra un intervalle d'environ sqrt(n log n) éléments --- soit environ |A| log |A|. Mais si l'on recommence, on obtient une plage de l'ordre du sqrt(n) polylog(n).

0voto

Mattia Larentis Points 66

Vous devez utiliser un algorithme de sélection pour trouver la médiane d'une liste non triée en O(n). Regardez ceci : http://en.wikipedia.org/wiki/Selection_algorithm#Linear_general_selection_algorithm_-_Median_of_Medians_algorithm

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X