ページ 11

std::random_shuffleで二次元配列のシャッフルがうまくいきません。

Posted: 2014年1月12日(日) 03:13
by Black_cc
動的確保した二次元配列の中身をstd::random_shuffle関数でシャッフルしたいのですがうまくいかずに困っています。
今マインスイーパーを作っていて地雷設置の処理で使おうと思っているのでその部分の抜粋だと思ってみてくださると幸いです。
どこで困っているかと言うと、以下のソースを実行して頂ければわかると思うのですが「9」を全体的にバラバラにシャッフルしたいのに列の状態を保持したままシャッフルしているという事です。

コード:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <stdlib.h>
#include <time.h>
#include <vector>
using namespace std;

struct Mine{
	int a;
};

vector<vector<Mine>> vec(9); 

void main()
{
	for(int i=0; i<9; i++){
		vec[i].resize(9);
	}

	for(int i=0; i<10; i++){
		mine.a=9;
		if(i<=8) vec[0][i].a=9;
		if(i>=9) vec[1][i-9].a=9;
	}

	puts("シャッフル前");
	for(int r=0; r<9; r++){
		for(int c=0; c<9; c++){
			printf("%d",vec[r][c].a);
		}
		printf("\n");
	}
	printf("\n");
	printf("\n");
	puts("シャッフル後");
    
	srand((unsigned)time(NULL));
	for(int i=0; i<9; i++){
		random_shuffle(vec.begin(),vec.end());
	}
	for(int r=0; r<9; r++){
		for(int c=0; c<9; c++){
			printf("%d",vec[r][c].a);
		}
		printf("\n");
	}

}

修正

Posted: 2014年1月12日(日) 03:27
by Black_cc
22行目のmine.a=9は消し忘れなので気にしないでください。

Re: std::random_shuffleで二次元配列のシャッフルがうまくいきません。

Posted: 2014年1月12日(日) 06:00
by shirokosi
はじめまして。

std::random_shuffle について調べてみたのですが、
これは基本的には一次元配列として表現可能なデータ構造にしか対応していないようです。

コード:

 vector<vector<Mine>> vec;
 
上記のvecは vector<Mine> の一次元配列になります。
vector<Mine> は Mine の一次元配列になります。

なので、

コード:

 random_shuffle(vec.begin(),vec.end());
これは vector<Mine> の型をシャッフルしているので、
一行を丸ごとシャッフルしていると考えてよいでしょう。

21行からのコードを下記のように変更すると確認できるかと思います。

コード:

for(int i=0; i<9; i++){
      vec[i][0].a=i;
	  vec[i][4].a=std::rand()%9;
    }
というわけで、
std::random_shuffle と vector を使ってやりたいことをやるとしたら、
vector<Mine>を2次元配列として使うやり方しかないと思われます。

以下テストコードです。

コード:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <stdlib.h>
#include <time.h>
#include <vector>
#include <conio.h>
using namespace std;
 
struct Mine{
    int a;
};

vector<Mine> vec;
const int w = 9; // 幅(横)
const int h = 9; // 高さ(縦)

int main()
{
	vec.resize(w*h);

	// 配列内の値を初期化
	for(int r=0; r<h; r++) { // 縦
		for(int c=0; c<w; c++) { // 横
			if(r==0) vec[ r*h + c].a =9;
			else     vec[ r*h + c].a =0;
		}
	}

	puts("シャッフル前");
    for(int r=0; r<9; r++){
        for(int c=0; c<9; c++){
            printf("%d",vec[r*h + c].a);
        }
        printf("\n");
    }
	/*
	printf("表示方法2\n");
	for(int i=0; i<vec.size(); i++){
		if( !(i%w) ) printf("\n");
		printf("%d", vec[i].a);
	}*/

	// シャッフルします
	srand((unsigned)time(NULL));
	random_shuffle(vec.begin(),vec.end());

	printf("\n");
    printf("\n");
    puts("シャッフル後");

	for(int r=0; r<9; r++){
        for(int c=0; c<9; c++){
            printf("%d",vec[r*h + c].a);
        }
        printf("\n");
    }
	/*
	for(int i=0; i<vec.size(); i++){
		if( !(i%w) ) printf("\n");
		printf("%d", vec[i].a);
	}*/

	//_getch();
	return 0;
}
 

解決しました。

Posted: 2014年1月12日(日) 12:10
by Black_cc
shirokosiさん、とてもわかりやすくご丁寧な回答ありがとうございます。
アドバイスされた通り一次元の配列を使っていこうと思います。
また、機会がありましたらよろしくお願いします。