第22講 素数探索を題材としたマルチスレッドプログラミングの学習
第3話 素数探索シングルスレッド解答例

皆さんどうでしたか。
では、まず素数を蓄える方から解答例を示してみましょう。
#pragma once
#include<math.h>
int N;
int cn1=0;
int s[100000000];
namespace 素数探索シングルスレッド {
     ・
     ・
     ・
#pragma endregion
  private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
          int i,j;
          N=int::Parse(textBox1->Text);
          for(i=0;i<cn1;i++){
            s[i]=0;
            cn1=0;
          }
          DateTime^ hj=DateTime::Now; //開始時間
          s[0]=2;
          f1();
          DateTime^ ow=DateTime::Now; //終了時間
          array<String^>^ w=gcnew array<String^>(10);
          for(i=0;i<=cn1;i++){
            w[i % 10]=(s[i]).ToString();
            if(i==cn1){
              for(j=(i%10)+1;j<10;j++){
                w[j]=L"";
              }
            }
            if(i % 10==9)dataGridView1->Rows->Add(w);
          }
          dataGridView1->Rows->Add(w);
          TimeSpan sa=ow->Subtract(*hj); //経過時間の計算
          w[0]=L"計算時間"; w[1]=sa.TotalSeconds.ToString();
          for(i=2;i<10;i++)w[i]=L"";
          dataGridView1->Rows->Add(w);
          w[0]=L"素数個数"; w[1]=(1+cn1).ToString();
          dataGridView1->Rows->Add(w);
     }
     void f1(){
       for (int n=3; n<=N; n+=1) {
         if(sh(n)){
           cn1++;
           s[cn1]=n;
         }
       }
     }
     char sh(double n){
       char h=1;
       double a;
       int b;
       int c;
       int i;
       a=sqrt(n);
       b=(int)a;
       c=(int)n;
       if(c % 2==0)h=0;
       if(h==1){
         for(i=1;i<=cn1;i=i+1){
           if(c % s[i]==0){
             h=0;
             break;
           }
         }
       }
       return(h);
     }
  };
}

ダウンロード用ファイルForm6.h

          for(i=0;i<=cn1;i++){
            w[i % 10]=(s[i]).ToString();
            if(i==cn1){
              for(j=(i%10)+1;j<10;j++){
                w[j]=L"";
              }
            }
            if(i % 10==9)dataGridView1->Rows->Add(w);
          }
の部分が難解ですね。
            if(i==cn1){
              for(j=(i%10)+1;j<10;j++){
                w[j]=L"";
              }
            }
は何のためにあるかと申しますと、
実験してみれば分かりますが、
これがないと

赤の囲いのようになってしまいます。
赤のデータを消すために
            if(i==cn1){
              for(j=(i%10)+1;j<10;j++){
                w[j]=L"";
              }
            }
が必要なわけです。

素数でなく、平方根以下の奇数で割っていく場合のプログラミング

#pragma once
#include<math.h>
int N;
int cn1=0;
int s[100000000];
namespace 素数探索シングルスレッド {
     ・
     ・
     ・
#pragma endregion
  private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
          int i,j;
          N=int::Parse(textBox1->Text);
          for(i=0;i<cn1;i++){
            s[i]=0;
            cn1=0;
          }
          DateTime^ hj=DateTime::Now; //開始時間
          s[0]=2;
          f1();
          DateTime^ ow=DateTime::Now; //終了時間
          array<String^>^ w=gcnew array<String^>(10);
          for(i=0;i<=cn1;i++){
            w[i % 10]=(s[i]).ToString();
            if(i==cn1){
              for(j=(i%10)+1;j<10;j++){
                w[j]=L"";
              }
            }
            if(i % 10==9)dataGridView1->Rows->Add(w);
          }
          dataGridView1->Rows->Add(w);
          TimeSpan sa=ow->Subtract(*hj); //経過時間の計算
          w[0]=L"計算時間"; w[1]=sa.TotalSeconds.ToString();
          for(i=2;i<10;i++)w[i]=L"";
          dataGridView1->Rows->Add(w);
          w[0]=L"素数個数"; w[1]=(1+cn1).ToString();
          dataGridView1->Rows->Add(w);
     }
     void f1(){
        for (int n=3; n<=N; n+=1) {
          if(sh(n)){
            cn1++;
            s[cn1]=n;
          }
       }
     }
     char sh(double n){
       char h=1;
       double a;
       int b;
       int c;
       int i;
       a=sqrt(n);
       b=(int)a;
       c=(int)n;
       if(c % 2==0)h=0;
       if(h==1){
         if(h==1){
           for(i=3;i<=a;i=i+2){
             if(c % i==0){
             h=0;
             break;
           }
         }
       }
       return(h);
     }
  };
}

変更箇所は、色がついている部分のみ。

さて、それではどちら速いでしょうか。実験してみましょう。
データを表にしてみましょう。時間データは、秒です。

探索範囲 10000 100000 1000000
素数で割るタイプ 0.0624 0.702 21.4188
奇数で割るタイプ 0.0624 0.4212 4.6332

計算回数が圧倒的に少ないはずの素数で割るタイプの方が、
範囲が大きくなると計算がかなり遅いという予想外の結果になりました。
プログラミングとは恐ろしいものです。
頭で考えていることと結果が違うことがあるのです。


上の記述は間違いです。実は、素数を蓄える方には、決定的な1行が欠けていました。
それは奇数で割っていくタイプのfor(i=3;i<=a;i=i+2)に相当する部分が欠けています。
素数であるかどうかは、問題にしている整数の平方根以下のみで割ればよいのに、
素数版の方にはうっかりそれを入れ忘れています。
char sh(double n){
  char h=1;
  double a;
  int b;
  int c;
  int i;
  a=sqrt(n);
  b=(int)a;
  c=(int)n;
  if(c % 2==0)h=0;
  if(h==1){
    for(i=1;i<=cn1;i=i+1){
      
if(s[i]>b)break;   //抜けていた決定的な1行
      if(c % s[i]==0){
        h=0;
        break;
      }
    }
  }
return(h);
}
今回も間違いを御指摘して頂いた方は、仮屋崎さんです。
貴重なご意見に感謝申し上げます。
また、私のミスで読者を混乱させたことをお詫び申し上げます。
私は、以後奇数版の方で進めてしまいましたが是非読者の方は、素数型で以後のプログラムを組んで頂ければと思います。
本来であれば以後の講義も書き直すべきですが、今回の主題がマルチスレッド対応プログラミングですし、
書き直しにはかなりの時間がかかりますので、書き直しについては勘弁して頂ければ幸いです。

正しい、データを掲載しておきます。

探索範囲 10000 100000 1000000
素数で割るタイプ 0 0.0312 0.3744
奇数で割るタイプ 0.0624 0.4212 4.6332

というわけで、素数版の圧勝です。







したがって、採用するのは奇数型の方です。
これを次回マルチスレッド化します。




第22講第2話へ 第22講第4話へ


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