第12講 様々な並び替えの方法
第7話 並び替え第3の方法の改良の試み

第6話においては、データ数が30までで、範囲が0から100までの整数に限られ、分類数も4と固定されていました。
ここではより大量のデータを扱えるようにして、範囲も分類数も任意に変更できるようにして、
前回のプログラムをより高速化できるか実験してみましょう。
まず、フォームは次のように改造します。

そして、ソースコードは、

     ・
     ・
     ・



(コピーペースト用
#include<stdlib.h>
const int N=300;
int rk=0;




#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
int a[N];
int n=int::Parse(textBox1->Text);
int i;
srand(static_cast<unsigned int>(0));
DateTime^ hj=DateTime::Now;
rk=0;
for(i=0;i<1000;i++){
ds(a,n);
if(n>9)Br(a,n);
Nb(a,n);
}
textBox6->Text=((double)rk/(double)1000).ToString();
DateTime^ ow=DateTime::Now;
TimeSpan sa=ow->Subtract(*hj);
textBox7->Text=(sa.TotalSeconds).ToString();
}
void Br(int* p,int n){
int i,j;
int w=0;
int max=0,min=100;
for(i=0;i<n;i++){
if(max<p[i])max=p[i];
if(min>p[i])min=p[i];
}
textBox4->Text=max.ToString();
textBox5->Text=min.ToString();
double hb;
int M;
M=int::Parse(textBox3->Text);
hb=((double)max-(double)min)/(double)M;
int a[100][N];
int cn[100];
for(i=0;i<M;i++){
cn[i]=0;
for(j=0;j<n;j++){
if(i<M-1){
if(p[j]>max-(i+1)*hb && p[j]<=max-i*hb){
a[i][cn[i]]=p[j];
cn[i]++;
}
}
if(i==M-1){
if(p[j]>=max-(i+1)*hb && p[j]<=max-i*hb){
a[i][cn[i]]=p[j];
cn[i]++;
}
}
}
}
int ks=0;
for(i=0;i<M;i++){
if(cn[i]>0){
for(j=0;j<cn[i];j++){
p[ks]=a[i][j];
ks++;
}
}
}
}
void Nb(int* p,int n){
int i,j;
int w,cn;
for(i=0;i<n-1;i++){
cn=0;
for(j=0;j<n-1;j++){
if(p[j]<p[j+1]){
w=p[j];
p[j]=p[j+1];
p[j+1]=w;
cn++;
}
}
if(cn==0){

rk+=i+1;
break;
}
}
String^ v=L"";
for(i=0;i<n;i++){
if(p[i]<10){
v+=L"00"+(p[i]).ToString()+L" ";
}
else if(p[i]<100){
v+=L"0"+(p[i]).ToString()+L" ";
}
else{
v+=(p[i]).ToString()+L" ";
}
if(i>0 && (i+1)%40==0)v+=L"\n";
}
label9->Text=v;
}
void ds(int* p,int n){
int i;
String^ w=L"";
int s;
s=int::Parse(textBox2->Text);
for(i=0;i<n;i++){
p[i]=rand() % s;
if(p[i]<10){
w+=L"00"+(p[i]).ToString()+L" ";
}
else if(p[i]<100){
w+=L"0"+(p[i]).ToString()+L" ";
}
else{
w+=(p[i]).ToString()+L" ";
}
if(i>0 && (i+1)%40==0)w+=L"\n";
}
label5->Text=w;
}




このソフトによって、最適状態は見つけられるでしょうか。
そして、大幅な改善は見られるでしょうか。

ところが、実験結果は思わしくありません。

データ数300,範囲100の場合(計測時間は、1000回トータル計測時間)

分類数 計測時間
適用なし 3.1356
2 2.886
3 2.8236
4 2.7612
5 2.7456
6 2.6988
7 2.7456
8 2.7612

データ数300,範囲1000の場合(計測時間は、1000回トータル計測時間)

分類数 計測時間
適用なし 3.0576
6 2.6832
10 2.7768
20 2.8236
30 2.9016
40 2.9328
50 2.9796

期待通り平均ループ回数は、データ数300,範囲1000の場合10分の1以下など大幅に改善されましたが、
計算速度の方は、範囲100の場合最大効果でも116%、
範囲1000においては、最大で最大効果113%にすぎません。
どうやら分類法は、たいした改善にはならないようです。
ループ回数が大幅に改善されても、分類に時間が食われてしまい、
結果は相殺されてしまうようです。

では、より高速化するにはどのような工夫が考えられるでしょうか。
皆さんも考えてみてください。

さらに、皆さんに、問題を出しましょう。今までで大きく分けて3つの並び替えの方法を考えました。
2番目と3番目の比較は、今日の実験でできましたが、
1番目と2番目ではどちらが優れているか実験してはいません。
皆さん、第7話のプログラムを改善して、どちらが優れているか実験で確かめましょう。

第6話と第7話のソースコードの詳しい解説は、次話で行いますが、1つだけ解説しておきましょう。。

DateTime^ hj=DateTime::Now;
for(i=0;i<1000;i++){
  ds(a,n);
  if(n>9)Br(a,n);
  Nb(a,n);
}
DateTime^ ow=DateTime::Now;
TimeSpan sa=ow->Subtract(*hj);
textBox7->Text=(sa.TotalSeconds).ToString();


ピンク
の部分は、時間計測のために必要なものです。
DateTime^ hj=DateTime::Now;においてDateTime^型の変数hjを宣言し、
現在の時間の代入も行っています。
C言語の特徴で、宣言と初期化(最初の値を代入すること)同時に行えるのです。
つまり、DateTime^ hj=DateTime::Now;
DateTime^ hj;
hj=DateTime::Now;

と同じです。私は、このように略した書き方は好みませんので、いつもは2行に分けています。
int i;
for(i=0;i<100;i++){
   ・
   ・

for(int i=0;i<100;i++){
   ・
   ・
と略することもできます。
また、DateTime::Nowで現在の時間を取得できます。
DateTime^ ow=DateTime::Now;も現在の時間(つまり終わりの時間)を取得しています。
TimeSpan sa=ow->Subtract(*hj);はTimeSpan型の時間の差変数saを宣言し、
hjとowの差つまり初めと終わりの時間差を求めています。
宣言を
DateTime hj=DateTime::Now;
DateTime ow=DateTime::Now;

(ハンドルマーク^がない)
でしておけば、ここの式はもっとシンプルな
TimeSpan sa=ow-hj;になります。
^付で宣言すると、owとhjの差を求める計算はow->Subtract(*hj)のようになるのです。
これは、決まりだと思ってください。この書き方が気にいらない方は、ハンドル^を入れないで宣言しましょう。
sa.TotalSeconds.TotalSecondsは時間差を秒単位にするためのものです。


第11講第6話へ 第12講第6話へ 第12講第8話へ 第13講第1話へ


vc++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座へ