[前へ]   [目次へ]   [次へ]

やり方を考えてみる(2)(C/C++)


今回は、「一枚ずつ抜き取ってみる」方法を配列一個で実現する方法を考えてみます。
配列一個でこの方法を使うには、結果配列と最初のカード配列を共有する必要があります。

ここで注目するのは結果配列が一枚増える時、カード配列からは一枚減るということです。
つまり、使わなくなった配列要素を結果配列の要素として用いることで配列一個で処理できます。

というわけで、書き換えていきます。

まず、結果配列と元配列を統合して、配列定義を一個にします。
int src[10]={0,1,2,3,4,5,6,7,8,9},cards[10];
               ↓
int cards[10]={0,1,2,3,4,5,6,7,8,9};


次に、配列を用いている部分も同様に訂正します。
n=rand()%srccnt;
cards[10-srccnt]=src[n];
src[n]=src[srccnt-1];
               ↓
n=rand()%srccnt;
cards[10-srccnt]=cards[n];
cards[n]=cards[srccnt-1];


これだけだとカード配列と結果配列ともに cards[0] から割り当ててしまっているので、
結果配列を cards[9] からの空き領域を使うように変更します。

cards[10-srccnt]=src[n];
               ↓
cards[srccnt-1]=src[n];


ではここまでで一度動かしてみましょう。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
   
int cards[10]={0,1,2,3,4,5,6,7,8,9};
   
int n,srccnt;
   
int i;
   
   srand(time(NULL));
   
for(srccnt=10;srccnt;srccnt--){
      n=rand()%srccnt;
      cards[srccnt-1]=cards[n];
//(1)
      
cards[n]=cards[srccnt-1];
   }
   
//どれくらいシャッフルできているか、確認してみます。
   
for(i=0;i<10;i++)printf("cards[%d]:%d\n",i,cards[i]);
   
//終了待ち
   
getchar();
   
return 0;
実行例:
cards[0]:0
cards[1]:1
cards[2]:2
cards[3]:0
cards[4]:2
cards[5]:2
cards[6]:1
cards[7]:3
cards[8]:8
cards[9]:7


おやおや、結果がおかしくなりました。
シャッフルは12〜16行でやってるので原因はこの中ですね。
さっきまで動いていたものが動かなくなった場合はまず疑うべきは当然編集した部分です。
時々、全然異なる場所がおかしくなることがあるのですが、
その場合も当該部分はもちろん、編集した部分が原因の可能性も高かったりします。

コードを編集する時はできるだけこまめに実行して異常を起こしていないか確認しておいた方が、
結果的にバグ取りが早く終わることが多いので覚えておいてください。

さて、今回のバグを考えましょう。
ようは(1)12行と13行の
cards[srccnt-1]=cards[n];
cards[n]=cards[srccnt-1];

なのですが、まず12行目、まだ cards[srccnt-1] には有効な番号が格納されている状態です。
それなのに代入してしまっているので、値が失われてしまったわけです。

ここを正しく書き直します。
n2=cards[srccnt-1];
cards[srccnt-1]=cards[n];
cards[n]=n2;


もう一度、動かしてみましょう。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
   
int cards[10]={0,1,2,3,4,5,6,7,8,9};
   
int n,n2,srccnt;
   
int i;
   
   srand(time(NULL));
   
for(srccnt=10;srccnt;srccnt--){
      n=rand()%srccnt;
      n2=cards[srccnt-1];
      cards[srccnt-1]=cards[n];
      cards[n]=n2;
   }
   
//どれくらいシャッフルできているか、確認してみます。
   
for(i=0;i<10;i++)printf("cards[%d]:%d\n",i,cards[i]);
   
//終了待ち
   
getchar();
   
return 0;
実行例:
cards[0]:9
cards[1]:2
cards[2]:0
cards[3]:8
cards[4]:7
cards[5]:5
cards[6]:1
cards[7]:6
cards[8]:4
cards[9]:3


今度は正しくシャッフルできています。
このように、プログラムがわずかにおかしいだけでも結果が劇的におかしくなってしまうことはよくあります。
派手に異常を起こしたとしても、冷静に原因を探すように心がけてください。


さて、今回のコードは何をしているのかを整理してみます。

配列と変数を定義します。
int cards[10]={0,1,2,3,4,5,6,7,8,9};
int n,n2,srccnt;


(1)山札がある限りループさせます。
srccnt は山札の残数です。
for(srccnt=10;srccnt;srccnt--)
(2)乱数を使って要素番号を生成します。
n=rand()%srccnt;
(3)山札の最後の要素と乱数で指定された要素を交換します。
n2=cards[srccnt-1];
cards[srccnt-1]=cards[n];
cards[n]=n2;


若干気付きにくい方式ですが、整理してみると案外あっさりしています。
全体を繰り返す場合も単純にループさせればよく、扱いやすいものになりました。

次回は、 第47回 のゲームを改造していきます。

[前へ]   [目次へ]   [次へ]

プログラミング講座 総合目次

最終更新 2008/10/17