プログラム実行の手間を省きたいです

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
とり

プログラム実行の手間を省きたいです

#1

投稿記事 by とり » 8年前

はじめまして 恥を忍んで投稿させていただきます・・・
大学生です
相関係数と呼ばれるものを計算するプログラムを作りました。
しかし、このプログラムになんどもファイルを通して計算したいのですが自分の力ではいまいち解決方法が
みあたらないのとプログラム能力が低くて困っています

やりたいこと
シェルスクリプト?を使えば処理を早くできるみたいですがいまいちわからなくてCプログラムの中でうまくできないかと
./soukan data1.txt data2.txt
./soukan data1.txt data3.txt
・・・・
とやっていくので今のプログラムだと大変過ぎまして・・・

困っている点
コードの赤文字の所を書き換えるとXの総和計算がおかしくなってしまいます・・・
どのように間違っているのか教えていただけたらと・・・

現プログラムであれば
./soukanで実行し
ファイルを入力という手間が激しく、data1と入力するのもかなりの手間です・・・・
総当たりの組み合わせでファイルがたくさんあるのでとても手動では・・・

dataファイルの中身は
1
2
2
1
のように数字が改行されて入っているファイルが100個ほどあります

ファイル1の配列の中身をx[n]にファイル2の中身をy[n]に入れたいのですけれどいまいちうまく生かしきれている気がしません・・・

環境 linux
言語はcしか使えません。。。

ファイルのソースコードです

コード:

//相関係数を求めるファイル名片一方固定
//gcc -o soukan soukan.c -lm


#include<stdio.h>
#include<math.h>
#include<stdlib.h>

int main(void){

  char filename[100];
  char filename2[100];
  FILE *fp;
  
  int i,n,m,t;
  int *ip;
  double sumx,avex,bunux,bunx,kyoubunx;
  double sumy,avey,bunuy,buny,kyoubuny;
  double kyoubunxy,soukan;
  double x[n];
  double y[n];
  char s[100];
  float f1,f2;

  printf("ファイル名を入力してください");
  scanf("%s",filename);

  fp=fopen(filename,"r");
  if(fp==NULL){
    printf("ファイルがありません\n");
    return 0;
  }
  
  for(n=0;n<20;n++){
    fscanf(fp,"%lf",&(x[n]));
  }
  
  fclose(fp);


 [color=#FF0000] //このコメント3行と下の一行を変えるとXの総和の計算がおかしくなってしまいます・・・  [/color]
  //printf("2つめのファイル名を入力してください");
  //scanf("%s",filename2);
  //fp=fopen(filename2,"r");
  
  fp=fopen("test2.txt","r");
  if(fp==NULL){
    printf("ファイルがありません\n");
    return 0;
  }
  
  for(n=0;n<20;n++){
    fscanf(fp,"%lf",&(y[n]));
  }
  
  fclose(fp);

  sumx=0.0;//Xの総和をもとめる
  for(i=0;i<n;i++){
  sumx+=x[i];
  }
  
  avex=0.0;//Xの平均をもとめる
  avex=sumx/i;

  bunx=0.0;//Xの分散をもとめる
  bunux=0.0;
  for(i=0;i<n;i++){
    bunux+=(x[i]-avex)*(x[i]-avex);
  }
  bunx=bunux/i;

  sumy=0.0;//Yの総和をもとめる
  for(i=0;i<n;i++){
    sumy+=y[i];
  }
  avey=0.0;//Yの平均をもとめる
  avey=sumy/i;

  buny=0.0;//Yの分散をもとめる
  bunuy=0.0;
  for(i=0;i<n;i++){
    bunuy+=(y[i]-avey)*(y[i]-avey);
  }

  buny=bunuy/i;

  kyoubunxy=0.0; //共分散を求める
    for(i=0;i<n;i++){
      kyoubunxy+=(x[i]-avex)*(y[i]-avex);
  }
  kyoubunxy=kyoubunxy/i;

  soukan=0.0;//相関係数をもとめる
  soukan=kyoubunxy/(sqrt(bunx)*sqrt(buny));
  
  printf("X合計=%f\n",sumx);
  printf("X平均=%f\n",avex);
  printf("X分散=%f\n",sqrt(bunx));//根号をとった
  printf("Y合計=%f\n",sumy);
  printf("Y平均=%f\n",avey);
  printf("Y分散=%f\n",sqrt(buny));//根号をとった
  printf("共分散=%f\n",kyoubunxy);
  printf("相関係数=%f\n",soukan);

  return 0;
}

とり

Re: プログラム実行の手間を省きたいです

#2

投稿記事 by とり » 8年前

41行目以降赤文字になってしまいました・・・

コード:

//相関係数を求めるファイル名片一方固定
//gcc -o soukan soukan.c -lm


#include<stdio.h>
#include<math.h>
#include<stdlib.h>

int main(void){

  char filename[100];
  char filename2[100];
  FILE *fp;
  
  int i,n,m,t;
  int *ip;
  double sumx,avex,bunux,bunx,kyoubunx;
  double sumy,avey,bunuy,buny,kyoubuny;
  double kyoubunxy,soukan;
  double x[n];
  double y[n];
  char s[100];
  float f1,f2;

  printf("ファイル名を入力してください");
  scanf("%s",filename);

  fp=fopen(filename,"r");
  if(fp==NULL){
    printf("ファイルがありません\n");
    return 0;
  }
  
  for(n=0;n<20;n++){
    fscanf(fp,"%lf",&(x[n]));
  }
  
  fclose(fp);


  //このコメント3行と下の一行を変えるとXの総和の計算がおかしくなってしまいます・・・  
  //printf("2つめのファイル名を入力してください");
  //scanf("%s",filename2);
  //fp=fopen(filename2,"r");
  
  fp=fopen("test2.txt","r");
  if(fp==NULL){
    printf("ファイルがありません\n");
    return 0;
  }
  
  for(n=0;n<20;n++){
    fscanf(fp,"%lf",&(y[n]));
  }
  
  fclose(fp);

  sumx=0.0;//Xの総和をもとめる
  for(i=0;i<n;i++){
  sumx+=x[i];
  }
  
  avex=0.0;//Xの平均をもとめる
  avex=sumx/i;

  bunx=0.0;//Xの分散をもとめる
  bunux=0.0;
  for(i=0;i<n;i++){
    bunux+=(x[i]-avex)*(x[i]-avex);
  }
  bunx=bunux/i;

  sumy=0.0;//Yの総和をもとめる
  for(i=0;i<n;i++){
    sumy+=y[i];
  }
  avey=0.0;//Yの平均をもとめる
  avey=sumy/i;

  buny=0.0;//Yの分散をもとめる
  bunuy=0.0;
  for(i=0;i<n;i++){
    bunuy+=(y[i]-avey)*(y[i]-avey);
  }

  buny=bunuy/i;

  kyoubunxy=0.0; //共分散を求める
    for(i=0;i<n;i++){
      kyoubunxy+=(x[i]-avex)*(y[i]-avex);
  }
  kyoubunxy=kyoubunxy/i;

  soukan=0.0;//相関係数をもとめる
  soukan=kyoubunxy/(sqrt(bunx)*sqrt(buny));
  
  printf("X合計=%f\n",sumx);
  printf("X平均=%f\n",avex);
  printf("X分散=%f\n",sqrt(bunx));//根号をとった
  printf("Y合計=%f\n",sumy);
  printf("Y平均=%f\n",avey);
  printf("Y分散=%f\n",sqrt(buny));//根号をとった
  printf("共分散=%f\n",kyoubunxy);
  printf("相関係数=%f\n",soukan);

  return 0;
}

だんごさん
記事: 273
登録日時: 13年前

Re: プログラム実行の手間を省きたいです

#3

投稿記事 by だんごさん » 8年前

20行目21行目の x[n] という宣言は正しくないように思います。nが初期化されていませんし、そもそも変数でサイズを指定できたかどうか…。
 Dango San

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: プログラム実行の手間を省きたいです

#4

投稿記事 by あんどーなつ » 8年前

Cでスクリプトを作ることもできます。

script.c

コード:

#include <stdio.h>

int main() {
    int i;
    for (i = 2; i <= 102; i++)
        printf("./soukan data1.txt data%d.txt\n", i);
    return 0;
}
ちなみにperlだと、
perl -e 'for ($i=2;$i<=102;$i++) { print("./soukan data1.txt data$i.txt\n"); }'
とコマンドライン上で入力することで同じことができます。

コマンドライン上で、このように入力してください。

コード:

gcc script.c -o script
./script > script.sh
source script.sh
[code]
最後の行で、./script.shでなくsource script.shとするのがポイントです。こっちのほうがうまくいくことが多いです。

とり

Re: プログラム実行の手間を省きたいです

#5

投稿記事 by とり » 8年前

だんごさん ありがとうございます
しばらくC言語触らず座学だったので配列を作っていたつもりでしたが
解説書読んで気づきましたこれだと大きさを定義してますね・・・・

あんどーなつさん
ありがとうございます
C言語でのループ全然思いつきませんでした。。私の経験が足りませんね。いろいろなプログラム見たりなど
してこういうプログラムを書けるのは本当に身についているのだなと実感しました。
プログラムだけでなく実行まで意味を考えながら実行してみます

お二人のアドバイスからもう一度明日学校で考えてなんとかファイル読み込みから配列への受け渡し考えてみます
またわからなければここにくるかもしれません・・・

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: プログラム実行の手間を省きたいです

#6

投稿記事 by みけCAT » 8年前

まず、20行目と21行目のxとyの宣言において、初期化されていない自動変数nの値(不定)が使用されているため、未定義動作となります。
このバグを修正したあと、このプログラムはコマンドライン引数ではなく標準入力からファイル名を入力するのですね。
popen関数でプロセスを実行して流し込むのがいいでしょう。

コード:

#include <stdio.h>
#include <errno.h>

int main(void) {
	FILE* pp;
	int i;
	for (i = 2; i <= 102; i++) {
		/* メモリ割り当てに失敗したpopenはerrnoをセットしないので、初期化する */
		errno = 0;
		/* プログラムsoukanを実行する */
		pp = popen("./oukan", "w");
		if (pp == NULL) {
			perror("popen failed");
		} else {
			/* プログラムsoukanの標準入力に書き込む */
			fprintf(pp, "data1.txt\ndata%d.txt\n", i);
			/* プログラムsoukanの終了を待つ */
			pclose(pp);
		}
	}
	return 0;
}
シェルスクリプトを生成して実行するなら、echoコマンドを使うといいでしょう。

コード:

#include <stdio.h>

int main(void) {
	int i;
	for (i = 2; i <= 102; i++) {
		printf("echo \"data1.txt data%d.txt\" | ./soukan\n", i);
	}
	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: プログラム実行の手間を省きたいです

#7

投稿記事 by みけCAT » 8年前

だんごさん さんが書きました:そもそも変数でサイズを指定できたかどうか…。
C言語ならC99以降ならできます。C++標準ではできません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

朱鷺

Re: プログラム実行の手間を省きたいです

#8

投稿記事 by 朱鷺 » 8年前

とり さんが書きました:はじめまして 恥を忍んで投稿させていただきます・・・

シェルスクリプト?を使えば処理を早くできるみたいですがいまいちわからなくてCプログラムの中でうまくできないかと
./soukan data1.txt data2.txt
./soukan data1.txt data3.txt
・・・・
シェルスクリプトを使うならこんな感じですかね(処理を早くするというのはよくわかりませんが)。

コード:

#!/bin/sh
MAX=100
for i in `seq 2 $MAX`
do
    echo "data1.txt data$i.txt" | ./soukan
done
exit 0

とり

Re: プログラム実行の手間を省きたいです

#9

投稿記事 by とり » 8年前

こんばんは
また困ってしまったので・・・・
なんとか皆さんのアドバイスでコマンドラインからファイル名を入れて
実行できるようになりました。

「困っている点」
(1)
今の現状では実行するとdata1.txtのファイルをループで回すため
0.1
0.2
0.1
・・・・
といった風に計算結果が表示されるのですが
これを
1-2 0.1
1-3 0.2
1-4 0.1
・・・
といった表示にできるようにしたいのですがどこを変えるのが良いでしょうか・・・
(2)
あんどーなつさんのプログラムを参考に
ループでjを追加してみたのですがこれだと
2-2というダブりや2-1というすでに行ったものが出てきて非効率的でした・・・
(1-2) 0.1
(1-3) 0.2
(1-4) 0.1
・・・
(2-1) 0.1
(2-2) 1
(2-3)0.5
・・・
()は組み合わせ見やすいように入れました。実際は実装できてません・・・

コード:

#include <stdio.h>
 
int main() {
    int i,j;
    for (j = 1; j<=102; j++){
    for (i = 2; i <= 102; i++){
        printf("./soukan data%d.txt data%d.txt\n", j,i);
      }
    }
    return 0;
}
これを1括で
1-2 0.1
1-3 0.3
・・・
2-3 0.5
2-4 0.6
・・・
とできるようになったら考えています

コード:

//11月24日 
//コマンドライン引数でファイル名を指定するプログラム
//
//コンパイルコマンド gcc -o soukan soukan.c -lm
//実行 ./soukan test1.txt test2.txt

#include<stdio.h>
#include<math.h>
#include<stdlib.h>

int main(int argc,char *argv[]){

  char *filename,*filename2;
  //char filename[100];
  //char filename2[100];
  FILE *fp;
  int i,n,m,t;
  int *ip;
  double sumx,avex,bunux,bunx,kyoubunx;
  double sumy,avey,bunuy,buny,kyoubuny;
  double kyoubunxy,soukan;
  double x[20];
  double y[20];
  char s[100];
  float f1,f2;

  if (argc!=3){
    puts("ファイル名を指定してください");
    return 1;
  }
  
  filename = argv[1];
  filename2 = argv[2];
  
  //printf("ファイル名を入力してください");
  //scanf("%s",filename);

  fp=fopen(filename,"r");
  if(fp==NULL){
    printf("ファイルがありません\n");
    return 0;
  }
  
  for(n=0;n<20;n++){
    fscanf(fp,"%lf",&(x[n]));
  }
  
  fclose(fp);

  //printf("2つめのファイル名を入力してください");
  //scanf("%s",filename2);
  fp=fopen(filename2,"r");
  
  if(fp==NULL){
    printf("ファイルがありません\n");
    return 0;
  }
  
  for(n=0;n<20;n++){
    fscanf(fp,"%lf",&(y[n]));
  }
  
  fclose(fp);

  sumx=0.0;//Xの総和をもとめる
  for(i=0;i<n;i++){
  sumx+=x[i];
  }
  
  avex=0.0;//Xの平均をもとめる
  avex=sumx/i;

  bunx=0.0;//Xの分散をもとめる
  bunux=0.0;
  for(i=0;i<n;i++){
    bunux+=(x[i]-avex)*(x[i]-avex);
  }
  bunx=bunux/i;

  sumy=0.0;//Yの総和をもとめる
  for(i=0;i<n;i++){
    sumy+=y[i];
  }
  avey=0.0;//Yの平均をもとめる
  avey=sumy/i;

  buny=0.0;//Yの分散をもとめる
  bunuy=0.0;
  for(i=0;i<n;i++){
    bunuy+=(y[i]-avey)*(y[i]-avey);
  }

  buny=bunuy/i;

  kyoubunxy=0.0; //共分散を求める
    for(i=0;i<n;i++){
      kyoubunxy+=(x[i]-avex)*(y[i]-avex);
  }
  kyoubunxy=kyoubunxy/i;

  soukan=0.0;//相関係数をもとめる
  soukan=kyoubunxy/(sqrt(bunx)*sqrt(buny));
  
  //printf("X合計=%f\n",sumx);
  //printf("X平均=%f\n",avex);
  //printf("X分散=%f\n",sqrt(bunx));//根号をとった
  //printf("Y合計=%f\n",sumy);
  //printf("Y平均=%f\n",avey);
  //printf("Y分散=%f\n",sqrt(buny));//根号をとった
  //printf("共分散=%f\n",kyoubunxy);
  printf("%f\n",soukan);

  return 0;
}

とり

Re: プログラム実行の手間を省きたいです

#10

投稿記事 by とり » 8年前

水曜日はお休みでしたね
たくさんの人にこたえていただいてびっくりすると同時にみなさんの
実力にびっくりしました・・・
自分だとプログラムするのに違うことしながらとはいえ1か月かかって書いてたので・・・
今日学校いってきて少し考えてきました

みけCATさん
プログラムまでありがとうございます
もともとコマンドラインから引数を渡したかったのですがいまいち理解できてなくて火曜日には実装できてませんでした
popen関数使ったことも聞いたこともなかったので理解して使わせていただきます・・・

朱鷺さん
シェルスクリプトでの模範プログラムありがとうございます
自分の言い方が悪かったです
処理を早くするのではなくプログラム実行の手間を少なくするが正しい言い回しだったと思います

Egg

Re: プログラム実行の手間を省きたいです

#11

投稿記事 by Egg » 8年前

ファイルの数が5個の例で考えてみましょう。
1-2, 1-3, 1-4, 1-5
2-3, 2-4, 2-5
3-4, 3-5
4-5
左の数字がjで右の数字がiだとすれば、
『iはj+1からファイルの最大数までいけばいい』
ということがわかります。

じゃあそれをコード化してみましょう

コード:

#define FILE_NUM 102 
int main() 
{
    int i, j;
    for (j = 1; j <= FILE_NUM; j++){
        // iはj+1から最大まで
        for (i = j+1; i <= FILE_NUM; i++){
            printf("./soukan data%d.txt data%d.txt\n", j,i);
        }
    }
    return 0;
}

Egg

Re: プログラム実行の手間を省きたいです

#12

投稿記事 by Egg » 8年前

おっと、このプログラムだと
5-?で条件で弾かれて処理を行わない無駄なループが一回ありますね。
先ほどの5ファイルの例ですと
jは最大数-1でしたので

コード:

for (j = 1; j <= FILE_NUM; j++){
の部分を

コード:

for (j = 1; j <= FILE_NUM - 1; j++){
とすれば正しいでしょうか。

朱鷺

Re: プログラム実行の手間を省きたいです

#13

投稿記事 by 朱鷺 » 8年前

C言語での回答がすでについていますのでシェルプログラムで書いてみました。
...もう少し簡潔に済ませたかったのですが(seqにこだわりすぎたかなぁ...)。

コード:

#!/bin/sh
MAX=100
for i in `seq 1 $MAX`
do
    j = `expr $i + 1`
    for k in `seq $j $MAX`
    do
        echo "data$i.txt data$k.txt" | ./soukan
    done
done

とり

Re: プログラム実行の手間を省きたいです

#14

投稿記事 by とり » 8年前

Eggさんありがとうございます
よくよく考えればよく初心者の問題にあるような碁石の数を増やす、減らして碁石を並べて表示させるような
問題と似ていたのですね。自分でも気づけばできそうですが全然気づかなかったのでていねいな解説で
ものすごく納得できました

朱鷺さんももういちどシェルスクリプトでのプログラムありがとうございます
ぜんぜん使ったことなかったのでコード読んで勉強させてもらってますほんとにありがとうございます

ここで一度質問は終わろうと思います
聞いてばかりではいけないと思い、また自分の力で進めようと思います
いろいろな人がいろいろな方法や、知らないこと、素早い解答等ものすごく勉強になりました

また困ったら来るかもしれません
重ね重ねですがありがとうございました!

閉鎖

“C言語何でも質問掲示板” へ戻る