プログラミングの課題をやっているのですが、一部どうしてもわからないことがあるので質問させて下さい。
課題では100個の点のX座標、Y座標を0から5の間でランダムに設定し、ファイルに書き出すといった内容なのですが、この点を設定した時、任意の2点のX座標、Y座標の差が0.01以下になる場合は設定し直さないといけません。
これは後々の課題に関係してくるから、みたいなのですが。
そこで点の設定の関数として以下のようなプログラムを考えてみました。(ちなみに構造体pointを前に宣言してあり、p[].xは点のX座標、p[].yは点のY座標です。また、NUM=100と最初に宣言してあります。)
void tensettei(point p[], int num){
int i, j, k;
double sa;
for(i=0; i < num; i++){
p[i].x = ((double) rand()) / ((double) RAND_MAX) * 5;
p[i].y = ((double) rand()) / ((double) RAND_MAX) * 5;
}
for(j=0; j < (num - 1); j++){
for(k=j+1; k < num; k++){
if(p[j].x > p[k].x)
sa = p[j].x - p[k].x;
else
sa = p[k].x - p[j].x;
if(sa < 0.01)
tensettei(p, NUM)
}
}
for(j=0; j < (num - 1); j++){
for(k=j+1; k < num; k++){
if(mp[j].y > mp[k].y)
sa = mp[j].y - mp[k].y;
else
sa = mp[k].y - mp[j].y;
if(sa < 0.01)
tensettei(p, NUM)
}
}
}
しかし、実行してもエラーが起こったのかすぐに終了してしまい、この後に点データを出力するプログラムを書いているのですが、ちゃんと出力されません。
いろいろプログラムを変えて試した結果、原因は「tensettei(p, NUM)」にあることはわかりました。
それさえ変えればすればうまく動作しましたので。
再帰と同じ感じでいけるかと思ったのですがどうやら駄目のようです。
そこで質問なのですが、ループの中である条件が起こったら最初からやり直し、てなプログラムはどんな風にすればいいのでしょうか?
No.1
- 回答日時:
void tensettei(point p[], int num){
start:
int i, j, k;
double sa;
for(i=0; i < num; i++){
p[i].x = ((double) rand()) / ((double) RAND_MAX) * 5;
p[i].y = ((double) rand()) / ((double) RAND_MAX) * 5;
}
for(j=0; j < (num - 1); j++){
for(k=j+1; k < num; k++){
if(p[j].x > p[k].x)
sa = p[j].x - p[k].x;
else
sa = p[k].x - p[j].x;
if(sa < 0.01) goto start;
}
}
for(j=0; j < (num - 1); j++){
for(k=j+1; k < num; k++){
if(mp[j].y > mp[k].y)
sa = mp[j].y - mp[k].y;
else
sa = mp[k].y - mp[j].y;
if(sa < 0.01) goto start;
}
}
}
のような単一ループはだめですか?
No.2
- 回答日時:
デバッガを使ってエラー位置を特定してみてください
>それさえ変えればすればうまく動作しましたので
どう変更したらうまく実行できましたか?
コンパイルは通っているのですか?
コンパイラによるのかもしれませんが
tensettei(p, NUM);
としなくてもコンパイル通るんでしょうか
>ループの中である条件が起こったら最初からやり直し
ある条件でgotoを使うとか?
再帰というか再呼び出しでもいいと思いますけども。
結果さえちゃんと順に返してあげれば。
0.01以下判定部分を削除したら問題なく動作しましたので。
コンパイラは通ってます。
問題は文法とかじゃなくてもっと根本的な考え方の部分にありました。
No.4
- 回答日時:
再帰呼出しでもできそうな気もするが、試してないので以下適当な意見。
再帰で呼出すとき、tensetti(p, NUM) となっているけど、これは tensettei(p, num) の誤記ですよね。
あと、戦略として 100個の点を p[] に格納してから、2点の差を検証して 0.01 を下回ったら、再度 100個の点を p[] に再設定するということですね。
そうであれば、 for の 2重ループで 2点間の最小の差を sa に格納し終ってから loop の外側で if ( sa < 0.01 ) tensettei(p, num); とした方がわかりやすいと思います。
>ループの中である条件が起こったら最初からやり直し、
>てなプログラムはどんな風にすればいいのでしょうか?
私なら最初からやり直すのではなしに、配列 p[] に点を格納する際に、「それまで格納した点と今 rand() 関数で得られた点の最小距離」を計算する関数
distance(point* p, size_t size, point new_pt); を作成して
do {
p[i].x = ((double) rand()) / ((double) RAND_MAX) * 5;
p[i].y = ((double) rand()) / ((double) RAND_MAX) * 5;
} while ( distance(p, i-1, p[i]) < 0.01 );
のような感じでループを回すのかなぁ。(これまた適当)
はい、誤記です。(まぁどっちでも結果は同じですが。)
結局いろいろな人にアドバイスを受けて
「私なら最初からやり直すのではなしに、配列 p[] に点を格納する際に、「それまで格納した点と今 rand() 関数で得られた点の最小距離」を計算する関数distance(point* p, size_t size, point new_pt); を作成して」
に近いプログラムに変更してちゃんと動作するようになりました。
ありがとうございました。
No.5
- 回答日時:
まず1つ、再帰呼出しから戻ってきた後、諦めたはずなのにさっきのforループを
続けてしまう、という問題があります。
それを解消するには色々策がありますが、質問者のやりたいことに近いだろうと
思われるコードを書いてみました。
ポイントは、差を評価する部分を関数化して「forループ2つを一気に終了させる」
ことを可能にした点です。
(ちなみに、C言語ではgoto文は蛇蝎のごとく嫌われるので、極力使わない方がいいです。)
一応これで動いているのですが、残念ながら100個の点全てのx座標、y座標が
うまく散らばることは非常に難しいらしく、何度も再帰呼出しを繰り返した末、
スタックが不足してエラーが起こっているようです。
ダメだったら最初からやり直し、というやり方に無理があるように思います。
#include <stdio.h>
#include <stdlib.h>
#define NUM 100
typedef struct _point {
double x;
double y;
} point;
int check_points(point p[], int num)
{
int j,k;
double sa;
for(j=0; j < (num - 1); j++){
for(k=j+1; k < num; k++){
if(p[j].x > p[k].x)
sa = p[j].x - p[k].x;
else
sa = p[k].x - p[j].x;
if(sa < 0.01)
return(0);
}
}
for(j=0; j < (num - 1); j++){
for(k=j+1; k < num; k++){
if(p[j].y > p[k].y)
sa = p[j].y - p[k].y;
else
sa = p[k].y - p[j].y;
if(sa < 0.01)
return(0);
}
}
return(1);
}
void tensettei(point p[], int num)
{
int i;
for(i=0; i < num; i++){
p[i].x = ((double) rand()) / ((double) RAND_MAX) * 5;
p[i].y = ((double) rand()) / ((double) RAND_MAX) * 5;
}
if(check_points(p, num) == 0)
tensettei(p, num);
}
void main(void)
{
point points[100];
int i;
tensettei(points, NUM);
for(i=0 ; i<NUM ; i++)
printf("points[%3d} = (%2.3lf, %2.3lf)\n", i, points[i].x, points[i].y);
}
はい、結局「残念ながら100個の点全てのx座標、y座標がうまく散らばることは非常に難しいらしく、何度も再帰呼出しを繰り返した末、スタックが不足してエラーが起こっている」ということが問題でした。
根本的な間違いだったわけです。
ありがとうございました。
No.6
- 回答日時:
★アドバイス
・再帰処理と書かれていますが、普通にループでも出来ますが…。
あと最初に 100 個の点を乱数で発生させるよりも1つ1つ乱数で発生させるときに
0.01 を越えないように乱数の段階で制御して 100 個の点を配列にセットすれば楽だと思います。
・つまり、最初の1つ目だけ 0~5 の乱数でそれ以降の 99 個は 0 ~ 0.02 の範囲に設定します。
そして、前の点に加算して 0~5 の範囲に制御すれば良いと思います。
・下にそのサンプルを載せておきます。
サンプル:
void tensettei( point p[], int num )
{
double px, rndX;
double py, rndY;
int i;
// 最初だけ 0~5 の乱数
p[ 0 ].x = (5.0 * rand() / RAND_MAX);
p[ 0 ].y = (5.0 * rand() / RAND_MAX);
for ( i = 1 ; i < num ; i++ ){
// -0.01~+0.01 までの乱数
rndX = (0.02 * rand() / RAND_MAX) - 0.01;
rndY = (0.02 * rand() / RAND_MAX) - 0.01;
// 新しいX軸,Y軸
px = p[i - 1].x + rndX;
py = p[i - 1].y + rndY;
// 新しいX軸,Y軸の 0~5 補正
px = (px >= 5.0) ? 5.0 : (px < 0.0) ? 0.0 : px;
py = (py >= 5.0) ? 5.0 : (py < 0.0) ? 0.0 : py;
// 補正したX軸,Y軸を配列にセット
p[ i ].x = px;
p[ i ].y = py;
}
}
下のお礼にも書いたとおり、最初に100個の点を作ったのが問題だったのですが、Oh-Orangeさんの考え方だと次の課題に影響が出てくるので・・・
結局1個新しい点を作るたびにそれまで作った点と比較して、差が0.01以下のものがあれば--iしてやってやり直し、というプログラムにしました。
No.7
- 回答日時:
★修正。
・問題文を間違って理解してしまったようです。
0.01 以下で再設定でしたね。
下に修正版を載せます。
修正版:
void tensettei( point p[], int num )
{
double diff, rndX, rndY;
int i;
// 最初だけ 0~5 の乱数
p[ 0 ].x = (5.0 * rand() / RAND_MAX);
p[ 0 ].y = (5.0 * rand() / RAND_MAX);
for ( i = 1 ; i < num ; i++ ){
// X軸の乱数
do {
rndX = (5.0 * rand() / RAND_MAX); // 0~5の乱数
diff = fabs( p[i - 1].x - rndX ); // 差を計算
} while ( diff < 0.01 );
// Y軸の乱数
do {
rndY = (5.0 * rand() / RAND_MAX); // 0~5の乱数
diff = fabs( p[i - 1].y - rndY ); // 差を計算
} while ( diff < 0.01 );
// X軸,Y軸を配列にセット
p[ i ].x = rndX;
p[ i ].y = rndY;
}
}
No.8
- 回答日時:
再帰は必要ないように思います
0から5までで 任意の2要素の差が 0.01より大きくしたいなら
500個のフラグを用意して これを参照しながら各要素を決定してみてはいかがでしょう
char flagX[500];
int n = 0, i = 0;
for( n=0; n<500; n++ ) {
flagX[n] = 1;
}
n = 0;
while ( n < NUM ) {
double rndX = 5.0 * rand() / RAND_MAX;
i = (int)(rndX * 100);
// 要素rndXが使用済みか
if ( flagX[i] ) {
// 要素rndXが 1以上か
if ( i>0 ) {
// 要素rndX-0.01は使用済みか
if (flagX[i-1]) {
// 要素rndXが 4.99未満か
if (i<499) {
// 要素rndX+0.01は使用済みか
if ( flagX[n+1] ) {
p[n].x = rndX;
n++;
flagX[i-1] = flagX[i] = flagX[i+1] = 0;
}
} else {
// 要素rndXが4.99の場合
p[n].x = rndX;
n++;
}
}
} else {
// 要素rndXが0の場合
if ( flagX[i+1] ) {
p[n] = rndX;
n++;
flagX[i] = flagX[n+1] = 0;
}
}
}
}
Y側も同様の処理で可能だと思います
こういう考え方もアリですね。
ただ、やっぱり1個作るたびに判定して・・・のほうがプログラムとしてはすっきりするので。。。
ありがとうございました。
No.9ベストアンサー
- 回答日時:
>任意の2点のX座標、Y座標の差が0.01以下になる場合
という定義が少しあいまいなので、認識が間違っているかも
しれません。2点というのは、今まで設定された座標と
今設定しようとしている座標の事だとして、
今までに登録されたX,Yとこれから登録するX,Yの誤差が
0.01未満ならば再設定不要であると言うことでよいでしょうか?
つまり、登録されるX,Yの差異が全て0.01以上になれば良いと
判断します。
以下サンプル
/*
* WinXP Pro SP2 VC++6.0
*/
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXARRAYCOUNT (100)
#define OUTPERMISSION (0.01) //許容外数値設定
typedef struct{
double x;
double y;
}point;
void tensettei( point *p, int num )
{
int i,j;
double diff = 0.0;
printf( "Out Permission Setting = %f \n", OUTPERMISSION);
for( i=0; i<num; i++ ){
// X軸,Y軸を配列にセット
p[i].x = ( 5.0 * rand()/RAND_MAX );
p[i].y = ( 5.0 * rand()/RAND_MAX );
//自身の座標X/Yが0.01以下
diff = fabs( p[i].x - p[i].y );
if( diff< OUTPERMISSION ){
i--;
continue;
}
//既存設定要素内と照合
for( j=0; j<i; j++ ){
//任意2点のXが0.01以下
diff = fabs( p[j].x - p[i].x );
if( diff< OUTPERMISSION ){
break;
}
//任意2点のX/Yが0.01以下
diff = fabs( p[j].x - p[i].y );
if( diff< OUTPERMISSION ){
break;
}
//任意2点のY/Xが0.01以下
diff = fabs( p[j].y - p[i].x );
if( diff< OUTPERMISSION ){
break;
}
//任意2点のYが0.01以下
diff = fabs( p[j].y - p[i].y );
if( diff< OUTPERMISSION ){
break;
}
}
//任意要素1つのXまたはYの誤差が0.01以下なので再設定
if( j != i ){
i--;
continue;
}
}//for
}
int main( int argc, char* argv[] )
{
int i, j;
double diff = 0.0;
point p[MAXARRAYCOUNT];
//乱数生成
srand(time(NULL));
srand(rand()/RAND_MAX);
//初期化
memset( p, 0, sizeof(p));
//問題の箇所
tensettei( p, MAXARRAYCOUNT );
printf( "----- Result Check -----\n" );
//Debug 既存設定要素内と照合確認し全ての要素の座標において重複が無いか確認
//もし配列内に1つでも差異0.01未満があれば何番目と何番目の差異がどれだけあるかを表示する
for( i=0; i<MAXARRAYCOUNT; i++ ){
for( j=0; j<MAXARRAYCOUNT; j++ ){
if( i != j ){
diff = fabs( p[j].x - p[i].x );
if( diff< OUTPERMISSION ){
printf( "[OUTPERMISSION] => X[%03d]:%f X[%03d]:%f Diff:%f\n", j, p[j].x, i, p[i].x, diff );
}
diff = fabs( p[j].x - p[i].y );
if( diff< OUTPERMISSION ){
printf( "[OUTPERMISSION] => X[%03d]:%f Y[%03d]:%f Diff:%f\n", j, p[j].x, i, p[i].y, diff );
}
diff = fabs( p[j].y - p[i].x );
if( diff< OUTPERMISSION ){
printf( "[OUTPERMISSION] => Y[%03d]:%f X[%03d]:%f Diff:%f\n", j, p[j].y, i, p[i].x, diff );
}
diff = fabs( p[j].y - p[i].y );
if( diff< OUTPERMISSION ){
printf( "[OUTPERMISSION] => Y[%03d]:%f Y[%03d]:%f Diff:%f\n", j, p[j].y, i, p[i].y, diff );
}
} else {
diff = fabs( p[i].x - p[i].y );
if( diff< OUTPERMISSION ){
printf( "[OUTPERMISSION] => X[%03d]:%f Y[%03d]:%f Diff:%f\n", j, p[i].x, i, p[i].y, diff );
}else{
printf( "[%03d]X:%f Y:%f \n", i, p[i].x, p[i].y );
}
}
}
}
return 0;
}
「任意の2点のX座標、Y座標の差が0.01以下」というのは任意の2点のX座標の差が0.01以下、または任意の2点のY座標の差が0.01以下、ということです。
わかりにくくて申し訳ありませんでした。
ただ、プログラム自体は最終的に作った物と同じ感じです。
ありがとうございました。
No.10
- 回答日時:
#9です。
コメントがうそがいっぱいありますね。。。
以下のように読み替えてください。
座標X/Yが0.01以下 >> 座標X/Yの『差異』が0.01『未満』
また、OUTPERMISSIONの値を大きくすると、
乱数の発生する数値の総数が100個以下になり、
無限ループに陥ります。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# 質問です 下記のコードを分かりやすく解説お願いします 初心者です #include ‹stdio.h 3 2022/05/26 22:03
- C言語・C++・C# C言語でif文が予想と違う動きをする件について7 4 2023/03/20 00:26
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- Excel(エクセル) VBA フォルダ見える化のコードについて 2 2023/06/19 15:04
- C言語・C++・C# LU分解法のピボッティングについて(C言語/gcc-9) 3 2022/07/11 23:10
- C言語・C++・C# C言語初心者 ポインタについて、お助けください、、 2 2023/03/15 23:50
- C言語・C++・C# 10個の実数に対する降順ソート結果を出力するプログラムを作りたいのですが、以下のプログラムをどう直せ 1 2022/07/09 22:16
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# pythonのファイルの並びでの読み込みとリストについて 4 2022/04/13 03:52
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
関数におけるif文とreturn文に...
-
c言語で、繰り返し文の中で、0....
-
C言語を実行すると-infが出てき...
-
2分法で方程式の複数の解を自...
-
C言語の型による処理速度の違い
-
微分方程式(ルンゲ=クッタ)...
-
DWORDの警告
-
c言語のコンパイルエラー canno...
-
指数形式で入力するには
-
プログラムでの数字につく”f”の...
-
極座標から直交座標に変換
-
C言語のマクローリン展開ローラ...
-
ルンゲクッタ法(RK4)で斜方投...
-
台形公式・シンプソン公式につ...
-
方程式を2分法を用いて解くプロ...
-
C言語 関数プロトタイプ宣言の...
-
c++について質問です
-
プログラミングについて
-
プログラミングでのテイラー展開
-
qsortと動的確保の2次元配列
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
プログラムでの数字につく”f”の...
-
float型とdouble型の変数の違い...
-
doubleの変数にintとintの割り...
-
C言語を実行すると-infが出てき...
-
C 開放してるのにエラー(doubl...
-
至急です! マクロ定義で #defi...
-
c言語で、繰り返し文の中で、0....
-
関数におけるif文とreturn文に...
-
C言語 関数プロトタイプ宣言の...
-
C言語初心者 構造体 課題について
-
C言語の型による処理速度の違い
-
Cで3乗根を求める方法
-
C言語で-23乗を取り扱うには
-
2分法で方程式の複数の解を自...
-
doubleは常に%lfとするべきなのか
-
c言語のコンパイルエラー canno...
-
C言語で直角三角形の斜辺を求め...
-
C言語のプログラムで#include<m...
-
int とdoubleの比較
-
C++で外積
おすすめ情報