- ベストアンサー
C言語のデータ処理プログラムの作成方法
- C言語を使用してデータの書いてあるファイルを読み込んで処理するプログラムを作成したいです。
- データは0.5ずつに区切られており、各範囲内に含まれるデータの個数を数えたいです。
- 現在のプログラムではうまく動作せず、どのように場合分けをするかが分からないです。
- みんなの回答 (10)
- 専門家の回答
質問者が選んだベストアンサー
あ、だから。 ・データは昇順に並んでいる ・しかし上限は不明 ということなのでしょう。 こんな感じ。 #include <stdio.h> int main() { FILE *fp = fopen("1.dat", "r"); double d; int count = 0; double base = 0; while(fscanf(fp,"%lf\n",&d)!=EOF) { if (d < base + 0.5) { count++; } else { // 次のレンジに突入した printf("%f to %f -> %d\n", base, base + 0.5, count); // 次のカウントをするのに、今読み込んで、そのままカウントしていないデータのつじつまを合わせる必要がある。 while(base + 0.5 <= d) base += 0.5; // base を読み込んだデータの下限に合わせておく // base + 0.5 > d のときにループから抜ける。つまり、d < base + 0.5 を満たす状況で抜ける count = 1; // このとき、読み込んだデータをカウントする必要があるので、+1(つまり1) } } // 最後のレンジのものは出力されていない // 忘れずに printf("%f to %f -> %d\n", base, base + 0.5, count); fclose(fp); return 0; }
その他の回答 (9)
- 麻野 なぎ(@AsanoNagi)
- ベストアンサー率45% (763/1670)
もうひとつおまけ。 実は、条件判断をするときに、どちらの条件の処理を最初にするかというのも、処理の見通しに影響を与えることがあります。 No.8, No.9 のソースは 1) 今のレンジに入っていれば、カウンタをインクリメント 2) 今のレンジに入ってなければ、レンジを調整する という処理でした。 この流れだと、2) に来たときの、その時のデータ分がカウントアップできないので、面倒な流れになっています。 これを、 1) 今のレンジに入ってなければ、レンジを調整して入るようにする。 2) ここの段階では、レンジに入っているので単純にカウンタをインクリメント。 という流れにすると、見通しはよくなります。 #include <stdio.h> int main() { FILE *fp = fopen("1.dat", "r"); double d; int count = 0; double base = 0; while(fscanf(fp, "%lf\n", &d) !=EOF) { while(! (d < base + 0.5)) { // 今想定しているレンジには入っていない → レンジの調整 if (count != 0) printf("%f to %f -> %d\n", base, base + 0.5, count); // 今のレンジのデーがあれば、表示して base += 0.5; // base を次のレンジに移して、 count = 0; // カウンタをクリア } // それを、レンジのつじつまがあうまで調整 // ここに来たときには、今のレンジに合致している count++; } // 最後のレンジのものは出力されていない // 忘れずに printf("%f to %f -> %d\n", base, base + 0.5, count); fclose(fp); return 0; }
- 麻野 なぎ(@AsanoNagi)
- ベストアンサー率45% (763/1670)
No.8 おまけです。 No.8 のプログラムが、かなり面倒に見えるのは、 ・今見ているレンジなら、カウントをインクリメント ・次のレンジに突入したなら、カウントをクリアして、やりなおし というロジックが案外作りにくいからです。 で、こういう場合に、goto が案外有効だったりします。 #include <stdio.h> int main() { FILE *fp = fopen("1.dat", "r"); double d; int count = 0; double base = 0; while(fscanf(fp,"%lf\n",&d)!=EOF) { retry: if (d < base + 0.5) { count++; } else { // 次のレンジに突入した if (count != 0) printf("%f to %f -> %d\n", base, base + 0.5, count); base += 0.5; count = 0; goto retry; // レンジを設定し直して、もう一度 } } // 最後のレンジのものは出力されていない // 忘れずに printf("%f to %f -> %d\n", base, base + 0.5, count); fclose(fp); return 0; }
- jjk65536
- ベストアンサー率59% (66/111)
fscanfのループ中で随時個数も表示していくのは案外 考慮しないといけないことが多くて面倒です。 シンプルに集計と結果表示を分けてみてはどうでしょう。 試しに書いてみた例をしめします。 #include <stdio.h> #define MAX 16 int main(int argc, const char *argv[]) { double n; int cnts[MAX] = {0}; int i; FILE *fp = fopen("1.dat", "r"); if(!fp) exit(1); // 集計 while(fscanf(fp, "%lf\n", &n)!=EOF){ int intn = (int)(n/0.5); cnts[intn]++; } // 集計結果を表示 for(i=0; i<MAX; i++) printf("%.1f ~ %.1f の範囲のデータは %d 個\n", (double)i/2., (double)i/2.+0.5, cnts[i]); return 0; } 実行結果 $ ./oshiete2 0.0 ~ 0.5 の範囲のデータは 2 個 0.5 ~ 1.0 の範囲のデータは 0 個 1.0 ~ 1.5 の範囲のデータは 1 個 1.5 ~ 2.0 の範囲のデータは 1 個 2.0 ~ 2.5 の範囲のデータは 1 個 2.5 ~ 3.0 の範囲のデータは 0 個 3.0 ~ 3.5 の範囲のデータは 0 個 3.5 ~ 4.0 の範囲のデータは 2 個 4.0 ~ 4.5 の範囲のデータは 0 個 4.5 ~ 5.0 の範囲のデータは 0 個 5.0 ~ 5.5 の範囲のデータは 1 個 5.5 ~ 6.0 の範囲のデータは 2 個 6.0 ~ 6.5 の範囲のデータは 2 個 6.5 ~ 7.0 の範囲のデータは 1 個 7.0 ~ 7.5 の範囲のデータは 1 個 7.5 ~ 8.0 の範囲のデータは 0 個 全角スペースは半角に置換してください。 0~0.5をcnts[0]に、0.5~1.0をcnts[1]に、と集計するプログラムです。 MAXの値を大きくすることで大きな値にも対応できますが、 環境によっては大きすぎる場合はmallocなどに置き換える必要があります。 メリットとして、ソートされていないデータソースでも正しく動作します。 デメリットとして、cnts配列の分余計にメモリを食います。 ま、考え方は一つではないよということで。
- kapurina
- ベストアンサー率22% (4/18)
質問者様のアルゴリズムがイマイチ伝わらないのですが・・・ 回答を書いている途中で気付きましたが、これってデータは必ず小さい順に並んでる事が絶対条件でしょうか? それであれば、下記の点を考え直してみてください。 ・cntとCntの2個で数えようとしてますが、2個で実現させるのでしょうか? あまりお勧めではないですが、2個で実現する為には1の位が変化するタイミングで「cntとCnt」は0にクリア必要ではありませんか? ・条件文のelseに入ってi++を実行した時の動作は問題無いですか? cntかCntを増やさずに次のデータに進んでしまったら、正しい結果は出ないですよね? 確認してみてください。 さらっとしか見てないので簡単にですが、上記確認してみて下さい。 もし、補足頂けるのであれば ・質問者様のアルゴリズム ・ソースにコメント の記載を希望します。
- kmee
- ベストアンサー率55% (1857/3366)
プログラミングと数学とでは違う、ということを覚えておいてください i <= n < (i+0.5) 数学では「i以上i+0.5未満のn」というのをこういう表現しますが、プログラム言語でこう書いて、数学と同じ意味になるものの方が少数派です。 C言語では (i<=n)<(i+0.5) と解釈され 「i <= n の結果が i+0.5 より小さいなら真」 という意味になります。<=のような比較演算子は、関係が成立していたら真を表す1、不成立なら偽を表す0になります。なので、この式は (i <= n でないなら) 0 < i+0.5 → 成立してたら1,不成立なら0 (i <= n でなら) 1 < i+0.5 → 成立してたら1,不成立なら0 のいずれか、ということになります。 C言語としては「正常」なので、エラーにはなりません。しかし、あなたの期待する計算ではありません。 数学の「i以上i+0.5未満のn」は「nがi以上、かつ、nがi+0.5未満」のことです。 Cでは、後者で表現する必要があります。
補足
ありがとうございます。 (0.5*i)<=n && n<0.5*(i+1)にしてみます。
- koi1234
- ベストアンサー率53% (1866/3459)
if文で振り分けなんかしてたらとんでもないことになります あくまで一つの方法ですが 得られた値を0.5で割って整数値を求めれば区切り位置を得ることができますよね 更にデータを読み込んでその都度どの区切り位置のデータかを表示するのではなくて 各区切りの総計データ数を表示したいのですよね? そうであるなら問題はデータは1万5千個以上あるデータで 最大値がいくつなのかということになります (各区切り毎の累計データ数を保持する分のデータ領域が必要になるということです) >データの最後の値は15454.629167 最後の値はどうでもいいですがもしこれがデータ中で最大の値 ということなら 15454.6 ÷ 0.5 で 30909 となり 総計データを保持するために 30910個のデータ領域が必要になる ということになります そのあたりを考えてプログラムすればいいんではないでしょうか (書いてあるプログラムのCnt/cnt2個の変数だけで実現できるわけがない) 中間データをファイルに書きつつ処理していくとか考えれば 集計データ分の配列データを持たずに作成することもできるでしょうが そういったことは基本ロジック作ってからの話でいいでしょう ロジックまでは書きませんのでご自分で考えてください (そのままけけばいいだけなので考えるほどのロジックではない)
- 麻野 なぎ(@AsanoNagi)
- ベストアンサー率45% (763/1670)
もう一つ追加。 このロジックだと、「条件に合致するときに(カウントアップは良いとして)出力」じゃなくて、「条件に合致しなかったとき(=次のレンジに突入した時)に出力」だから。
- 麻野 なぎ(@AsanoNagi)
- ベストアンサー率45% (763/1670)
確か、「データは必ず昇順に並んでいる」という条件がありませんでしたか? その条件がないと、そもそもとんでもないロジックになっていますから。 とりあえず、if() の条件の書き方が間違っています。 あと、比較するのに使っている i を、(0.5ずつではなく)1ずつ増やしているのはなぜでしょう? もしも、0.5ずつ足していくと誤差が出てくるから、というのを聞いたのなら、(double の変数に)0.5ずつ足していっても、誤差は出ませんというのは、覚えておくとちょっと役に立つかも。 データの条件を考えれば、 0.5未満なら…… 1.0未満なら…… 1.5未満なら…… と考えていくと、もしかしたら、シンプルに行くかもしれません。 あと、printf() で、double を出力するには、"%f" でいいです。 fscanf() で、double を読み込むのは、"%lf" ですけど。
補足
忘れていました。データは昇順です。 >>あと、比較するのに使っている i を、(0.5ずつではなく)1ずつ増やしているのはなぜでしょう? (0.5*i)<=n<0.5*(i+1)にしてみましたがうまくいきませんでした。 %f了解しました。ありがとうございます。
- Tacosan
- ベストアンサー率23% (3656/15482)
なぜ「うまくいかなかった」のか, 原因は理解できていますか? あと, 確認しておきたいんだけど値の範囲はあらかじめ仮定しておいていい?
補足
>>なぜ「うまくいかなかった」のか, 原因は理解できていますか? 完璧には理解できていません。 場合分けの仕方がだめなのだとは思っているのですが。 >>確認しておきたいんだけど値の範囲はあらかじめ仮定しておいていい? 勉強不足でおっしゃっている意味があまり理解できないのです。 申し訳ありません。for文を使うためにということでしょうか? ちなみに僕が読み込んでいるデータの最後の値は15454.629167です。
補足
ありがとうございます。 0個の範囲も”0”と出したいのですがこの方法だと”0”は出ませんよね?