web-dev-qa-db-fra.com

Calculer la médiane des valeurs stockées dans le vecteur - C ++?

Je suis un étudiant en programmation, et pour un projet sur lequel je travaille, l'une des choses que je dois faire est de calculer la valeur médiane d'un vecteur de valeurs int. Je dois le faire en utilisant uniquement la fonction de tri de la STL et des fonctions membres vectorielles telles que .begin(), .end() et .size().

Je suis également censé m'assurer de trouver la médiane, que le vecteur ait un nombre impair de valeurs ou un nombre pair de valeurs.

Et je suis coincé, ci-dessous, j'ai inclus ma tentative. Alors, où vais-je me tromper? Je vous serais reconnaissant de bien vouloir me donner des conseils ou des ressources pour aller dans la bonne direction.

Code:

int CalcMHWScore(const vector<int>& hWScores)
{
     const int DIVISOR = 2;
     double median;
     sort(hWScores.begin(), hWScores.end());
     if ((hWScores.size() % DIVISOR) == 0)
     {
         median = ((hWScores.begin() + hWScores.size()) + (hWScores.begin() + (hWScores.size() + 1))) / DIVISOR);
     }
     else 
     {
       median = ((hWScores.begin() + hWScores.size()) / DIVISOR)
     }

    return median;
}

Merci !!

33
Alex

Vous faites une division supplémentaire et dans l'ensemble, vous la rendez un peu plus complexe qu'elle ne devrait l'être. De plus, il n'est pas nécessaire de créer un DIVISOR lorsque 2 est en fait plus significatif dans le contexte.

double CalcMHWScore(vector<int> scores)
{
  size_t size = scores.size();

  if (size == 0)
  {
    return 0;  // Undefined, really.
  }
  else
  {
    sort(scores.begin(), scores.end());
    if (size % 2 == 0)
    {
      return (scores[size / 2 - 1] + scores[size / 2]) / 2;
    }
    else 
    {
      return scores[size / 2];
    }
  }
}
30
Max Shawabkeh

Il n'est pas nécessaire de trier complètement le vecteur: std::nth_element peut faire suffisamment de travail pour mettre la médiane dans la bonne position. Voir ma réponse à cette question pour un exemple.

Bien sûr, cela n'aide pas si votre professeur interdit d'utiliser le bon outil pour le travail.

58
Mike Seymour

Ce qui suit est une fonction simple qui renverra la médiane d'un ensemble de valeurs à l'aide d'itérateurs d'entrée. Il ne modifiera pas l'ensemble de données d'origine, au prix de l'allocation de mémoire.

// Get the median of an unordered set of numbers of arbitrary 
// type without modifying the underlying dataset.
template <typename It>
auto Median(It begin, It end)
{
    using T = typename std::iterator_traits<It>::value_type;
    std::vector<T> data(begin, end);
    std::nth_element(data.begin(), data.begin() + data.size() / 2, data.end());
    return data[data.size() / 2];
}

Si vous souhaitez éviter le coût d'allocation d'une copie de l'ensemble de données et souhaitez modifier l'ensemble de données sous-jacent, vous pouvez utiliser ceci à la place:

// Get the median of an unordered set of numbers of arbitrary 
// type (this will modify the underlying dataset).
template <typename It>
auto Median(It begin, It end)
{
    const auto size = std::distance(begin, end)
    std::nth_element(begin, begin + size / 2, end);
    return *std::next(begin, size / 2);
}
11
quant
const int DIVISOR = 2;

Ne fais pas ça. Cela rend simplement votre code plus compliqué. Vous avez probablement lu des lignes directrices sur la non-utilisation des nombres magiques, mais l'uniformité par rapport à la bizarrerie des nombres est une propriété fondamentale, donc le résumer ne fournit aucun avantage mais entrave la lisibilité.

if ((hWScores.size() % DIVISOR) == 0)
{
    median = ((hWScores.begin() + hWScores.size()) + (hWScores.begin() + (hWScores.size() + 1))) / DIVISOR);

Vous prenez un itérateur à la fin du vecteur, vous prenez un autre itérateur qui pointe au-delà de la fin du vecteur, vous ajoutez les itérateurs (ce qui n'est pas une opération qui a du sens), puis vous divisez l'itérateur résultant (qui n'a pas non plus de sens). C'est le cas le plus compliqué; Je vais d'abord expliquer ce que vous devez faire pour le vecteur de taille impaire et vous laisser le cas de taille paire comme exercice.

}
else 
{
    median = ((hWScores.begin() + hWScores.size()) / DIVISOR)

Encore une fois, vous divisez un itérateur. Ce que vous voulez plutôt faire, c'est incrémenter un itérateur au début du vecteur par des éléments hWScores.size() / 2:

    median = *(hWScores.begin() + hWScores.size() / 2);

Et notez que vous devez déréférencer itérateurs pour en extraire des valeurs. Ce serait plus simple si vous utilisiez des indices:

    median = hWScores[hWScores.size() / 2];
4
jamesdlin

Je donne ci-dessous un exemple de programme qui est quelque peu similaire à celui de la réponse de Max S. Pour aider le PO à faire progresser ses connaissances et sa compréhension, j'ai apporté un certain nombre de changements. J'ai:

a) changé l'appel par référence const en appel par valeur, car sort va vouloir changer l'ordre des éléments dans votre vecteur, (EDIT: je viens de voir que Rob Kennedy a également dit cela pendant que je préparais mon message)

b) remplacé size_t par le vecteur plus approprié <int> :: size_type (en fait, un synonyme pratique de ce dernier),

c) taille enregistrée/2 dans une variable intermédiaire,

d) levé une exception si le vecteur est vide, et

e) J'ai également introduit l'opérateur conditionnel (? :).

En fait, toutes ces corrections sont directement issues du chapitre 4 de "C++ accéléré" de Koenig et Moo.

double median(vector<int> vec)
{
        typedef vector<int>::size_type vec_sz;

        vec_sz size = vec.size();
        if (size == 0)
                throw domain_error("median of an empty vector");

        sort(vec.begin(), vec.end());

        vec_sz mid = size/2;

        return size % 2 == 0 ? (vec[mid] + vec[mid-1]) / 2 : vec[mid];
}
4
Alexandros Gezerlis

Je ne sais pas exactement quelles sont vos restrictions sur l'utilisateur des fonctions membres de vector, mais l'accès aux index avec [] Ou at() rendrait l'accès aux éléments plus simple:

median = hWScores.at(hWScores.size() / 2);

Vous pouvez également travailler avec des itérateurs comme begin() + offset comme vous le faites actuellement, mais vous devez d'abord calculer le décalage correct avec size()/2 et l'ajouter à begin(), pas l'inverse. Vous devez également déréférencer l'itérateur résultant pour accéder à la valeur réelle à ce stade:

median = *(hWScores.begin() + hWScores.size()/2)
0
sth

La réponse acceptée utilise std::sort qui fait plus de travail que nous n'en avons besoin. Les réponses qui utilisent std::nth_element ne gère pas correctement le cas de taille paire.


Nous pouvons faire un peu mieux que simplement utiliser std::sort. Nous n'avons pas besoin de trier complètement le vecteur pour trouver la médiane. On peut utiliser std::nth_element pour trouver l'élément central. Puisque la médiane d'un vecteur avec un nombre pair d'éléments est la moyenne des deux intermédiaires, nous devons faire un peu plus de travail pour trouver l'autre élément central dans ce cas. std::nth_element garantit que tous les éléments précédant le milieu sont inférieurs au milieu. Cela ne garantit pas leur commande au-delà de cela, nous devons donc utiliser std::max_element pour trouver le plus grand élément précédant l'élément du milieu.

int CalcMHWScore(std::vector<int> hWScores) {
  assert(!hWScores.empty());
  const auto middleItr = hWScores.begin() + hWScores.size() / 2;
  std::nth_element(hWScores.begin(), middleItr, hWScores.end());
  if (hWScores.size() % 2 == 0) {
    const auto leftMiddleItr = std::max_element(hWScores.begin(), middleItr);
    return (*leftMiddleItr + *middleItr) / 2;
  } else {
    return *middleItr;
  }
}

Vous voudrez peut-être envisager de renvoyer un double car la médiane peut être une fraction lorsque le vecteur a une taille paire.

0
Kerndog73