質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.32%

条件付きの乱数の作り方を質問させてください

解決済

回答 5

投稿

  • 評価
  • クリップ 0
  • VIEW 1,937

sukiyakigozens

score 28

条件付きの乱数の作り方を質問させてください。
スマホのパズルゲームを製作しております。(初心者、unity5.6使用)

10ピースのパズルがあり、
ゲーム開始時に、パズルピースをシャッフルします。


パズルピース
|1|2|3|4|5|6|7|8|9|10|

スロット
|A|B|C|D|E|F|G|H|I|J|


スロット「A」に対しては「1」が正解でスロット「B」に対しては「2」が正解とします。(同様に「J」まで続く)
(スロットは固定で、パズルピースがシャッフルされます)

その際に問題になる条件が、
シャッフル時に、偶然に正解位置に割り振られる事を避けたい、という条件です。
つまり、スロット「A」には「1」以外を、スロット「B」には「2」以外を割り振りたいです。
(もちろん数字の重複も避けたいです)

この場合、
下記のような、正解位置を避けたシャッフルパターンを手動で作るのがいいでしょうか?
例えば
|10|6|2|1|7|8|6|4|5|9|
のような数列をあらかじめ5パターンくらい用意して、
それをランダムに使い回すのがいいでしょうか?
(自分で作った数列5パターンをシーンロード時に
5パターンの中からランダムにどれかを選択し使用する)

または

正解位置を避けながら、なおかつ同じ数字の重複も避けたの乱数の制作はC#で可能でしょうか?

※シーンが進むにつれてパズルのピースは増えて50個~60個になる予定です。


現状のプログラムの状態
1 まずシャッフルしたパズルピースを実際にシーン上に配置
2 シャッフル時に偶然に正解位置に置かれていないかを、コリジョン判定を利用して調べる
3 偶然に正解があった場合はシーンをリロードして再シャッフル
(全部のピースが完全に正解位置を避けるまで何度もシャッフルする)

というプログラムを作ったのですが、
この方法ですとスマホの実機上で実行した場合、
特にパズルピースの数が多い場合に、実機のメモリの消費が激しいようなので、
メモリ消費の少ない方法を探しております。

ご教授よろしくおねがいいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

checkベストアンサー

+1

動作未検証&どこまでメモリ消費するか分かりませんが、以下はいかがでしょう?

  1. 最初に正解のスロットにピースをはめる。
  2. そこから1スロット毎に、別のランダムなスロットとピースを入れ替える。
    2-1. ただし入れ替えた結果どちらかのスロットが正解となる時は次のスロットを入れ替え先とする。次のスロットも正解になる場合は更に次のスロットを入れ替え先とする。
    2-2. 最後までスロットが選択出来ず、自分のスロットに正解ではないピースが入っていれば2へ戻る。
  3. 並び替え後の配列の通りにオブジェクトを配置する。

動作例

・開始時(正解):分かりやすいように数を減らしています
|A-1|B-2|C-3|

・スロットAのピースと、別のランダムなスロット(Cが選ばれたとする)のピースを入れ替え
(どちらも正解ピースではないことをチェック→問題無し)
|A-3|B-2|C-1|

・スロットBのピースと、別のランダムなスロット(Aが選ばれたとする)のピースを入れ替え
(どちらも正解ピースではないことをチェック→問題無し)
|A-2|B-3|C-1|

・スロットCのピースと、別のランダムなスロット(Aが選ばれたとする)のピースを入れ替え
Aと入れ替えるとAが正解になってしまうので次のスロットを見る
→Bと入れ替えるとCが正解になってしまうので次のスロットを見る
→最後のスロットまで来たが、スロットC(自分自身)が正解ではないので問題無し(入れ替えはしない)
|A-2|B-3|C-1|

・全てのスロットを入れ替え終わったのでループ処理終了

・出来上がった順にピースを配置すればOK
|2|3|1|


※速度や負荷を考慮すると入れ替え前に「現在のスロットに入っているピースが正解ではない場合、入れ替えを行わない」という判定を入れてもいいかもしれません。
※下手にやると「終了しない処理」になりそうなのでご注意ください。「n回ループしてたら最初からやり直し」とか必要かも。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/12 10:47

    BAありがとうございます。余談的に。

    オブジェクト名判定で単純にやるなら
    ピースの名前:piece1、piece2……
    スロットの名前:slot_piece1、slot_piece2……
    みたいにしておき、

    if ("slot_"+other.gameObject.name == gameObject.name) {
    //正解時の動作
    }

    という形でも出来ると思います。
    (これならGameObject.Find("piece1")で1つしかヒットしない)

    色々書いてしまいましたが「とりあえず動けばいいんだよ」等の都合があると思うので、検討の上お好きな方法でやってもらえればいいと思います。

    キャンセル

  • 2017/10/12 18:28

    さらなるご説明ありがとうございます。
    if ("slot_"+other.gameObject.name == gameObject.name) をやってみましたが動作しませんでした。
    if (other.gameObject.name == gameObject.name && other.gameObject.tag == "slot")
    との二段階での判定なら動きました。できましたら同名オブジェクトは避けてコリジョン判定を"slot_"+のように”同じ名前を含む”で動作できるとベストなんですが無理のようです。

    キャンセル

  • 2017/10/17 20:47

    申し訳ありません。上記に当方が「無理のようです」と書きましたが、
    コリジョン判定条件の「名前の一部が同じ場合に命令実行」は
    >>この指定方法>> if ("slot_"+other.gameObject.name == gameObject.name)
    sakura_hanaさんのご指摘どおり、作動しました。すいませんでした。ご教授ありがとうございました。

    キャンセル

+1

完全順列(derangement)というやつですね。

ランダムなderangementの生成はこの質問で紹介されてる論文が参考になるのではないでしょうか。
https://math.stackexchange.com/questions/302057/generating-a-random-derangement

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/12 10:00

    ご返答ありがとうござます。

    キャンセル

+1

正解あり

正解があったピースと隣のピースを入れ替える

再評価

というのはどうでしょう。
交換された2つの箇所は必ず正解でない組み合わせになるはず。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/11 20:36

    雑すぎたので補足
     普通にランダムリストを作る
     ↓
    ┌評価
    ↑↓
    │正解があれば、正解のピースと隣のピースを入れ替える
    └┤
     ↓
     正解がなくなったら評価終了

    片方が正解の場合、隣合うもうひとつの値と入れ替えると、必ず両方とも不正解となる。
    そのためコストは
    ・1回分のランダムリスト生成
    ・リストの要素が60として最大30回の位置交換

    キャンセル

  • 2017/10/12 10:00

    ご返答ありがとうござます。

    キャンセル

0

追記:以下は回答者の勘違いにより的を外した内容になっています。読み飛ばしてくださるようお願いします。

次のようなアルゴリズムではダメでしょうか。 

n個のスロットがあるとする。 
m<=1, m=>nの範囲で以下を繰返す。

  1. 乱数を1からn-1までの間で生成する。これをrとする。
  2. m==r ならば r=r+1とする。 
  3. スロットmの値はrとする。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/12 00:10

    ご返答ありがとうございます。
    すいませんが、もう少し詳しく教えていただけませんでしょうか?
    (初心者なもので)

    例えばスロット数が4つの場合
    n=3
    rは1、2、3を使った乱数
    mは繰り返しの数?でとの理解で間違ってないでしょうか?

    キャンセル

  • 2017/10/12 03:31

    申し訳ないです。前提条件を勘違いしていました。私の回答は役に立たないと思います。

    キャンセル

  • 2017/10/12 05:34

    とんでもないです。ご返答ありがとうございます。私がもっと知識があれば上記の方法でもできそうな気がします。

    キャンセル

0

C#やったことないんで的外れになるかもしれませんが・・・
あとシャッフルというのが視覚的効果を伴うものであれば役に立たないかもしれません。

1.一旦パズルピースを未使用リストみたいなのに格納
2.頭から順にシャッフルしたパズルピースを決めていくとして、
3.その位置の正解が未使用リストに残っている場合は、そのピースをリストから退避
4.残ったリストの範囲で乱数を取得してピースを決定
5.位置が決定されたピースは未使用リストから削除
6.退避していたピースがあれば未使用リストに戻す
7.次の位置へ
8.3に戻る

これを全てピースが決まるまで繰り返します。

C#でリスト内のオブジェクトを特定する際にかかるコストがわからないので
もしかしたら処理時間が大変なことになるかもしれませんが・・・(60程度問題ないですよね?)

メモリ消費についてはわからないです。
繰り返し処理内で変にインスタンスを生成しなければそこまで大量に消費することはないと思いますが・・・

突っ込みどころがあれば教えてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/12 18:05

    ご返答ありがとうございます。初心者で上記の方法のコードが思い浮ばないのですが、できるのではと思います。ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る