/*
* CDATE FILENAME
*
* 自然数の階乗を返す関数
*/
#include <stdio.h>
#include "s_print.h" /* s_print_int を利用するので.. */
/*
* int factrial ( int n )
* int n : 階乗を計算する自然数
* 返り値 : n! ( n の階乗 )
*/
n! = n * (n-1) * (n-2) * ... * 2 * 1
= n * { (n-1) * (n-2) * ... * 2 * 1 }
= n * (n-1)!
再帰
0! = 1
終了条件 : n が 0 になった時に終わり
int factrial ( int n ) {
if ( n <= 0 ) { /* 0!, 負の数の階乗は、取り敢えず 1 にする */
return 1; /* 特に 「0! = 1」に注意 */
} else { /* n が正の時は、「n! = n * (n-1)!」となるので.. */
/*
** この部分を完成させなさい
*/
return n * factrial ... ;
}
}
/*
* main
*/
int main( void )
{
s_print_int ( 3 );
s_print_string ( " の階乗の値は " );
s_print_int ( factrial ( 3 ) );
s_print_string ( " になります。\n" );
s_print_int ( 5 );
s_print_string ( " の階乗の値は " );
s_print_int ( factrial ( 5 ) );
s_print_string ( " になります。\n" );
return 0;
}
/*
* CDATE FILENAME
*
* 二つの整数の最大公約数を返す
*/
#include <stdio.h>
#include "s_print.h" /* s_print_int を利用するので.. */
/*
* int euclid ( int a, int b )
* int a : 非負の整数
* int b : 非負の整数
* 返り値 : a と b の最大公約数を返す
*/
int euclid ( int a, int b ) {
if ( b == 0 ) { /* 任意の数 a と 0 の最大公約数 (a,0) は a となる */
return a;
} else { /* b が 0 でない時 (a,b) = (b,a%b) となる */
/*
** この部分を完成させなさい
*/
return euclid ( b, ... );
}
}
/*
* main
*/
int main( void )
{
s_print_int ( 12 );
s_print_string ( " と、 " );
s_print_int ( 18 );
s_print_string ( " の最大公約数は " );
s_print_int ( euclid( 12, 18 ) );
s_print_string ( " です。\n" );
s_print_int ( 1111 );
s_print_string ( " と、 " );
s_print_int ( 111111 );
s_print_string ( " の最大公約数は " );
s_print_int ( euclid( 1111, 111111 ) );
s_print_string ( " です。\n" );
return 0;
}
ユークリッドの互除法
自然数 m,n が与えられたときに、m,n の最大公約数を求める手続(アルゴリズム)
次のステップで考える
1. m,n の大きい方を小さい方(ここでは、m > nとする)で割り、その余りを r とする
2. もし r が 0 なら、割った方(小さい方)が答 (最大公約数)
3. そうでなければ、n (小さい方)と r の最大公約数を求める。
# はじめから、再帰的に定義されている
a%b は、a を b で割った余り(r)になる
/*
* CDATE FILENAME
*
* 数当てを行う
*/
#include <stdio.h>
#include <time.h>
#include "s_print.h" /* s_print_int を利用するので.. */
#include "s_input.h" /* s_input_int を利用するので.. */
/*
* prompt
*/
int prompt(void) {
s_print_string ( "私が選んだ数を予想して入力してください : " );
return s_input_int(); /* キーボードから整数値を一つ読み込む(先読み) */
}
/*
*/
void game ( int input, int answer ) {
if ( input == answer ) {
s_print_string ( "お見事です。あたりました。\n" );
} else {
if ( input > answer ) {
/**BEGIN**/
s_print_string ( "その数は大きすぎます。\n" );
/**END**/
} else {
s_print_string ( "その数は小さすぎます。\n" );
}
game( prompt(), answer );
}
}
/*
* main
*/
int main( void )
{
time_t time;
ctime(&time);
srand ( time.utime );
s_print_int ( rand() );
s_print_string ( "私が考えた 1 〜 100 数を予想して当ててみてください。\n" );
game ( prompt(), (rand()%100) + 1 );
return 0;
}
/*
方針
問題は、乱数で生成する -> rand 関数を利用する
(rand()%100) + 1
rand() : 0 〜 3 万まで乱数
(rand()%100) : 0 〜 99 まで乱数
(rand()%100)+1 : 1 〜 100 まで乱数
人間に答を予想してもらって、その予想した整数値を入力し、
その結果を出力する
あたるまで、繰返し ( インプットループ )
ヒントとしては、大小関係を教える
*/
/*
* CDATE FILENAME
*
* 与えられた自然数の素因数を出力する
*
* 12 は..
*
*
*
*
*
*
*/
#include <stdio.h>
#include "s_print.h" /* s_print_int を利用するので.. */
/*
* void print_prime_factor_sequence ( int n, int p )
* 機能 : p 以上の n の素因数を小さい順に並べで表示する
* 条件 : n には p 未満の素因数を含まれていないものとする
* int n : p 未満の素因数を含まれていない自然数
* 返り値 : なし
*/
void print_prime_factor_sequence ( int n, int p ) {
if ( p > n ) { /* もう n には p 以上の素因数を含まない */
/* 何もしなくてよい */
/* 本来、ここにくるのは n = 1 の時だが、念の為 */
} else if ( n % p == 0 ) { /* n が p を素因数として含む */
s_print_char ( ' ' ); /* 隙間を空け */
s_print_int ( p ); /* 素因数を表示し */
/* もう一度 p の要素があるかもしれないので、そこから試す [再帰] */
n が p で割り切れるので、
n / p の素因数分解をするので
print_prime_factor_sequence ( n/p, 2 );
でもよいのだが...
すでに、p 未満の数では割り切れない事がわかっているから..
print_prime_factor_sequence ( n/p, ... );
/*
** この部分を完成させなさい
*/
} else { /* n が p を素因数として含まない */
/* 本来は p の次の素数を試すべきだが.. */
/* その代りに p + 1 を試す (のは無駄だが、正く動く) [再帰] */
/*
** この部分を完成させなさい
*/
print_prime_factor_sequence ( n, p + 1 );
}
}
/*
* void print_prime_factor_of ( int n )
* 機能 : n の素因数を出力する
* int n : 素因数を表示したい自然数
* 返り値 : なし
*/
void print_prime_factor_of ( int n ) {
if ( n < 1 ) { /* 与えらた数が自然数ではない */
s_print_int ( n );
s_print_string ( "は、自然数ではありません\n" );
} else if ( n == 1 ) { /* 与えられた数が 1 の場合 */
s_print_int ( n );
s_print_string ( "の素因数はありません\n" );
} else {
s_print_int ( n );
s_print_string ( "の素因数分解は" );
/*
** この部分を完成させなさい
*/
print_prime_factor_sequence ( n, ... ); /* ... の所は最初の素数 */
s_print_string ( " となります。\n" );
}
}
/*
* main
*/
int main( void )
{
print_prime_factor_of ( 12 );
return 0;
}
/*
print_prime_factor_of ( 12 );
-> 12 = 2^2 * 3^1
2 2 3
素因数は、どうやって求めるか ?
小さい方の素数から順にわってみる
割りきれたら、素因数として出力する
!!! 素数を作る必要がありそう...
-> (計算時間は無駄になるが..) 2 以上の自然数でわっていってよい
例 15 の場合 [A]
15 を 2 ( 最初の素数 )で、割ってみる
割り切れない
15 を 3 ( 2 の次の素数 ) で、割ってみる
割り切れ、商は 5 になる。
3 をプリント
5 を 3 ( 2 の次の素数 ) で、割ってみる
割り切れない
5 を 5 ( 3 の次の素数 ) で、割ってみる
割り切れ、商は 1 になる。
5 をプリント
1 は、これ以上、素因数をふくまないので、終わり
結果として、 3 5 が表示される
例 15 の場合 [B]
15 を 2 ( 最初の素数 )で、割ってみる
割り切れない
15 を 3 ( 2 の次の素数 ) で、割ってみる
割り切れ、商は 5 になる。
3 をプリント
5 を 3 ( 2 の次の素数 ) で、割ってみる
割り切れない
5 を 4 ( 3 の次の自然数 ) で、割ってみる
割り切れない
5 を 5 ( 4 の次の自然数 ) で、割ってみる
割り切れ、商は 1 になる。
5 をプリント
1 は、これ以上、素因数をふくまないので、終わり
結果として、 3 5 が表示される
! 本来なら、小さい方の素数から割るのが正しい(定義通り)
! しかし、小さい方の自然数から割っても、同じ結果がえられる
! 無駄な事をするので時間が余分にかかる
! その代わりに、「分かり易く」なっている
!! この二つの異るやり方が、同じ結果になる事は、
!! 「数学的に証明する」
!! 必要がある(できる)
*/
#include <stdio.h>
#include "s_print.h"
/*
関数の作り方
基本は、プログラムの一部に名前をつける
関数定義
関数の頭部+関数の体部
関数の体部:名前をつけたいプログラムの断片
関数の頭部:関数の名前+関数の型
関数の型:どんな引数をもち、どんな値を返すか
具体例:
二つの整数値を引数にして、その和を値として返す関数
前回の課題
*/
int add ( int a, int b ) {
/*
add ; 関数の名前
add の前の int は、add 返す値の型
今回の場合は、int が指定されているので、整数値を返す
add の後ろの ( int a, int b ) は add の引数の型
引数は、二つあって、共に整数値を指定
*/
return a + b;
/* 関数で名前をつけている部分
「return 式」で、式の値を関数の値として返す
*/
}
int main(void)
{
s_print_int ( add ( 1, 3 ) );
/*
add ( 1, 3 ) で、「1+3=4」が計算され、4 が値
として返るので、画面に、4が表示される
*/
return 0;
}
/*
* 整数の絶対値を計算する関数
*/
int abs ( int x ) {
/* x が非負なら、xの値を、そうでなければ、符号かえた値を返す */
if ( x >= 0 ) { /* x が非負 */
return x; /* x の値をそのまま返す */
} else { /* そうでない場合は、x が負の数なので... */
return x * (-1); /* -1 をかけると、符号がかわる */
/* -x としても同じ意味 */
}
}
/*
* 整数の符号返す
* 正の時は 1
* 負の時は -1
* 零の時は 0
*/
int sign ( int x ) {
if ( x > 0 ) { /* まず、正かどうかを調べる */
return 1; /* 正だったら 1 を返す */
} else { /* 零か、負 */
if ( x < 0 ) { /* 次に、負かどうかを調べる */
return -1; /* 負の場合は -1 を返す */
} else { /* ここにくるのは、零の時のみ */
return 0;
}
}
/* ここにくる事はない */
}
int sign2 ( int x ) {
if ( x > 0 ) {
return 1;
} else if ( x < 0 ) { /* else-if 形式 */
return -1;
} else {
return 0;
}
}
/* else-if の例 */
/* トランプの役札の判定 (絵札 : 1, 10, 11(J), 12(Q), 13(K) */
/* 役札の場合は 1 を、そうでない場合は 0 を返す */
int yakufuda ( int fuda ) {
if ( fuda == 1 ) { /* A なので、役札 */
return 1;
} else if ( fuda == 10 ) { /* 10 なので、役札 */
return 1;
} else if ( fuda == 11 ) { /* 11 なので、役札 */
return 1;
} else if ( fuda == 12 ) { /* 12 なので、役札 */
return 1;
} else if ( fuda == 13 ) { /* 13 なので、役札 */
return 1;
} else { /* 以上で、絵札は尽したので、残るは.. */
return 0; /* 絵札ではない */
}
}
/* 参考 */
int yakufuda2 ( int fuda ) {
if ( fuda == 1 ) { /* A なので、役札 */
return 1;
} else {
if ( fuda == 10 ) { /* 10 なので、役札 */
return 1;
} else {
if ( fuda == 11 ) { /* 11 なので、役札 */
return 1;
} else {
if ( fuda == 12 ) { /* 12 なので、役札 */
return 1;
} else {
if ( fuda == 13 ) { /* 13 なので、役札 */
return 1;
} else { /* 以上で、絵札は尽したので、残るは.. */
return 0; /* 絵札ではない */
}
}
}
}
}
}
#include <stdio.h>
/*
myprintf ( "abc" );
-> 画面に「abc」が出力される
*/
void myprintf ( char *string ) {
if ( *string == '\0' ) {
/* 文字列は全て出力したので、もうやることはない */
} else {
putchar ( *string ); /* 文字列の先頭の一文字を出力 */
myprintf ( string + 1 ); /* 残りは、再帰呼出で.. */
}
}
/*
myprintf ( "abc" );
# myprintf という関数が呼び出されるので、
# その定義が利用される
->
string : "abc"
myprintf ( string )
->
string : "abc"
if ( *string == '\0' ) {
} else {
putchar ( *string );
myprintf ( string + 1 );
}
->
string : "abc" "abc" : { 'a', 'b', 'c', '\0' }
if ( *"abc" == '\0' ) { *"abc" : 'a'
} else {
putchar ( *"abc" );
myprintf ( "abc" + 1 );
}
->
putchar ( *"abc" ); : a が出力
myprintf ( "bc" ); : ここで、もし bc が出力されるなら、上 a と併せて
: 目的の abc 出力ができる事になる
<<ポイント>>
再帰呼出を利用する事によって、
同じ命令を(必要な回数だけ)繰り返す事ができる
例 : 上記の場合は putchar が何度も呼び出される
*/
パターン
X を繰り返す関数 loopX を作るには、
loopX() {
if ( ) { 終了条件
} else {
X;
loopX();
}
}
#include <stdio.h>
#include "s_print.h"
/*
1 から、引数 n までの整数の二乗和を計算する関数
squreSum ( n ) = 1^2 + 2^2 + .. + n^2
考え方
squreSum ( n )
= 1^2 + 2^2 + .. + (n-1)^2 + n^2
= (1^2 + 2^2 + .. + (n-1)^2 ) + n^2
= squreSum ( n-1 ) + n^2
再帰を利用する(足し算の「繰返し」なので、「再帰」)
後は、終了条件 ( n が 0 の時は、直接計算できる )
squreSum ( 0 ) = 0
関数の型
引数として、一つの整数値 (n)をとり、結果として整数値を返す
*/
int squreSum ( int n ) {
if ( n <= 0 ) {
return 0;
}else{
return squreSum ( n - 1 ) + (n*n);
}
}
int main(void) {
s_print_int ( squreSum ( 5 ) ); /* 5^2 + 4^2 + 3^2 + 2^2 + 1^2 = 55 ? */
return 0;
}
#include <stdio.h>
/*
入力ループ
すこしずつ入力をしながら処理をして、すこしずつ出力を作る
入力 -(データ)--------> *
|
人間 処理(計算) プログラム
v
出力 <-(データ)--------- *
一つ入力、一つ処理、一つ出力
上記のパターンを連続的に、繰り返したい
「入力された一行(改行まで)文字列の小文字を全て大文字する」
プログラムを、
文字の入力、処理、出力の繰返し
で実現したい。
*/
void toUpperLoop ( char ch ) {
if ( ch == '@' ) { /* 改行だったら */
/* 一行分処理し終ったので、その改行を出力して、終わり */
putchar ( ch );
} else { /* 小文字 ( 'a' <~ ch <= 'z' ) を大文字にして出力する作業必要 */
if ( ch < 'a' ) { /* 小文字でない */
putchar ( ch ); /* そのまま出す */
} else if ( 'z' < ch ) { /* 小文字でない */
putchar ( ch ); /* そのまま出す */
} else { /* ここにくるのは、 'a' <= ch <= 'z' なので、小文字 */
putchar ( ch - 'a' + 'A' );
}
toUpperLoop ( getchar() ); /* 次文字を読みだして、再帰 */
}
}
int main(void){
toUpperLoop ( getchar() ); /* 次文字を読みだ(先読み)して、関数を呼ぶ */
return 0;
}
/*
inputLoop ( char ch ) {
if ( ) {
} else {
inputLoop ( getchar() );
}
}
int main(void) {
inputLoop ( getchar() );
}
*/
#include <stdio.h>
#include <stdlib.h> /* rand 関数を使うために.. */
#include "s_print.h"
int main ( void ) {
s_print_string ( "1 回数目 : " );
s_print_int ( rand() ); /* rand() は乱数関数 : 呼び出す度に、異る乱数値を返す */
s_print_newline();
s_print_string ( "2 回数目 : " );
s_print_int ( rand() );
s_print_newline();
s_print_string ( "3 回数目 : " );
s_print_int ( rand() );
s_print_newline();
return 0;
}
#include <stdio.h>
#include <stdlib.h> /* rand 関数を使うために.. */
#include "s_print.h"
void printRadomNumberLoop ( char ch ) {
if ( ch == '@' ) {
printf ( "終了\n" );
} else {
s_print_int ( rand() );
s_print_newline();
printRadomNumberLoop ( getchar() );
}
}
int main ( void ) {
printRadomNumberLoop ( getchar() );
return 0;
}
/*
* CDATE FILENAME
*
* 数当てを行う
*/
#include <stdio.h>
#include "s_print.h" /* s_print_int を利用するので.. */
#include "s_input.h" /* s_input_int を利用するので.. */
/*
* prompt
*/
int prompt(void) {
s_print_string ( "私が選んだ数を予想して入力してください : " );
return s_input_int(); /* キーボードから整数値を一つ読み込む(先読み) */
}
/*
*/
void game ( int input, int answer ) {
if ( input == answer ) {
s_print_string ( "お見事です。あたりました。\n" );
} else {
if ( input > answer ) {
/* ここを完成しなさい */
} else {
s_print_string ( "その数は小さすぎます。\n" );
}
game( prompt(), answer );
}
}
/*
* main
*/
int main( void )
{
s_print_string ( "私が考えた 1 〜 100 数を予想して当ててみてください。\n" );
game ( prompt(), (rand()%100) + 1 );
return 0;
}
/*
方針
問題は、乱数で生成する -> rand 関数を利用する
(rand()%100) + 1
rand() : 0 〜 3 万まで乱数
(rand()%100) : 0 〜 99 まで乱数
(rand()%100)+1 : 1 〜 100 まで乱数
人間に答を予想してもらって、その予想した整数値を入力し、
その結果を出力する
あたるまで、繰返し ( インプットループ )
ヒントとしては、大小関係を教える
*/
123ABCxyz$$$ 123ABCxyz$$$ 123ABCxyz$$$ 123ABCxyz$$$ @
else 節の内容が空っぽの時は、「else {} 」となるが、その場合は、省略可能
if ( x > 0 ) {
printf ( "正\n" );
} else {
/* 空っぽ */
}
の場合は、
if ( x > 0 ) {
printf ( "正\n" );
}
だけで良い。
<<次回>>
srand の話をする
123ABCXYZ$$$ 123ABCXYZ$$$ 123ABCXYZ$$$ 123ABCXYZ$$$ @
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20150612-01.c ( SJIS 版 )
/*
* CDATE FILENAME
*
* 数当てを行う
*/
#include <stdio.h>
#include "s_print.h" /* s_print_int を利用するので.. */
#include "s_input.h" /* s_input_int を利用するので.. */
/*
* prompt
* メッセージを出力し、キーボードから整数値を読み込んで、
* その値を返す関数
*/
int prompt(void) {
s_print_string ( "私が選んだ数を予想して入力してください : " );
return s_input_int();
}
/*
*/
void game ( int input, int answer ) {
if ( input == answer ) { /* 入力と答えが一致した */
s_print_string ( "お見事です。あたりました。\n" );
} else {
if ( input > answer ) { /* 入力が、答えより大きい */
/*
** この部分を完成させなさい
*/
} else { /* 入力が、答えより小さい */
s_print_string ( "その数は小さすぎます。\n" );
}
/* 未だ、答えが、当っていないので、ゲームを続ける.. */
/*
** この部分を完成させなさい
*/
}
}
/*
* main
*/
int main( void )
{
s_print_string ( "私が考えた 1 〜 100 数を予想して当ててみてください。\n" );
game ( prompt(), (rand()%100) + 1 );
return 0;
}
50 75 83 90 85 84
$ ./20150612-01-QQQQ.exe 私が考えた 1 〜 100 数を予想して当ててみてください。 私が選んだ数を予想して入力してください : 50 その数は小さすぎます。 私が選んだ数を予想して入力してください : 75 その数は小さすぎます。 私が選んだ数を予想して入力してください : 83 その数は小さすぎます。 私が選んだ数を予想して入力してください : 90 その数は大きすぎます。 私が選んだ数を予想して入力してください : 85 その数は大きすぎます。 私が選んだ数を予想して入力してください : 84 お見事です。あたりました。 $
Download : 20150612-02.c ( SJIS 版 )
/*
* CDATE FILENAME
*
* 与えられた自然数の素因数を出力する
*
* 12 は..
*
*
*
*
*
*
*/
#include <stdio.h>
#include "s_print.h" /* s_print_int を利用するので.. */
/*
* void print_prime_factor_sequence ( int n, int p )
* 機能 : p 以上の n の素因数を小さい順に並べで表示する
* 条件 : n には p 未満の素因数を含まれていないものとする
* int n : p 未満の素因数を含まれていない自然数
* 返り値 : なし
*/
void print_prime_factor_sequence ( int n, int p ) {
if ( p > n ) { /* もう n には p 以上の素因数を含まない */
/* 何もしなくてよい */
/* 本来、ここにくるのは n = 1 の時だが、念の為 */
} else if ( n % p == 0 ) { /* n が p を素因数として含む */
s_print_char ( ' ' ); /* 隙間を空け */
s_print_int ( p ); /* 素因数を表示し */
/* もう一度 p の要素があるかもしれないので、そこから試す [再帰] */
/*
** この部分を完成させなさい
*/
} else { /* n が p を素因数として含まない */
/* 本来は p の次の素数を試すべきだが.. */
/* その代りに p + 1 を試す (のは無駄だが、正く動く) [再帰] */
/*
** この部分を完成させなさい
*/
}
}
/*
* void print_prime_factor_of ( int n )
* 機能 : n の素因数を出力する
* int n : 素因数を表示したい自然数
* 返り値 : なし
*/
void print_prime_factor_of ( int n ) {
if ( n < 1 ) { /* 与えらた数が自然数ではない */
s_print_int ( n );
s_print_string ( "は、自然数ではありません\n" );
} else if ( n == 1 ) { /* 与えられた数が 1 の場合 */
s_print_int ( n );
s_print_string ( "の素因数はありません\n" );
} else {
s_print_int ( n );
s_print_string ( "の素因数分解は" );
/*
** この部分を完成させなさい
*/
s_print_string ( " となります。\n" );
}
}
/*
* main
*/
int main( void )
{
print_prime_factor_of ( 12 );
return 0;
}
abc123
$ ./20150612-02-QQQQ.exe 12の素因数分解は 2 2 3 となります。 $