宜しくお願いします。
Data Rank
---------
32 3
22 2
22 1
18 4.5
9 4.5
8 7
3 6
2 8
※ 同値の場合平均順位を算出すること
この様に、各データ(Data)にデータの大きさで順位(Rank)を付けたいのですが、
反復子にdistanceという関数がありうって付けだと思い下記のプログラムを書きましたが
問題点が出てきました。
[問題点 1]
取得した順位を別の配列に保存したいのと取得した順位の基数を0から1に変える目的で
別の配列に格納したいのですが。
error C2664: 'std::list<_Ty>::push_back' : 1 番目の引数
を 'std::list<_Ty>::_Iterator<_Secure_validation>' から 'const int &' に変換でき
ません。
と型が違うとエラーが出てしまいます、distanceを数値に変換する方法を教えて頂けない
でしょうか
[問題点 2]
下記のプログラムを実行しますと、結果として
最初の 32 は 0番目です.
最初の 22 は 1番目です.
最初の 22 は 1番目です.
最初の 18 は 3番目です.
最初の 9 は 4番目です.
最初の 8 は 5番目です.
最初の 3 は 6番目です.
最初の 2 は 7番目です.
この様な結果になります、同値の場合順位を平均したいのですが、STL的な実装としては
どの様になるのでしょうか?
以上宜しくお願い致します。
nt main() {
list< int > L;
list< int > RANK; // 取得した順位を格納する配列
L.push_back( 32 );
L.push_back( 22 );
L.push_back( 22 );
L.push_back( 18 );
L.push_back( 9 );
L.push_back( 8 );
L.push_back( 3 );
L.push_back( 2 );
list< int > LSource(L.size());
list< int >::iterator iter;
//LSourceに元データをコピー
copy(L.begin(), L.end(), LSource.begin());
//昇順に並べえ
stable_sort(L.begin(), L.end(), greater< int >());
iter = LSource.begin();
while(iter != LSource.end()) {
std::list< int >::iterator it = std::find( L.begin(), L.end(), *iter );
if( it != L.end() ) {
cout << 最初の << *iter << は
<< distance( L.begin(), it ) << 番目です. << endl;
//RANK.push_back(it); // ここでエラー
} else {
cout << *iter << は見つかりませんでした. << endl;
}
iter++;
}
}
・Windows XP SP2
・VS2005 MFC
すみません自己レスですが
[問題点 1]に関しては
RANK.push_back((int)distance( L.begin(), it ));
この様にすることで解決いたしました、[問題点 2]に関してご教示願えないでしょうか
尚上記のプログラムですが
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
が抜けていましたごめんなさい。
例示されたプログラムでは、Lに最初から降順にデータが入っているようですが、
> //LSourceに元データをコピー
> copy(L.begin(), L.end(), LSource.begin());
> //昇順に並べえ
> stable_sort(L.begin(), L.end(), greater< int >());
最初にこういった操作をしているということは、初期状態でのLの並び順はバラバラ
であることもあり得ると仮定しているのでしょうか。
だとしたら、RANKにはどういった順で順位を格納したいのでしょうか。
1. 並べ替え前のデータに対応する順にしたい
(初期状態の L = (1, 10, 5, 5) だとしたら RANK = (4, 1, 2.5, 2.5))
2. 並べ替え後のデータに対応する順にしたい
(初期状態の L = (1, 10, 5, 5) だとしたら、並べ替え後の (10, 5, 5, 1) に
対応する形で、RANK = (1, 2.5, 2.5, 4))
どちらにするかによって採るべき方法が変わってきますので。
あともうひとつ。
> list< int > RANK;
これだと、4.5位とかいった数を入れられないのですが。 list< double > あたり
にすべきではないですか?
まあ、固定小数点数を使うという手もアリですが。
yoh2さんお世話になります。そして宜しくお願い致します。
>例示されたプログラムでは、Lに最初から降順にデータが入っているようですが、
コメントの間違いでした、”降順に並べ替え”を意とします。
×
//昇順に並べえ
○
//降順に並べ替え
>最初にこういった操作をしているということは、初期状態でのLの並び順はバラバラ
>であることもあり得ると仮定しているのでしょうか。
はい、Lの並び順はバラバラの状態でして、重複値の値。及び、重複個数も不定な状態と
なります。
>RANKにはどういった順で順位を格納したいのでしょうか。
yoh2さんに提示して頂いた、1. の事例が期待する動作となります。
>これだと、4.5位とかいった数を入れられないのですが。
ご指摘通りです、私の誤りです。
以上、説明不足、誤記申し訳ございませんでした。
では、問題点3として、順位のリストを元々のリストと対応付けられた順序で保持した
い、
というものも出てきますね。
まずは問題点2について。
std::upper_bound()とstd::distance()を組み合わせて使うことによって、ソート済みの
リストから、同じ値の個数が求められます。
(std::count()を使うのも手ですが、リスト全体を無駄に舐めることになります)
で、平均順位は、 [注目している値より大きな値の数] + (同じ値の個数 + 1) / 2
という式で求められます(納得できなかったら、紙に書いて計算してみましょう)。
コード断片を以下に書いてみます。
// work_list: list<int>型。ソート済み。
// iter: list<int>::iterator型。同じ値の連続(1回も含む)の先頭を指す。
// greater_items: int型。iterが指している値よりも大きな値の数。
// distance(work_list.begin(), iter)でも可。
// (ソイレントグリーンさんが書いたコードと同じですね)
// upper_bound() と distance() の合わせ技で同じ値の個数を得る。
list<int>::iterator same_val_end
= upper_bound(iter, work_list.end(), *iter, greater<int>());
list<int>::difference_type num_same_vals = distance(iter, same_val_end);
// iterが指している値に対応する平均順位。
double r = greater_items + (num_same_vals + 1) / 2.0;
// ちなみに、ここで得た same_val_endを新しいiterとすると、iterは
// 次の値の先頭を指すことになります。
次に問題点3。
リスト中の値とその順位は1対1で対応していますので、ソート済の値と順位の対応表を
作ってやり、最後に元々のリストを順に辿りつつ対応表を見て、順位リストを作る
という方法が使えます。
この、値と順位の対応表として、std::map<int, double> (値→順位) が使えます。
こちらでは、この方針でサンプルを書いてみて、期待通りに動くっことを確認
しましたが、まずは載せないでおきますので考えてみて下さい。
yoh2さんお世話になります、お陰さまで何となくできたっぽいです。
この掲示板に最初投稿してから、ほぼyoh2さんが提示して頂いた、考え方で進めて
いたのですが、ボタンのかけちがいで、おおはまりしていた所でした。
>>リスト中の値とその順位は1対1で対応していますので、ソート済の値と順位の対応表を
>>作ってやり、最後に元々のリストを順に辿りつつ対応表を見て、順位リストを作る
>>という方法が使えます。
はまっていた原因ですが、ソート済の値と順位の対応表の作成で、yoh2さんは「全ての項
目に対して行う」とご助言して下さいましたが、私は
「重複した要素だけの値と重複数」のマップテーブルを作成しようとしていたために
ループ条件と、ループカウンタの進め方で頭が混乱していました(^^;
詳しくは書きませんが、こんな感じで実装していたため、アクセスバイオレーションの
オンパレードで、ステップインしても原因が分からず苦労していました。
//重複値を格納するmap配列宣言map<重複値, 重複数> m;
map< int, double > m;
list< int >::iterator target;
target = L.begin();
//配列mに格納
while ((target = adjacent_find(target, L.end())) != L.end()) {
int res = (int)count(L.begin(), L.end(), *target);
m.insert(pair< int, int >(*target, res));
advance(target, res);
}
以下、yoh2さんにご助言していただいた、意にそって実装したコードを載せますので
皆さんダメ出し宜しくお願い致します(^^;
#include <list>
#include <list>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
list< int > L;
L.push_back( 22 );
L.push_back( 32 );
L.push_back( 22 );
L.push_back( 18 );
L.push_back( 9 );
L.push_back( 100 );
L.push_back( 2 );
L.push_back( 8 );
L.push_back( 100 );
L.push_back( 3 );
L.push_back( 55 );
L.push_back( 44 );
L.push_back( 100 );
L.push_back( 3 );
//元データのLをソートした結果を格納するwork_listを作成する
list< int > work_list(L.size());
//work_listに元データをコピー
copy(L.begin(), L.end(), work_list.begin());
//降順に並べえ
stable_sort(work_list.begin(), work_list.end(), greater< int >());
//同値が連続して出現した最初の位置を指す反復子iter
list< int >::iterator iter;
//イテレータの開始位置をソート済みデータの先頭に設定
iter = work_list.begin();
//ソート済の値と順位テーブルmap<ソート済みデータ, 重複数> m
map< int, double > m;
//ソート済の値と順位の対応表作成
while (iter != work_list.end()) {
//iterが指している値よりも大きな値の数
int greater_items = (int)distance(work_list.begin(), iter);
// upper_bound() と distance() の合わせ技で同じ値の個数を得る。
list<int>::iterator same_val_end
= upper_bound(iter, work_list.end(), *iter, greater<int>());
list<int>::difference_type num_same_vals = distance(iter, same_val_end);
// iterが指している値に対応する平均順位。
double result = greater_items + (num_same_vals + 1) / 2.0;
m.insert(pair< int, double >(*iter, result));
iter++;
}
//ソート済の値と順位の対応表から順位リスト作成map<元データ, 平均順位>
m_order_list
map< int, double > m_order_list;
//順位リスト作成mapのイタレーターの宣言
map< int, double >::iterator it_order;
it_order = m_order_list.begin();
//ソート済の値と順位の対応表mapのイテレータを宣言
map< int, double >::iterator it_sort;
it_sort = m.begin();
//元データのイテレータを宣言
list< int >::iterator it_L;
it_L = L.begin();
//元データをソート済の値と順位の対応表から検索
while(it_L != L.end()) {
it_order = m.find(*it_L);
m_order_list.insert(pair< int, double >(*it_L, it_order->second));
cout << 元データは << *it_L << で << it_order->second << 番目です <<
endl;
it_L++;
}
}
> // iterが指している値に対応する平均順位。
> double result = greater_items + (num_same_vals + 1) / 2.0;
平均順位はこの計算式でいいのかしら?
1位が9人いたら 1 + (9+1)/2 = 6位?
今日はεπιστημη さん、毎度お世話になります。
>>1位が9人いたら 1 + (9+1)/2 = 6位?
切れ味鋭い突っ込みありがとうございます。(^^;
確かにそうですね...
double result = greater_items + (num_same_vals + 1) / 2.0;
この場合greater_items(イテレータの値より大きな要素数)は0個で
num_same_valsが1×9
なので上記のコードですと平均順位は5位ということで、よろしくない結果ですね。
この場合、期待する解としては
1 / 9 = 0.11位ですし
また、
L = (2, 1, 1, 1, 1, 1, 1, 1, 1, 1)
の場合だと
1位 5.5位 5.5位 5.5位 5.5位 5.5位 5.5位 5.5位 5.5位
これまたよろしくないですね
ちょっと一考します
自己レスです
// iterが指している値に対応する平均順位。
//修正前
//double result = greater_items + (num_same_vals + 1) / 2.0;
//修正後
double result = greater_items + 1.0 + (1.0 / (double)num_same_vals);
強引ですがこれで、平均順位の取得ができました。
わかんねーなその計算式。
たとえば得点 9、8, 8, 8, 7 を取った5人がいたら、
8点の3人が 2, 3, 4位を分け合ったのだから平均3位とも考えられる。
それがなぜに2.33位なワケ?
また、2位が10人いたら2.1位、人数が多いほど2位に近づく。
なんでやねん?
> >>1位が9人いたら 1 + (9+1)/2 = 6位?
> ...
> この場合、期待する解としては
> 1 / 9 = 0.11位ですし
0.11位を期待するのが理解できない。
επιστημηさんの疑問と同じだけど、
単独首位よりも同率首位の方が順位が上と表示するの?
そもそも、
> ※ 同値の場合平均順位を算出すること
この仕様が普通じゃないんだけど、
ソイレントグリーンさんの中では、
どのように解釈していますか?
プログラムやSTLから離れて、整合性のある解釈を書いてみてください。
その結果、0.11位や2.33位という計算が正しいというなら
それはそれで構いませんので。
# 思うに、平均順位を次のステップで使いたいのかな?
# 例えば賞金の分配とか。
# その場合、次のステップも含めて平均順位の定義を考えた方が
# 分かりやすいと思うんだけど。
> double result = greater_items + (num_same_vals + 1) / 2.0;
> この場合greater_items(イテレータの値より大きな要素数)は0個で
> num_same_valsが1×9
> なので上記のコードですと平均順位は5位ということで、よろしくない結果ですね。
この式の出所となった(しかも自信たっぷりに言い切ってしまった)yoh2です。
平均、という言葉から、ソイレントグリーンさんが求める結果が
> たとえば得点 9、8, 8, 8, 7 を取った5人がいたら、
> 8点の3人が 2, 3, 4位を分け合ったのだから平均3位とも考えられる。
だと思いこんでしまいましたが、実際はよろしくないようで。申し訳ないです。
でも、επιστημηさんの指摘通り、
> double result = greater_items + 1.0 + (1.0 / (double)num_same_vals);
これはこれでおかしくありませんか?
> また、2位が10人いたら2.1位、人数が多いほど2位に近づく。
極めつけは逆に1人しかいなかった場合。3位になってしまい、普通に順位を
数えたときより1つ低くなりますし。
もしかして、「平均」という言葉とは関係なく、以下のような性質を持つ「順位」を
付けたいのではないですか?
0. まず、注目している値の個数が1個だった場合の順位を「基準順位」と
呼ぶことにする。
注目している値より大きな値の個数+1 と言わない理由は後述。
そして、その注目している値の個数がn個あるものとする。
1. n = 1 のとき、「順位」を「基準順位」そのものとする。
2. nが大きくなれば、「順位」は下がる。(「順位」の数値が大きくなる)
3. 「順位」は、「基準順位」+1 以上になることはない。
(例えば「基準順位」が2だったら、「順位」は 3以上にはならない)
4. 上記の性質を満たすならどんな定義でもよい。
この場合、n = 1の時0になり、nが増加すると必ず増加するが、1以上になることは
ない式(例: (n - 1) / n)を「基準順位」に足してやるか、n = 1の時に1になり、
nが増加すると必ず増加するが、0以下にはならない式 (例: l / (2 ** (n - 1))
ここで ** はべき乗を表すとする) を「基準順位」+1 から引いてやるかすればよいです。
ついでに少し先走ってしまいますが、求めたい「順位」が上記のものでよい場合、
以下のような疑問が生じます。
例えばリストが(9, 8, 8, 8, 7)の時、9、8、7に相当する順位は
それぞれ 1位、2.x位 になります。さて、ここで7に相当する順位はいくつにすべきですか?
7より大きい数が4個あるから、その次の数ということで5位ですか? それとも、3.x位、
4.x位が空位になるのを避けて、3位にするのですか?
先程「基準順位」という新しい言葉を導入したのはこれが理由。
注目している値より大きな値の個数+1 と言ってしまうと、上記の7の基準順位は
5でしかあり得なくなるからです。
> # 思うに、平均順位を次のステップで使いたいのかな?
> # 例えば賞金の分配とか。
んむ。でもそゆことなら、
2位が3人いるなら2,3,4位の賞金総額を山分けするだけやろしなー
話題を変えて、STLに関する話をば。
> stable_sort(work_list.begin(), work_list.end(), greater< int >());
std::list::iteratorを<algorithm>のstd::stable_sort()に食わせていますが、
std::stable_sort()はランダムアクセス反復子しか取れないことになっていますので、
双方向アクセス反復子でしかないstd::list::iteratorをstd::stable_sort()に
食わせるのはNGです。
ちなみにstd::sort()についても同様です。
std::listをソートしたいなら、
work_list.sort(greater<int>());
のように、std::listのメンバ関数のsort()を使うようにして下さい。
(名前こそsortですが、std::stable_sort()と同様に安定なソートです)
あともうひとつ。
> while (iter != work_list.end()) {
> //iterが指している値よりも大きな値の数
> int greater_items = (int)distance(work_list.begin(), iter);
> // upper_bound() と distance() の合わせ技で同じ値の個数を得る。
> list<int>::iterator same_val_end
> = upper_bound(iter, work_list.end(), *iter, greater<int>());
> list<int>::difference_type num_same_vals = distance(iter, same_val_end);
> // iterが指している値に対応する平均順位。
> double result = greater_items + (num_same_vals + 1) / 2.0;
> m.insert(pair< int, double >(*iter, result));
> iter++;
> }
最後、 iter++; となっていますが、これだと、同じ値が複数ある場合、また同じ値に対して
順位を求めてしまいます。しかも、 greater_itemsとnum_same_vals の値が変わるため、
異なった順位が結果として出てしまいます。
最初に計算した値がmに登録されていることにより、m.insert()が蹴られて、間違えた
値が登録されてしまうことはありませんが、無駄な計算を行ってしまっていることには
変わりありません。
iter++; の代わりに、 iter = same_val_end; とすれば、正しく別の値の先頭に飛べます。
順位の定義が揺れているようですが、このiterに関する話は、順位の定義にかかわらず
有効だと思いますので指摘しておきます。
あ、書き間違い。
> nが増加すると必ず増加するが、0以下にはならない式 (例: l / (2 ** (n - 1))
> ここで ** はべき乗を表すとする) を「基準順位」+1 から引いてやるかすればよいです。
× nが増加すると必ず増加するが、0以下にはならない式
○ nが増加すると必ず減少するが、0以下にはならない式