
100ます計算の問題を作る、というプログラムを作ってみようと考えています。
問題となる数列は、1から9までの数字をランダムに並べ替えた物になるはずです。
さて、この「ランダムに並べ替えた数字」というのは、どういったアルゴリズムで作成するのが最適なのでしょうか?
個人的に思いついたのは、以下のような方法です。
1.整数型の配列変数(要素が9個)を作成し、それぞれ1から9まで数字を入れておきます。
2.乱数を使って、「x番目とy番目の数字を入れ替え」という風に、何度も入れ替えを行います。
これだと非常に単純なのですが、正直言って素人の考えなので、最適なのかどうか疑問なのです。
もっと最適な方法があれば教えて頂きたいです。
No.7ベストアンサー
- 回答日時:
質問文にある方法でも実用上は問題ないと思いますが、アルゴリズムの理論の上では、「最適」な方法とは言えません。
数列をランダムに並べ替えるアルゴリズムについては、このページが参考になります。
http://ray.sakura.ne.jp/tips/shaffle.html
http://www.hyuki.com/d/200510.html#i20051020190000
No.8
- 回答日時:
> _Boolはその通りですが、_Trueや_Falseというのはありません。
# ご指摘多謝。ISO/IEC9899:1999確認しました。ないですね。
# 独自拡張(の定数マクロ)だったのか…orz
自作する場合は、#6さんの実装例に、実際には多重定義の
ガード追加(__bool_true_false_are_defined)ですね。
No.6
- 回答日時:
議論する意図はありませんが、#3の内容に関して...
> > 最新のCだと、bool,true,falseが使えたと思います。
> 最新のC(通称C99)だと、_Bool, _True, _False です。
_Boolはその通りですが、_Trueや_Falseというのはありません。
C99では、bool、true、falseは<stdbool.h>というヘッダで定義されるマクロです。(boolもtypedef名ではなくマクロです)
> とはいえ、未対応のコンパイラ(VCとかbccとか)が多いと思いますが。最新のgccとかなら使えるはず。
これはその通りなのですが、上述の通りヘッダで定義されるマクロですので、<stdbool.h>を自作することで対応してもよいと思います(むしろ、その方が望ましい)。
一応定義例を挙げておきます。
/* <stdbool.h> */
#define bool char
#define true 1
#define false 0
#define __bool_true_false_are_defined 1
No.5
- 回答日時:
あなたの発想で正しいです。
ただ、乱数でxとyを発生させて置き換え漏れを起こさないようにしようとすると、繰り返し回数を多くしないといけないです。
各要素について1回づつ、入れ替えると良いでしょう。つまり、
for(x=1;x<=9;x++) {
y=乱数発生(1~9)
swap(x番目とy番目)
}
これで、12345678から987654321まで、すべての順列が等確率で発生します。乱数発生も9回ですみます。swapのオーバーヘッドを指摘している人も居ますが、乱数発生の手間を比べれば無視できるものです。
確立を均等にする事は考え付きませんでした。ご指摘ありがとうございます。
無事プログラムは完成しそうです。ありがとうございました。
No.4
- 回答日時:
順番に並んでいない重複のない数列を作るという点では乱数を使うしかないと思います。
基本方針は乱数を発生させて、1~9(100マスだとすると本来は0~9)の数字の
配列をX軸分とY軸分作成するという考えでいいと思います。
ただ、
>2.乱数を使って、「x番目とy番目の数字を入れ替え」という風に、何度も入れ替え
>を行います。
というのは、
1)入れ換えの分のオーバヘッドが大きい(退避変数含めて3回、変数間の移動が発生する)
2)乱数値のシノニム(同値)がでた時、無駄になる。(乱数なので可能性はあります。)
という点で好ましくないかと思います。
わたしの考えたのは、0~9のベースとなる配列から乱数でランダムに取り出して、
X軸用の数値の配列と、Y軸用の数値の配列に順次設定していくというやり方です。
詳細は、
1)constの0~9の数値の配列を準備する。(配列Z)
2)取り出し済みのフラグの配列を準備する。初期状態:全て未取り出し(配列F)
3)0~9の範囲を取る乱数を発生させて、その数値番目の配列Z上の要素を
X軸用の数値の配列(配列X)に順次設定していく。
同時に、配列Zから取り出した事を示すフラグを配列Fに立てる。
4)3)を繰り返す。但し、配列Fに取り出し済みフラグが立っていたらリトライする。
結果的にNo.2さんと同じ考え方です。違うのは乱数値をそのまま使ってない事です。
ソースはこんな感じです。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <memory.h>
#define TRUE 1
#define FALSE 0
int rand0and9(int init){
if(init==TRUE) {
srand((unsigned int)time(0));
return 0;
}
else return (int)((10.0 * rand() / (RAND_MAX + 1.0)));
// 下位にあるビットは乱数として質が悪い。そのため上位にあるビットを利用する。
}
int main(void)
{
const int Base[] = {0,1,2,3,4,5,6,7,8,9,};
int take[10];
int lines[2][10];
int indexLine,indexElement,index;
rand0and9(TRUE); // 乱数の初期化
for(indexLine=0;indexLine<2;indexLine++) { // ループ2回でX軸、Y軸分
memset(take,0,sizeof(take));
for(indexElement=0;indexElement<10;indexElement++) { // 一つの軸分
index=rand0and9(FALSE); // 取り出し先を乱数で決定
while(take[index]==TRUE) index=rand0and9(FALSE); //未取出しがあるまでリトライ
lines[indexLine][indexElement] = Base[index];
take[index]=TRUE; // 取出した箇所にフラグを立てる
}
}
/* 確認ためのプリント */
for(indexLine=0;indexLine<2;indexLine++) {
printf("%s: ",indexLine==0?"X軸":"Y軸");
for(indexElement=0;indexElement<10;indexElement++) {
printf("%d ",lines[indexLine][indexElement]);
}
printf("\n");
}
}
No.3
- 回答日時:
補足。
1~9とかなら#2さんの方法でもいいのですが、
大きな数になる場合は後半で異常に時間がかかるので(既出の数字が出続けることになる)、すべての値が必要なら避けた方がよい。
逆に、いくつかだけ使うなら全部のシャッフルが無駄になるので、
既出チェックが早いかもしれない。(この場合も、
既出チェックを配列に入れると予め全部の容量が必要になるので、
大サイズなら、ビットにするなり、ある程度の単位で動的に確保するなりした方がいいかも)
というトレードオフがあります。
> 最新のCだと、bool,true,falseが使えたと思います。
最新のC(通称C99)だと、_Bool, _True, _False です。
とはいえ、未対応のコンパイラ(VCとかbccとか)が多いと思いますが。最新のgccとかなら使えるはず。
ちなみに、bool,true,falseはC++の予約語でしょう。
No.2
- 回答日時:
・1~9の乱数を発生
・既出ならやり直し
では駄目なのですか?
int x[9],y[9];
BOOL bx[9],by[9];
int i,n;
for(i = 0; i < 9; i++){
bx[i] = FALSE;
by[i] = FALSE;
}
i = 0;
while(i < 9)
{
n = rand()%9;
if(bx[n] == FALSE) {
x[i] = n+1;
bx[n] = TRUE;
i++;
}
}
i = 0;
while(i < 9)
{
n = rand()%9;
if(by[n] == FALSE) {
y[i] = n+1;
by[n] = TRUE;
i++;
}
}
BOOL,TRUE,FALSEは環境に応じて変えてください。
最新のCだと、bool,true,falseが使えたと思います。
ありがとうございます。取りあえず自分で作ってみました。
ただ、UKYさんの投稿された記事を最終的に採用させてもらいました。
コードをそのまま書いて頂けると本当に助かります。ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- Excel(エクセル) 結合セルのソートについて 5 2022/04/22 11:57
- Excel(エクセル) Excelで、行に複数の数字が入力されているセルが複数の列存在し、行を跨いでセル内の数値を並び替える 5 2022/06/17 18:03
- C言語・C++・C# このプログラミングの問題を教えて欲しいです。 キーボードから整数kを入力し、kが配列aの中に何個存在 2 2022/12/19 22:50
- その他(プログラミング・Web制作) pythonのプログラムについての質問です。 1 2023/05/26 10:31
- Visual Basic(VBA) Excel のユーザー定義関数でソルバーが動作しない 1 2022/09/05 19:51
- Ruby 初心者プログラミング 3 2022/10/12 11:31
- 計算機科学 アルゴリズムについて 1 2023/01/01 19:43
- 数学 既存の数列のランダム性について(初歩的質問) 2 2022/06/07 20:04
- 数学 数学Aについて分からない問題があります。 答えは載っているので分かりますが、 解き方がわかりません。 5 2023/02/03 18:58
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語 配列の長さの上限
-
配列で格納したものをmsgboxで...
-
C# Listを使わずに2次元配列の...
-
VBで構造体の配列を関数に渡す...
-
C言語で特定列だけを抽出して配...
-
テキストファイルから文字列を...
-
配列を使わずに、変数名を動的...
-
整数型の配列をランダムに並べ...
-
複数の選択範囲の行番号を個別...
-
C言語でcharの足し算
-
C言語 配列の再初期化
-
配列の参照渡しで型が一致しま...
-
GCCについて
-
先頭アドレスとは何ですか?
-
C言語初心者 ポインタについて...
-
2次元配列を戻り値とする関数?
-
VB.netでRadioButtonを配列にし...
-
VB.NET 構造体の配列の検索機能...
-
【C言語】配列の中に配列を入れ...
-
インデックスが配列の境界外で...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語 配列の長さの上限
-
C# Listを使わずに2次元配列の...
-
配列を使わずに、変数名を動的...
-
配列で格納したものをmsgboxで...
-
【速いブラインドタッチ】手を...
-
配列をEraseしてもメモリが開放...
-
ExcelVBAで質問です。離れた二...
-
Redimした動的配列はEraseする...
-
C# 配列の変数宣言について。
-
複数の選択範囲の行番号を個別...
-
VBで構造体の配列を関数に渡す...
-
先頭アドレスとは何ですか?
-
配列の参照渡しで型が一致しま...
-
銀行ATMの数字キーの配列
-
配列を含む構造体の初期値について
-
C言語で特定列だけを抽出して配...
-
unsigned char配列への入力の仕方
-
VB.NET 構造体の配列の検索機能...
-
C++ vectorに配列をプッシュしたい
-
C言語初心者 構造体 課題について
おすすめ情報