web-dev-qa-db-ja.com

ベクトルに保存されている値の中央値を計算する-C ++?

私はプログラミングの学生であり、現在取り組んでいるプロジェクトでは、int値のベクトルの中央値を計算する必要があります。これを行うには、STLの並べ替え関数と、.begin().end().size()などのベクトルメンバー関数のみを使用します。

また、ベクトルの値が奇数であるか偶数であるかに関係なく、中央値を見つけるようにする必要があります。

そして、私はStuckです。以下に私の試みを含めました。だから私はどこが間違っているのですか?正しい方向に進むための指針やリソースを提供していただければ幸いです。

コード:

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

ありがとう!!

33
Alex

あなたは余分な除算を行い、全体的に必要以上に複雑にしています。また、コンテキストで実際に2の方が意味がある場合は、DIVISORを作成する必要はありません。

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

ベクトルを完全に並べ替える必要はありません:std::nth_elementは、中央値を正しい位置に配置するのに十分な作業を実行できます。例については、 この質問 に対する私の答えをご覧ください。

もちろん、教師が仕事に適切なツールを使用することを禁じている場合、それは役に立ちません。

58
Mike Seymour

以下は、入力反復子を使用して値のセットの中央値を返す単純な関数です。メモリの割り当てを犠牲にして、元のデータセットを変更しません。

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

データセットのコピーを割り当てるコストを回避し、基になるデータセットを変更したい場合は、代わりにこれを使用できます。

// 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;
_

これをしないでください。コードがより複雑になります。おそらく、マジックナンバーを使用しないことに関するガイドラインを読んだことがありますが、数字の偶数と奇数は基本的な性質です。

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

あなたはベクトルの終わりまでイテレータを取り、ベクトルの終わりを過ぎて指す別のイテレータを取り、イテレータを一緒に追加し(これは理にかなった操作ではありません)、そして結果のイテレータを分割します(これは意味がありません)。これはより複雑なケースです。まず、奇数サイズのベクトルに対して何をすべきかを説明し、偶数サイズのケースは演習として残しておきます。

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

繰り返しますが、イテレータを分割しています。代わりにやりたいことは、イテレータをhWScores.size() / 2要素だけベクトルの先頭にインクリメントすることです:

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

また、値を取得するにはdereferenceイテレータを使用する必要があることに注意してください。インデックスを使用すると、より簡単になります。

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

以下に、Max S.の応答に似たサンプルプログラムを示します。 OPが知識と理解を向上させるために、私はいくつかの変更を加えました。私が持っています:

a)並べ替えはベクトル内の要素の順序を変更するため、const参照による呼び出しをvalue by呼び出しに変更しました(編集:投稿の準備中にRob Kennedyもこれを言ったことがわかりました)

b)size_tをより適切なvector<int> :: size_type(実際には後者の便利な同義語)に置き換え、

c)size/2を中間変数に保存し、

d)ベクトルが空の場合、例外をスローします。

e)条件演算子(?:)も導入しました。

実際、これらの修正はすべて、KoenigとMooによる「Accelerated C++」の第4章から直接外れています。

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

Vectorのメンバー関数のユーザーに対する制限が正確にはわからないが、_[]_またはat()を使用したインデックスアクセスにより、要素へのアクセスが簡単になります。

_median = hWScores.at(hWScores.size() / 2);
_

現在行っているようにbegin() + offsetのようなイテレータを使用することもできますが、最初にsize()/2で正しいオフセットを計算し、それをbegin()に追加する必要があります。その逆ではありません。また、その時点で実際の値にアクセスするには、結果のイテレータを逆参照する必要があります。

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

受け入れられた答えは、std::sortを使用します。これは、必要以上の作業を行います。 std::nth_elementを使用する回答は、偶数サイズのケースを正しく処理しません。


std::sortを使用するだけでなく、少し改善することもできます。中央値を見つけるためにベクトルを完全に並べ替える必要はありません。 std::nth_elementを使用して中央の要素を見つけることができます。偶数の要素を持つベクトルの中央値は中央の2つの平均であるため、その場合はもう一方の中央の要素を見つけるためにもう少し作業を行う必要があります。 std::nth_elementは、中間に先行するすべての要素が中間よりも小さいことを保証します。それ以上の順序は保証されないため、std::max_elementを使用して、中央の要素の前にある最大の要素を見つける必要があります。

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

ベクトルのサイズが偶数の場合、中央値は小数になる可能性があるため、doubleを返すことを検討してください。

0
Kerndog73