• ベストアンサー

C言語について質問です。このプログラムの作成方法

以下のプログラムは隣の数を引いた値が下に行き、すべての数字が重ならないようにするプログラムです。完成したのは良いですが、凄く見にくいプログラムになってしまいました。 見やすいように修正箇所を教えて頂けますか? ttp://ideone.com/cJyaH

質問者が選んだベストアンサー

  • ベストアンサー
回答No.6

では最後に何段でもいけるやつ。 ただ、本質的に効率無視のプログラムなので、5段(= 15まで)はちゃんと出たけど、6段(= 21まで)は該当無しという結果が出て、7段(= 28まで)に至っては、時間がかかりすぎて終わらなかった。 4段や3段はきっちり答が出た。 #include <stdio.h> #include <stdlib.h> #define maxStep (5) #define numOfCell (maxStep * (maxStep + 1) / 2) static int firstLine[maxStep]; static void init(void) { int i; for(i = 0; i < maxStep; i++) firstLine[i] = 0; } static int checkNum(int cell[]) { int i; int j; for(i = 0; i < numOfCell; i++) { if (cell[i] < 1) return 0; if (cell[i] > numOfCell) return 0; } for(i = 0; i < numOfCell - 1; i++) for(j = i + 1; j < numOfCell; j++) if (cell[i] == cell[j]) return 0; return 1; } static void disp(int num[]) { int i; int j; int dispPos = 0; printf("\n\n"); for(i = 0; i < maxStep; i ++) { printf("%*s", i * 2, ""); for (j = 0; j < maxStep - i; j++) printf("%4d", num[dispPos++]); printf("\n"); } } static int getALine(int cell[]) { int i; firstLine[0]++; for (i = 0; i < maxStep - 1; i++) { if (firstLine[i] > numOfCell) { firstLine[i] = 0; firstLine[i + 1]++; } } if (firstLine[maxStep - 1] > numOfCell) return 0; for(i = 0; i < maxStep; i++) cell[i] = firstLine[i] + 1; return 1; } static void expand(int cell[]) { int i; int j; int doneCount = maxStep; for(i = maxStep - 1; i >= 1; i--) { int leftOfThisLine = doneCount; int leftOfLastLine = doneCount - i - 1; for (j = 0; j < i; j++) cell[leftOfThisLine + j] = abs(cell[leftOfLastLine + j] - cell[leftOfLastLine + j + 1]); doneCount += i; } } int main() { int cell[numOfCell]; init(); while(getALine(cell)) { expand(cell); if (checkNum(cell)) disp(cell); } return 0; }

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (5)

回答No.5

しまった……。 No.2 の回答ですが、そもそも、 int canSubtract(int a, int b, int c) // 引き算が成立するか? なんて定義する必要ありませんでした。 if (canSubtract(i3, i2, i1)) は if (abs(i3 - i2) == i1) でOK 以下同じ。

全文を見る
すると、全ての回答が全文表示されます。
回答No.4

さて、先のプログラムで使った(重複を気にしないで)全部の数字を生成する方法は、実は、数字の位取りの方法と同じです。 ここでは、(同じ事なのですが)15進数を使用して、各桁の数字を使うという方法で少し直してみました。 また、いくつかループにしていなかった部分をループに直しています。 いずれにしても、このプログラムは「効率は悪い」です。 こういうソースでも答えが出てしまう時代でもありますが。 もう少し複雑な場合では、既に回答があるように、バックトラックなど使用して、効率を稼ぐ必要があります。 なお、バックトラックというのは、見かけは、「再帰呼び出しを使ってループを見えなくする方法」でもあります(そういうものではないですけど) その意味では、オリジナルのプログラムが、バックトラックへは近道かと思います。 #include <stdio.h> #include <stdlib.h> #define maxNum 15 int isOK(int num[]) { int i; int forCheck[maxNum] = {0}; for(i = 0; i < maxNum; i++) { if (num[i] < 0) return 0; // 小さすぎる if (num[i] > maxNum) return 0; // 大きすぎる if (forCheck[num[i] - 1] != 0) return 0; // 重複 forCheck[num[i] - 1] = 1; // 重複チェック用 } return 1; } void disp(int num[]) { int i; int j; int startPos[] = { 0, 5, 9, 12, 14}; int endPos[] = { 4, 8, 11, 13, 14}; printf("\n\n"); for(j = 0; j < 5; j ++) { printf("%*s", j * 2, ""); for(i = startPos[j]; i <= endPos[j]; i++) printf("%4d", num[i]); printf("\n"); } } int main() { int num[maxNum]; int i; int j; int base; int ketaWk; int startPos[] = { 5, 9, 12, 14}; int endPos[] = { 8, 11, 13, 14}; int diff[] = { 5, 4, 3, 2}; for(base = 0; base < 15 * 15 * 15 * 15 * 15; base++) { // 答えは先頭の5つの数字で決まる。 // だから基本になる数字を 00000(15) から EEEEE(15) まで変化させて // 各桁を数値として使用する // ketaWk = base; for(i = 0; i <= 4; i++) { num[i] = (ketaWk % 15) + 1; ketaWk /= 15; } // 2段目以降の計算 for(j = 0; j <= 3; j++) { for(i = startPos[j]; i <= endPos[j]; i++) num[i] = abs(num[i - diff[j] + 1] - num[i - diff[j]]); } // 結果を確認 if (isOK(num)) disp(num); } return 0; }

全文を見る
すると、全ての回答が全文表示されます。
回答No.3

オリジナルとは考え方を変えて、 ・数字の組み合わせを全部出してみる(この段階では重複を気にしない) ・ただし、答えは先頭の段(5つの数字)だけで決まるので、5個しか考えない ・2段目以降の計算をしてみる ・最後に妥当性をチェックする という考え方で書いたのがこのソースです。 #define maxNum 15 int isOK(int num[]) { int i; int forCheck[maxNum] = {0}; for(i = 0; i < maxNum; i++) { if (num[i] < 0) return 0; // 小さすぎる if (num[i] > maxNum) return 0; // 大きすぎる if (forCheck[num[i] - 1] != 0) return 0; // 重複 forCheck[num[i] - 1] = 1; // 重複チェック用 } return 1; } void disp(int num[]) { int i; printf("\n\n"); for(i = 0; i <= 4; i++) printf("%4d", num[i]); printf("\n"); printf(" "); for(i = 5; i <= 8; i++) printf("%4d", num[i]); printf("\n"); printf(" "); for(i = 9; i <= 11; i++) printf("%4d", num[i]); printf("\n"); printf(" "); for(i = 12; i <= 13; i++) printf("%4d", num[i]); printf("\n"); printf(" "); for(i = 14; i <= 14; i++) printf("%4d", num[i]); printf("\n"); } int main() { int num[maxNum]; int i; for(i = 0; i < 5; i++) num[i] = 1; while(1) { num[0]++; for(i = 0; i < 4; i++) { if (num[i] > maxNum) // この桁があふれた { num[i] = 1; num[i + 1]++; } } if (num[4] > maxNum) break; // すべての場合を尽くした。 // 2段目以降の計算 for(i = 5; i <= 8; i++) num[i] = abs(num[i - 4] - num[i - 5]); for(i = 9; i <= 11; i++) num[i] = abs(num[i - 3] - num[i - 4]); num[12] = abs(num[10] - num[9]); num[13] = abs(num[11] - num[10]); num[14] = abs(num[13] - num[12]); if (isOK(num)) disp(num); } return 0; }

全文を見る
すると、全ての回答が全文表示されます。
回答No.2

まずは、構造を保持して直してみました。 いかんせん、ネストしたループはプログラミングの大敵ですし、 また、あれをやりながらこれをやるという(数字の範囲をチェックしながら、引き算の結果になるかどうかチェックして、重複チェックをする)というのは、混乱の元です。 ここでは、その構造は保持したまま、引き算の結果になるような数字を使うのではなく、全部の数字を 1 から 15まで変化させて、引き算の結果になるかどうかを確認するようにしています。 こうすると、数値は必ず 1 から 15に収まるので、範囲チェックも不要です。 また、重複チェックは最後にまとめてやっています。 オリジナルのものに比較すると、「効率」という点では明らかに劣りますが。 #include <stdio.h> int canSubtract(int a, int b, int c) // 引き算が成立するか? { // a - b == c or b - a == c のとき、1 を返す if (a - b == c) return 1; if (b - a == c) return 1; return 0; } // こうすると、たとえば、for (i5 = i4 - i2; i5 <= i4 + i2; i5 += 2 * i2) // という謎の式は // for (i5 = 1; i5 <= 15; i++) // { if canSubtract(i5, i4, i2) で置き換えできる。 int checkDup(int use[], int num) // 重複チェック { if (use[num]) return 1; use[num] = 1; return 0; } int main(void) { int i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15; for (i1 = 1; i1 <= 15; ++i1) { for (i2 = 1; i2 <= 15; ++i2) { for (i3 = 1; i3 <= 15; i3++) { if (canSubtract(i3, i2, i1)) { for (i4 = 1; i4 <= 15; ++i4) { for (i5 = 1; i5 <= 15; i5++) { if (canSubtract(i5, i4, i2)) { for (i6 = 1; i6 <= 15; i6++) { if (canSubtract(i6, i5, i3)) { for (i7 = 1; i7 <= 15; ++i7) { for (i8 = 1; i8 <= 15; i8++) { if (canSubtract(i8, i7, i4)) { for (i9 = 1; i9 <= 15; i9++) { if (canSubtract(i9, i8, i5)) { for (i10 = 1; i10 <= 15; i10++) { if (canSubtract(i10, i9, i6)) { for (i11 = 1; i11 <= 15; ++i11) { for (i12 = 1; i12 <= 15; i12++) { if (canSubtract(i12, i11, i7)) { for (i13 = 1; i13 <= 15; i13++) { if (canSubtract(i13, i12, i8)) { for (i14 = 1; i14 <= 15; i14++) { if (canSubtract(i14, i13, i9)) { for (i15 = 1; i15 <= 15; i15++) { if (canSubtract(i15, i14, i10)) { int use[16] = {0}; int result = 0; result += checkDup(use, i1); result += checkDup(use, i2); result += checkDup(use, i3); result += checkDup(use, i4); result += checkDup(use, i5); result += checkDup(use, i6); result += checkDup(use, i7); result += checkDup(use, i8); result += checkDup(use, i9); result += checkDup(use, i10); result += checkDup(use, i11); result += checkDup(use, i12); result += checkDup(use, i13); result += checkDup(use, i14); result += checkDup(use, i15); if (! result) printf("%2d %2d %2d %2d %2d\n %2d %2d %2d %2d\n %2d %2d %2d\n %2d %2d\n %2d\n\n", i15, i14, i13, i12, i11, i10, i9, i8, i7, i6, i5, i4, i3, i2, i1); } } } } } } } } } } } } } } } } } } } } } } } } } return 0; }

全文を見る
すると、全ての回答が全文表示されます。
  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.1

関連する質問でいいかげんな答えをしてしまった私が言うのもアレですが、 総当たり法の定番ともいえる「バックトラック」について調べてみるといいかもしれません。 再帰呼び出しを使うので、取っつきにくいところがあるかもしれません。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • C言語について質問です。このプログラムの作成方法

    1から10の数字の隣と隣を引いた値を下に表示させるプログラムを作りたいです。 ただし使用する数字は同じ数字を使ってはいけない事。 上記のようなプログラムを作りたいのですが、for文とif文を使って作成可能でしょうか? ご指導お願いします。 数値=□ イメージ図   □  □  □  □     □  □  □       □  □         □

  • C言語についてなんですが

    プログラムの作成で分からないところがあります。 「二つの整数値を読み込み、小さいほうの数以上で大きい数以下の 整数を全て加えた値を表示するプログラムをdo文を使って 作成せよ。」 下の図のようにしたいです。 2つの整数を入力せよ。 整数A:37 整数B:28 28以上37以下の全整数の和は325です。 よろしくお願いします。

  • c言語です。

    c言語です。 現在電話番号入力のプログラムを書いているのですが、うまくいきません。 決まりは18文字以下で、入力できる値が数字1から9と'(',')','-'のみです。 私はあと少しなんですが、うまくプログラムを組めませんでした。 forを使ってやってみたのですがうまくいきません。文字数制限はできたのですが、、 どなたか良い知恵がありましたら教えてください。

  • C言語のプログラムみてください(isdigit)

    質問は2つあります。 (1) scanf関数を使って、 時給: と表示されたところに一文字打ち込み、数字以外ならばもう一度 時給: と表示されるプログラムを作りたいです。 以下のプログラムなら、 isdigit関数は数字を渡せば0以外の数を返す(真)から、 while文の条件式(偽)のようにすれば、 ・数字を一文字うちこめばwhile文の条件式にあてはまらない、すなわち下のプログラムではisdigit(payment) != 0 となり、return 0; が実行され、プログラムが終わる ・数字以外を一文字打ち込めばwhile文の条件式が真となり、printf関数とscanf関数が実行されて入力待ち状態になる と思ったのですが、どうやら違うようです。実際には ・数字一文字打てばまた入力待ちになる ・数字以外を一文字打てば永遠にprintf関数とscanf関数が実行される のはなぜでしょうか。 #include <stdio.h> #include <ctype.h> int main(void){ int payment; do{ printf("時給[円]:"); scanf("%d",&payment); } while (isdigit(payment) ==0) ; return 0; } (2) 実際には、一文字の制限なく、数字以外の何かを入力したら、再び入力待ちになるプログラムを作りたいのですが、これはどうしたらいいのでしょうか。 つまり 時給: のところに数字以外ならまた 時給: となるようにしたいのです。お願いします。

  • C言語でサイコロのプログラムを作ってみたのですが

    まず、以下のような”実行するたびに1~6の数字をランダムに出力するプログラム”を 作ってみたのですが、特定の数字が出てこないのです。 例えば、1と4、2と5、3と6 が出ない ここで気がついた事は、 1、出ない目の2数の差は3であること。 2、乱数自体を表示させたところ、ちゃんと1秒毎に乱数は変わっている。こちらには問題はない 3、約1分間隔で、出ない数字の組み合わせが変わる。 (例えば 1,3,4,6,1,6,3,6,4(2と5が出ない)  →1分間実行し続けると・・・  2,3,6,5,3,2,2,6(1と4が出ない)) この原因は一体なんなのでしょうか・・・ とても気になって仕方がありません。わかる方教えてください。 以下、ソースコードになります #include<stdio.h> #include<time.h> #include<stdlib.h> int func(void); int main(void){ int number=0; number=func(); //サイコロの目の表示 printf("%d\n",number); return 0; } int func(){ //乱数の初期化 srand((int unsigned)time(NULL)); //サイコロの目をランダムで出力し、返す return rand()%6+1; } プログラムの仕組みは パソコンから現在の歴時刻を元に乱数の初期値を変更 ↓ rand関数で適当な値を受け取り、6で割った余剰+1を計算し値を返却する

  • C言語の質問。

    課題が出たのですがよくわからないので回答していただける方お願いします。 # 次のようにキーボードから4桁の10進数の入力を2回受付ける. Input the 1st value > 6479 Input the 2nd value > 1497 但し,4桁の各位の数は必ず互いに異なるものが入力されるとする. # 入力された数値に対して,以下に定義する「ヒット数」と「ホームラン数」を求める. * ホームラン数: 二つの数字を同じ位ごとに比較した時,何ヶ所の位について一致しているかを表す. * ヒット数: 二つの数字を異なる位同士で比較した時,一致している組み合わせの数を表す. 例 1234と1234の場合,0ヒット4ホームラン 1234と4321の場合,4ヒット0ホームラン 6479と1497の場合,2ヒット1ホームラン # ヒット数とホームラン数を次のように表示する. 2 hit(s) 1 home run(s)

  • C言語プログラム

    こんな課題が出たのですが、さっぱりわかりません。 誰か解る人がいたら教えてほしいです。 1 3+5+7+…+nと奇数の和を求め、和が100を越えたときのnの値を求めて表示するプログラムをdo~whileを使って作成しなさい。 2  キーボードから3つの数を読み込み、最大値を求める処理を繰り返し行うプログラムを、do~whileを使って作成しなさい。繰り返しの終了は、計算のあとで、'S'(1文字)が入力されるまでとし、それ以外の文字では、処理を続行すること。 3  整数型1次元配列kに整数123,456,789をそれぞれ代入し、その内容を表示するプログラムを作成しなさい。 4  文字型1次元配列strに'u'を、'n'、'i'、'x'それぞれ代入し、その内容を文字列として出力するプログラムを作成しなさい。 5  要素数50の整数型1次元配列mの要素すべてに、その添字に対応した0~49の数値を代入し、その内容を表示するプログラムを作成しなさい。 3は #include <stdio.h> main() { int k[3]; k[0] = 123; k[1] = 456; k[2] = 789; printf("%d\n%d\n%d", k[0], k[1], k[2]); return 0; } と解いてみたんですが、やっぱ間違えてますよね?

  • C言語(問題)

    高校で使っているC言語の参考書の問題でどうしてもわからない部分があり悩んでます。 問題 正の二桁の整数をキーボードから入力し,その値を初期値として以下の増加値に従って増加を続け,それぞの値を表示する値が100以上になったら終了し,各値の総和およびデータ数を表示する。 増加値は入力した初期値の下一桁によって以下のように設定される。   ・初期値の下一桁が1または6のとき増加値は8   ・初期値の下一桁が2または7のとき増加値は3   ・初期値の下一桁が3または8のとき増加値は10   ・初期値の下一桁が4または9のとき増加値は5   ・それ以外のとき増加値は6 実行結果例 正の二桁の整数を入力してください 45 45 51 57 63 69 75 81 87 93 99 総和は 720 です データ数は 10 個です この問題をswitch-caseを使用して回答せよとのことなのですが初期値の下一桁の部分のプログラムがよくわかりません。。 回答お待ちしています。よろしくお願いします。。

  • C言語のプログラムについて

    大学のテストの確認をしているのですが、この問題の回答がどうしても分かりません。どなたか教えてください。 9桁以内の整数を入力し、int型の変数に格納し表示するプログラムを作成しなさい。 ただし、先頭は、数字か「+」か「-」とし、それ以外はすべて数字とする。この条件に合わないものおよび10桁以上のものは、「入力エラー」として表示後、再入力を行うこと。 例えば「-123456」や「45678989」は、整数としてint型の変数に格納し、「abcd」や「123-234」は、「入力エラー 」を表示し、再入力を行う。 ---------------------------------------------------------------- また、自動判定の都合上、表示は以下の表示例に準拠すること。 【 23-234 *12345 -123456 を入力した例】 入力エラー 入力エラー 整数は、-123456 です。 お願いします

  • C言語!プログラム書いたのですがエラーです!

    課題内容 キーボードから数字を入力してその値までの合計を表示する。 さらに入力した値が偶数なら0から入力した値までのすべての偶数の和を 入力した値が奇数なら1から入力した値までのすべての奇数の和を表示するプログラム。 #include<stdio.h> int main(void) { int i, n, sum; sum=0; scanf("%d",&n); printf("入力値:%d\n", n); if(n%2 = 0) { for(i=0;i<=n;i+=2) { sum=sum+i; } printf("合計値:%d\n",sum); } else { for(i=1;i<=n;i+=2) { sum=sum+i; } printf("合計値:%d\n",sum); } return(0); } これでコンパイルすると10行目に左辺値が必要とでます。 どうすればよいでしょうか? 教えてください。よろしくお願いします。