C言語入門 4 (素数を扱う 1)

  • by

C言語のプログラムで素数を判定

これまでのC言語の解説の一旦の締めくくりとして、
素数を扱うプログラムをテーマを2回に分けて記事にします。

今回は「入力された数字が素数かどうか判定する」プログラムを作ります。
ちなみに素数の判定にしてはかなり非効率な方法になります。

今回取り組むこと

  1. 仕様の説明
  2. 関数について
  3. 制御文とフローチャート
  4. プログラム
  5. 問題?
  6. まとめ

1 プログラムの仕様について

素数(prime)は、1と自分自身以外の因数をもたない自然数(1以上の整数)で、
1は含まれない数字のことを指します。

素数であるかないかの判定の対象とする数字は、キーボードを使って入力するものとします。

因数を全て求めて表示します。

因数の数が2と等しい(つまり因数が1と自身の数字のみ)時に素数、
それ以外は素数ではないと判定し、その結果を表示します。

2 使う関数

今回使う道具を以下のリストに示します。

  • scanf関数:キーボードからの入力を受け取る
  • printf関数:画面に出力をする

両方ともstdio.h内にあるので、インクルードするヘッダーファイルはstdio.hのみになります。

3 制御文

プログラミング言語には制御文というものがあります。
基本的にプログラムは上から順に実行されます。
以前の記事で具体例として紹介した「卒業式のプログラム」も、一方通行に進行されます。
(縦書きなので右から左ですが…)

この実行する流れをフロー(flow)と言いますが、これを制御する文を制御文と呼びます。
具体的には繰り返しや分岐をおこなうものです。

今回用いる制御文を以下のリストに示します。

  • for文:繰り返しの処理ができます。
  • if(&else)文:条件の分岐ができます。
  • return文:実行中の関数を終了します。main関数内で現れたらプログラム終了を意味します。

またフローを図示したものをフローチャート(flowchart、もしくは流れ図)と呼びます。
JISによって記号の意味が決められています。が、設計段階で自分用に作るのであれば、
適当でかまわないと思います。

作成するプログラムのフローチャートをFig. 1に示します。

Fig. 1 素数判定プログラムのフローチャート

4 素数判定プログラム

#include <stdio.h>

int main(void){
	int inputNumber;
	int i;
	int countFactor = 0;

	scanf("%d", &amp;inputNumber);
	for(i=1; i <= inputNumber; i++){
		if(inputNumber%i == 0){
			printf("factor = %d\n", i);
			countFactor++;
		}
	}
	
	if(countFactor == 2){
		printf("%d is a prime.", inputNumber);
	}else{
		printf("%d is not a prime.", inputNumber);
	}

	return 0;
}

まずテキストエディタにコピー&ペーストして「prime.c」とでも名前付けて保存しましょう。
「gcc -o prime prime.c」でコンパイル、「prime.exe」で実行ができます。
ためしに「123」と数字を打ってEnterキーを押すと出力が即座に表示されます。

1行目:include ヘッダーファイルの読み込み

使う関数はprintfとscanfでしたのでこの二つが入っているstdio.hをインクルードしています。

3行目:main関数スタート

入力なし(void)で戻り値の型がintです。また23行目の閉じカッコ「}」がセットです。

4~6行目:変数定義

プログラム内で変数を扱うときにはその定義をしなければなりません。
定義の仕方は「(型名) (変数名)」です。
たとえば4行目では「int型のinputNumberといいます!よろしくおねがいします!」と自己紹介(宣言)
していることになります。

変数名は自分で名前を付けることができます。
入力する値である「inputNumber」は、フローチャート(Fig. 1)内の「x」を表しています。
「x」だけでは名前から何を表す変数か推測ができないので、
input、入力されること
Number、中身が数字であること
をわかるようにしています。

命名規則について
変数に限らず定数や自分で作った関数など、自分で名前をつけるときのルールを「命名規則」といいます。
ある程度の慣習はありますが、基本は同じ属性のものは同じ形で表記します。
詳しくは「命名規則」で検索するか、リーダブルコードを読むことをおすすめします。
今回変数については「ローワーキャメルケース」で統一しています。
forでつかうインデックス(5行目の「i」)は、 ループを数えるのに使うことが慣習的に知られているため
例外的にi, j, kでもいいとされています。

7行目のように「=」(代入)を使って初期化することもできます。
初期値を与える感じです。

8行目:scanf キーボードからの入力

このプログラムを実行するとまずカーソルが点滅するだけの状態になります。
これは入力を待っていることになります。
Enterが押されるまでの文字を読み込んでinputNumberの中身が格納される記憶領域に放り込まれます。
したがってscanfの場合は「&(アンパサンド)」が必要になります。

9行目:for ループ開始

Fig. 1における因数表示ループがここで開始されます。14行目の閉じカッコ「}」とセットです。
(i=1; i <= inputNumber; i++)について解説します。

i=1
これはループを数える変数(ループインデックス)であるiの初期化をしています。
そういう意味では6行目と同じような意味です。

i <= inputNumber
この部分では継続する条件を示しています。これを満たすとfor以下(10~12行目)が実行されます。
この条件を満たさなくなった場合は15行目以降に続きます。
条件には比較演算子というものが使われています。
両辺の数字の大小などを比較するもので、今回は「<=」なので「iがinputNumber以下」を意味します。
比較演算子の種類については「比較のための演算子(苦しんで覚えるC言語)」を
参考にするといいと思います。

i++
この部分では操作を表しています。1ループ無事におこなわれるとiに1を足します。

総合すると、
ループインデックスであるiが初期値1で、
iがinputNumber以下だとfor内が実行されてiに1が足されて、
iがinputNumberより大きくなるとループが終了する
、というものです。

10行目:if 条件分岐

13行目の閉じカッコ「}」とセット。

Fig. 1のひし形で→が二つでているブロックは条件によって分岐をする「条件分岐」を意味しており、
if文というもので実現できます。
ifの後ろにある()の中身が条件を示しており、条件を満たす(真、yesとか言う)と{}内が実行されます。
今回の条件は「inputNumber%i が0と等しい」というものです。
%は四則演算の+-×÷と同じ仲間で、余りを求めます。(例えば9%7=2)
inputNumberをiで割った余りが0、つまり割り切れる時に11、12行目が実行されます。
この条件を満たす場合、iはinputNumberの約数であることを意味します。

iはfor文で使っているループインデックスであり、最初は1から始まって、
ループごとに1ずつ足されて、最後はinputNumberと同じ数字になります。
forと合わせて、1~入力された値で順々に割り切れるかどうか調べているという感じです。

11行目:因数の出力

printfを使って因数を出力しています。
(iで入力された値(inputNumber)が割り切れることがif文で示されています。)
実行された画面では%dの部分がiの値に代わって表示されます。
こういった形で結果がわかりやすいように文字列を追加することもできます。

12行目:因数カウンタ

if文の条件から、割り切れる場合にこの行も実行されます。countFactorという変数に1を足します。
したがってこの変数は割り切れた数(=因数の数)に等しくなります。
今回のforとifの組み合わせだと1と自身の数字がiに含まれるので少なくとも2回割り切れることになります。
言い換えればcountFactorの値が2ならば素数、そうでないなら素数ではないと判断できます。

16~20行目:最終出力 inputNumberは素数か?

上で出てきたif文と同じ条件分岐パートです。forパートを抜け出すとここにたどり着けます。
12行目の因数カウンタの部分で説明した通りcountFactorが2のときに、
inputNumberが素数なのでそう表示します。
18行目のelseは「そうでないとき」を表しています。
countFactorが2以外の値をとればこの{}内が実行されます。今回は素数じゃないことを表示しています。

22行目:return文

無事に入力に対して素数かどうか判定できたのでmain関数(=プログラム)を終了します。

5 問題?

これにて今回のプログラムの解説は終わります。
が、しかし、考えてみてください。
ユーザが必ず「1より大きい自然数」を入力してくれるとも限らないですよね?
例えば「-3.464979」とか、「a」とか、「おかめ納豆」とかかもしれません。
このプログラムはそこまで考えていません。

意図しない挙動をさけるために、特定の条件によって処理をおこなわない選択をとることも大切です。
つまり「特定の条件(今回でいう1より大きい自然数が入力された場合)」を「例外」として扱い、
エラー表示など(この例だと 「1より大きい自然数が入力してください」的な )通常と異なる処理を
することが必要なのです。

そしてそのことには「例外処理」という名前がついています。

今回は変な結果出たな~くらいで済みますが、
中にはデータを消すなど不可逆的で、致命的な挙動が起きないとも限りません。
例外処理が甘いところをつくのはハッカーやクラッカーの常套手段…らしいです。

6 素数の判定をするC言語プログラミングまとめ

  • scanfでキーボード入力を受け取れる
  • 制御文(forやifで)の組み合わせでフローチャートの動きを再現できる
  • このプログラムは非常に詰めが甘い

こんなところですかね。ちなみに約数と因数を同じように言っていますが因数の方がより広い概念です。

また説明途中にでてきた苦しんで覚えるC言語ですが書籍も出ているくらい、役に立つサイトです。
名前こそ冗談めいていますが、苦しまずにC言語学びたい人は真っ先にこのサイトを見るべきです。

次はもっと面白い素数の扱い方をしたいと思います。(ヒントは「篩(ふるい)」です。)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です