質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Q&A

解決済

8回答

6129閲覧

どちらがより良い配列の最大値を求めるアルゴリズムでしょうか?

退会済みユーザー

退会済みユーザー

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

0グッド

0クリップ

投稿2016/10/11 23:29

編集2016/10/12 03:40

###はじめに
配列の中の最大値を求めるアルゴリズムについて質問します.
そのアルゴリズムについて友人と議論になり,客観的意見が欲しくて質問しました.

###サンプルコード

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <stdbool.h> 4#include <time.h> 5#include <limits.h> 6 7bool putMaxVal1(int array[],int size){ 8 if(size<=0){ return false; } 9 int maxVal=array[0]; 10 int i; 11 for(i=1; i<size; i++){ 12 if(array[i]>maxVal) maxVal=array[i]; 13 } 14 printf("maxVal1 = %d\n",maxVal); 15 return true; 16} 17 18bool putMaxVal2(int array[],int size){ 19 if(size<=0){ return false; } 20 int maxVal=INT_MIN ; 21 int i; 22 for(i=0; i<size; i++){ 23 if(array[i]>maxVal) maxVal=array[i]; 24 } 25 printf("maxVal2 = %d\n",maxVal); 26 return true; 27} 28 29int main(void){ 30 int array[100]={0}; 31 32 srand(time(NULL)); 33 int i; 34 for(i=0; i<sizeof array/sizeof array[0]; i++){ 35 array[i]=1+rand()%1000; 36 printf("[%3d] %3d\n",i+1,array[i]); 37 } 38 printf("maxVal1:%s\n",putMaxVal1(array,sizeof array/sizeof array[0])?"TRUE":"FALSE"); 39 printf("maxVal2:%s\n",putMaxVal2(array,sizeof array/sizeof array[0])?"TRUE":"FALSE"); 40 41 printf("maxVal1:%s\n",putMaxVal1(array,-1)?"TRUE":"FALSE"); 42 printf("maxVal2:%s\n",putMaxVal2(array,-1)?"TRUE":"FALSE"); 43 44 return 0; 45}

###質問
0. 二つの関数(maxVal1 と maxVal2)のどちらのうがいいでしょうか.

質問内容を変更す.

「どちらを推奨しますか?」「どちらのほうが個人的に好きですか?」

2.引数sizeに-1を入れたところ二つの関数には以下のような違いが見られましたが,これは処理系によって違うのでしょうか.

maxVal1 = 984 maxVal2 = -2147483648

###補足など
質問について:
「いいでしょうか」という質問が不適切であるという指摘をいただいた為,「推奨しますか?」,「どちらのコードのほうが好きですか?」という質問に変更します.

コードについて:コードを修正しました.修正前コードは以下の通りです.

C

1 2/*このコードは修正前のものです.*/ 3/*このコードは修正前のものです.*/ 4/*このコードは修正前のものです.*/ 5/*このコードは修正前のものです.*/ 6 7#include <stdio.h> 8#include <stdlib.h> 9#include <time.h> 10#include <limits.h> 11 12int maxVal1(int array[],int size){ 13 int maxVal; 14 int i; 15 for(i=0; i<size; i++){ 16 if(0==i) maxVal=array[0]; 17 if(array[i]>maxVal) maxVal=array[i]; 18 } 19 printf("maxVal1 = %d\n",maxVal); 20 return maxVal; 21} 22 23int maxVal2(int array[],int size){ 24 int maxVal=INT_MIN ; 25 int i; 26 for(i=0; i<size; i++){ 27 if(array[i]>maxVal) maxVal=array[i]; 28 } 29 printf("maxVal2 = %d\n",maxVal); 30 return maxVal; 31} 32 33int main(void){ 34 int array[100]={0}; 35 36 srand(time(NULL)); 37 int i; 38 for(i=0; i<sizeof array/sizeof array[0]; i++){ 39 array[i]=1+rand()%1000; 40 printf("[%3d] %3d\n",i+1,array[i]); 41 } 42 maxVal1(array,sizeof array/sizeof array[0]); 43 maxVal2(array,sizeof array/sizeof array[0]); 44 45 return 0; 46}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答8

0

質問1ですが、整数や浮動小数点数のように最小値がわかりきっているものについては、それを入れておくことで判定条件を簡略化する、番兵と呼ばれる技があります。

ただ、自分だったら、まずmax = array[0];として、ループを1から回す、という手段を取ると思います。

質問2ですが、どちらもループは1度も実行されません。maxVal2では最初に代入したINT_MINがそのまま帰りますが、maxVal1では初期化していない変数をそのまま返そうとして、厳密には「未定義の動作」となります(多くの処理系では、もとからメモリに入っていた何かしらの値が返ってくることになるでしょう)。

投稿2016/10/11 23:38

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

質問2については、maisumakunさんの回答が申し分ないので、思ったところがある質問1についてのみ書きます。

二つの関数(maxVal1 と maxVal2)のどちらのほうがいいでしょうか.

とのことですが、「よい」とはどういうことでしょうか。コードの分かりやすさですか? 実行速度ですか? 想定外の入力への安全性ですか? …
というように、「よい」と言っても、様々な基準があるので、単純には答えられないのが事実です。ただ、それだけでは何のアドバイスにもならないので、個人的にこれらの視点からどう評価するか、その理由を説明します。

分かりやすさ:ほぼ同じ
1.のほうは、0を特別扱いしていますが、理由は自明なので特に分かりにくいとは思いません。
速度:1.<=2.
最近のコンパイラなら、ループに関しては最適化で全く同じコードになる可能性が高いですが、1.は0との比較が挟まりうる分だけ、遅くなる可能性もあります。
安全性:1.<2.
不適切な入力時に、2.であれば決まった値が帰るのでエラーチェックにも使えますが、1.の場合は全く分かりません。
拡張性:1.>2.
今回は最小値があるint型でしたが、最小値が存在しない文字列型には2.のロジックは使えないですよね?

とまあ、こんな感じです。個人的には2.を推します。

投稿2016/10/12 00:32

majiponi

総合スコア1720

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

majiponi

2016/10/12 15:40

修正後の質問への回答としては、どのようなデータ型にも用いることができる1のほうを勧めたいです。他の条件が同じなので。
guest

0

アルゴリズムというよりかは、変数(maxVal)の初期化はどのタイミングでやるのが良いかという感じですね。
どちらかと言えば2の方が好みです。
ループの外に書けるコードはなるべくループから出した方が、そのループの処理で何をしたいのかが明確になります。
質問2は、maisumakunさんの回答通りですね。
どちらの処理もループを回らないので、1の方は変数を初期化していないので不定値、2の方はINT_MINが返ります。
それからこの質問の場合、引数sizeが0以下の場合の仕様が曖昧なので、そこがきっちり決まっていればまた違うコードになるのではないでしょうか。

投稿2016/10/12 00:23

編集2016/10/12 02:28
ttyp03

総合スコア16998

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2016/10/12 02:16

「自動記憶域期間のオブジェクトの値が不定の時に使用された場合」、不定の値が返るだけではすまないかもしれません(いわゆる「未定義の動作」です)。 https://www.jpcert.or.jp/sc-rules/c-exp33-c.html 最適化をかけた場合、「未定義な動作」をしたコードパスについて「そこは通らない」とみなして最適化してしまってもかまわないので、とんでもない動作になることがあります。
ttyp03

2016/10/12 02:28

最適化の例は知らなかったので助かります。 でもまあ、対初心者なので小難しい話よりかは、想定していない値が返るコードは良くないよ、程度の話でいいと思いますけどね。
guest

0

ベストアンサー

私だったら、こう書きます。

C

1// #define NDEBUG 2#include <assert.h> 3#include <stdio.h> 4#include <stdlib.h> 5#include <time.h> 6 7// Cはスネークケース、C++はキャメルケースというオレオレコーディングスタイルに 8// 基づき関数名はスネークケースにする。 9// 配列のサイズは size_t を使う。符号無しなので負の数にはならない。 10int max_val(int *array, size_t size) 11{ 12 // 範囲チェックは assert() を使う。 13 // リリース版は NDEBUG を有効にして、チェック分をなくし、高速にする。 14 assert(size > 0); 15 // 最初の値を max に入れる。 16 int max = *array; 17 // size - 1 回分ループする。 18 // 余計なインデックスの代入や比較が無い分速い。 19 while (--size) { 20 // 前置インクリメントした後にチェックしていく。 21 // array[i] よりもインクリメントの方が速い。 22 if (*++array > max) 23 max = *array; 24 } 25 return max; 26} 27 28int main(void) 29{ 30 int array[100] = {0}; 31 32 srand(time(NULL)); 33 for (int i = 0; i < sizeof(array) / sizeof(*array); i++) { 34 array[i] = 1 + rand() % 1000; 35 printf("[%3d] %3d\n", i + 1, array[i]); 36 } 37 size_t size = sizeof(array) / sizeof(*array); 38 printf("max_val:%d\n", max_val(array, size)); 39 printf("max_val:%d\n", max_val(array, 0)); 40 41 return 0; 42}

「良いコード」というのは、基準がたくさんあるため、一概には言えません。上のコードはなるべくは速く、また、簡潔でわかりやすくしたつもりですが、人によっては「while(--size)の意味がわかりにくい、forループの方がいい」とか「最初の値だけ特別視しない方が良い」とか「インデントせずに添字アクセスの方が直感的」とか「assert()無効でsize0の時、デクリメントがアンダフローを起こすのはよくない」とか言う場合もあるでしょう。また、アルゴリズムさえ間違っていなければ、コンパイラの最適化によってほぼ同じになってしまう場合もあります。

明らかにアルゴリズムが間違っているというのでなければ、好みの使い方をするのが良いと思います。

【forやwhileが嫌いな人のためのおまけ】

C

1// #define NDEBUG 2#include <assert.h> 3#include <stdio.h> 4#include <stdlib.h> 5#include <time.h> 6 7int max_val_r(int *array, size_t size, int max) 8{ 9 if (!size) 10 return max; 11 if (*array > max) 12 max = *array; 13 return max_val_r(++array, --size, max); 14} 15 16int max_val(int *array, size_t size) 17{ 18 assert(size > 0); 19 int max = *array; 20 return max_val_r(++array, --size, max); 21} 22 23int main(void) 24{ 25 int array[100] = {0}; 26 27 srand(time(NULL)); 28 int i; 29 for (i = 0; i < sizeof array / sizeof array[0]; i++) { 30 array[i] = 1 + rand() % 1000; 31 printf("[%3d] %3d\n", i + 1, array[i]); 32 } 33 size_t size = sizeof(array) / sizeof(*array); 34 printf("max_val:%d\n", max_val(array, size)); 35 printf("max_val:%d\n", max_val(array, 0)); 36 37 return 0; 38}

関数型に毒されているとこっちの方がわかりやすいという人もいるとかいないとか。

投稿2016/10/12 12:09

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

僕だったら"最大値"ではなく"最大値なのは配列の何番目か"を返すかも。
そうすれば size <= 0 のときにたとえば-1を返すことで"該当なし"を表現できる。

さもなくば"size <= 0 のときの戻り値は不定(or INT_MIN)" と明記する。

投稿2016/10/12 01:57

編集2016/10/12 05:20
episteme

総合スコア16614

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

「配列の最大値を求める」のが目的ならブーリアンで帰ってくるのはいただけません。
そういう意味では修正前の方が良いです。どうしてもブーリアンで返したいならMaxValを返せるようにポインタの引数を追加した方が良いでしょう。

投稿2016/10/13 01:15

WoodenHamlet

総合スコア306

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fuzzball

2016/10/13 01:51 編集

「配列の最大値を求める関数」とは書いていないので問題ないかと。
退会済みユーザー

退会済みユーザー

2016/10/13 10:28

戻り値が最大値ではないので,関数名を「put・・・」という形にしました.
guest

0

コード修正前

良い悪いの前に、maxVal1はsize <= 0のときにバグるのでダメ。

コード修正後

1かな。
カッコイイのは2だけど、無駄なコードがあるのは気持ち悪いです。

投稿2016/10/12 01:05

編集2016/10/13 01:50
fuzzball

総合スコア16731

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

maxVal1のほうは、1回だけでいい初期化の処理判定をループごとに行っているため、若干効率が悪いです(とは言え優位な差はないでしょうが)。maxVal2の様に最小値で初期化するか、ループに入る前に0番の要素(あれば)で初期化するほうが好みです。

投稿2016/10/12 00:30

swordone

総合スコア20651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問