[PHP]shuffle関数を完全解説!配列をランダムに並び替える方法

PHP

こんにちは!今回は、PHPの標準関数であるshuffle()について詳しく解説していきます。配列の要素をランダムに並び替える便利な関数です!

shuffle関数とは?

shuffle()関数は、配列の要素をランダムな順序に並び替える関数です。

元の配列を直接変更し(破壊的操作)、インデックスは0から振り直されます。クイズ、抽選、ゲームなどランダム性が必要な場面で活躍します!

基本的な構文

shuffle(array &$array): bool
  • $array: シャッフルする配列(参照渡し)
  • 戻り値: 成功時はtrue、失敗時はfalse

基本的な使用例

シンプルなシャッフル

// 数値の配列
$numbers = [1, 2, 3, 4, 5];

shuffle($numbers);

print_r($numbers);
/*
Array (
    [0] => 3
    [1] => 1
    [2] => 5
    [3] => 2
    [4] => 4
)
// 実行ごとに異なる順序になる
*/

文字列の配列をシャッフル

// 文字列の配列
$fruits = ['apple', 'banana', 'orange', 'grape'];

shuffle($fruits);

print_r($fruits);
/*
Array (
    [0] => grape
    [1] => apple
    [2] => orange
    [3] => banana
)
*/

インデックスの振り直し

// 連想配列のキーは失われる
$data = [
    'a' => 'Apple',
    'b' => 'Banana',
    'c' => 'Cherry'
];

shuffle($data);

print_r($data);
/*
Array (
    [0] => Banana
    [1] => Cherry
    [2] => Apple
)
// キー(a, b, c)は失われて0から振り直される
*/

複数回シャッフル

$cards = ['A', 'K', 'Q', 'J'];

echo "元の配列: " . implode(', ', $cards) . "\n";

for ($i = 1; $i <= 3; $i++) {
    shuffle($cards);
    echo "シャッフル{$i}: " . implode(', ', $cards) . "\n";
}
/*
元の配列: A, K, Q, J
シャッフル1: Q, A, J, K
シャッフル2: K, J, A, Q
シャッフル3: J, Q, K, A
*/

実践的な使用例

例1: カードゲームシステム

class CardDeck {
    private $cards = [];
    
    /**
     * デッキを初期化
     */
    public function __construct() {
        $this->reset();
    }
    
    /**
     * デッキをリセット
     */
    public function reset() {
        $suits = ['ハート', 'ダイヤ', 'クラブ', 'スペード'];
        $ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
        
        $this->cards = [];
        
        foreach ($suits as $suit) {
            foreach ($ranks as $rank) {
                $this->cards[] = "{$suit}の{$rank}";
            }
        }
        
        return $this;
    }
    
    /**
     * デッキをシャッフル
     */
    public function shuffle() {
        shuffle($this->cards);
        return $this;
    }
    
    /**
     * カードを引く
     */
    public function draw($count = 1) {
        return array_splice($this->cards, 0, $count);
    }
    
    /**
     * 残りのカード枚数
     */
    public function count() {
        return count($this->cards);
    }
    
    /**
     * すべてのカードを取得
     */
    public function getCards() {
        return $this->cards;
    }
    
    /**
     * 手札を配る
     */
    public function deal($players, $cardsPerPlayer) {
        $hands = [];
        
        for ($i = 0; $i < $players; $i++) {
            $hands["プレイヤー" . ($i + 1)] = $this->draw($cardsPerPlayer);
        }
        
        return $hands;
    }
    
    /**
     * カットする(デッキを分割して入れ替え)
     */
    public function cut($position = null) {
        if ($position === null) {
            $position = rand(1, count($this->cards) - 1);
        }
        
        $top = array_slice($this->cards, 0, $position);
        $bottom = array_slice($this->cards, $position);
        
        $this->cards = array_merge($bottom, $top);
        
        return $this;
    }
}

// 使用例
echo "=== カードゲーム ===\n";

$deck = new CardDeck();

// シャッフル
$deck->shuffle();

echo "デッキ枚数: {$deck->count()}枚\n";

// カードを5枚引く
$cards = $deck->draw(5);
echo "\n引いたカード:\n";
foreach ($cards as $card) {
    echo "  - {$card}\n";
}

echo "\n残り枚数: {$deck->count()}枚\n";

// 新しいデッキで4人に5枚ずつ配る
echo "\n=== 手札配布 ===\n";
$deck->reset()->shuffle();
$hands = $deck->deal(4, 5);

foreach ($hands as $player => $hand) {
    echo "\n{$player}:\n";
    foreach ($hand as $card) {
        echo "  - {$card}\n";
    }
}

echo "\n残り枚数: {$deck->count()}枚\n";

例2: クイズシステム

class QuizManager {
    /**
     * 問題をシャッフル
     */
    public static function shuffleQuestions($questions) {
        shuffle($questions);
        return $questions;
    }
    
    /**
     * 選択肢をシャッフル
     */
    public static function shuffleChoices($question) {
        $correctAnswer = $question['answer'];
        
        // 選択肢をシャッフル
        shuffle($question['choices']);
        
        // 正解のインデックスを更新
        $question['answer_index'] = array_search($correctAnswer, $question['choices']);
        
        return $question;
    }
    
    /**
     * すべての選択肢をシャッフル
     */
    public static function shuffleAllChoices($questions) {
        foreach ($questions as &$question) {
            $question = self::shuffleChoices($question);
        }
        
        return $questions;
    }
    
    /**
     * ランダムにN問選択
     */
    public static function selectRandomQuestions($questions, $count) {
        $shuffled = self::shuffleQuestions($questions);
        return array_slice($shuffled, 0, $count);
    }
    
    /**
     * 難易度別にランダム選択
     */
    public static function selectByDifficulty($questions, $easy, $medium, $hard) {
        $byDifficulty = [
            'easy' => [],
            'medium' => [],
            'hard' => []
        ];
        
        // 難易度別に分類
        foreach ($questions as $question) {
            $level = $question['difficulty'];
            $byDifficulty[$level][] = $question;
        }
        
        // 各難易度からランダムに選択
        $selected = [];
        
        if ($easy > 0) {
            shuffle($byDifficulty['easy']);
            $selected = array_merge($selected, array_slice($byDifficulty['easy'], 0, $easy));
        }
        
        if ($medium > 0) {
            shuffle($byDifficulty['medium']);
            $selected = array_merge($selected, array_slice($byDifficulty['medium'], 0, $medium));
        }
        
        if ($hard > 0) {
            shuffle($byDifficulty['hard']);
            $selected = array_merge($selected, array_slice($byDifficulty['hard'], 0, $hard));
        }
        
        // 最終的にシャッフル
        shuffle($selected);
        
        return $selected;
    }
    
    /**
     * クイズを生成
     */
    public static function generateQuiz($questions, $count = 10) {
        $quiz = self::selectRandomQuestions($questions, $count);
        $quiz = self::shuffleAllChoices($quiz);
        
        return $quiz;
    }
}

// 使用例
echo "=== クイズシステム ===\n";

$questions = [
    [
        'question' => 'PHPの開発者は?',
        'choices' => ['Rasmus Lerdorf', 'Guido van Rossum', 'Brendan Eich', 'James Gosling'],
        'answer' => 'Rasmus Lerdorf',
        'difficulty' => 'easy'
    ],
    [
        'question' => 'PHPはどの言語から影響を受けた?',
        'choices' => ['Perl', 'Python', 'Ruby', 'Java'],
        'answer' => 'Perl',
        'difficulty' => 'medium'
    ],
    [
        'question' => 'PHP 8.0の新機能は?',
        'choices' => ['JIT', 'Async/Await', 'Generics', 'Pattern Matching'],
        'answer' => 'JIT',
        'difficulty' => 'hard'
    ]
];

// 問題をシャッフル
echo "シャッフル前:\n";
foreach ($questions as $i => $q) {
    echo ($i + 1) . ". {$q['question']}\n";
}

$shuffled = QuizManager::shuffleQuestions($questions);

echo "\nシャッフル後:\n";
foreach ($shuffled as $i => $q) {
    echo ($i + 1) . ". {$q['question']}\n";
}

// 選択肢をシャッフル
echo "\n=== 選択肢シャッフル ===\n";
$question = $questions[0];
echo "元の選択肢:\n";
foreach ($question['choices'] as $i => $choice) {
    echo ($i + 1) . ". {$choice}\n";
}

$shuffledQuestion = QuizManager::shuffleChoices($question);
echo "\nシャッフル後:\n";
foreach ($shuffledQuestion['choices'] as $i => $choice) {
    $mark = ($i === $shuffledQuestion['answer_index']) ? ' ← 正解' : '';
    echo ($i + 1) . ". {$choice}{$mark}\n";
}

例3: 抽選システム

class LotterySystem {
    /**
     * 単純抽選(全員から指定人数を選ぶ)
     */
    public static function simpleDraw($participants, $winners) {
        shuffle($participants);
        return array_slice($participants, 0, $winners);
    }
    
    /**
     * 重み付き抽選
     */
    public static function weightedDraw($items, $count = 1) {
        $pool = [];
        
        foreach ($items as $item => $weight) {
            for ($i = 0; $i < $weight; $i++) {
                $pool[] = $item;
            }
        }
        
        shuffle($pool);
        
        $winners = [];
        $selected = [];
        
        foreach ($pool as $item) {
            if (!in_array($item, $selected)) {
                $winners[] = $item;
                $selected[] = $item;
                
                if (count($winners) >= $count) {
                    break;
                }
            }
        }
        
        return $winners;
    }
    
    /**
     * 複数回抽選(重複なし)
     */
    public static function multipleDraws($participants, $rounds) {
        $results = [];
        $remaining = $participants;
        
        for ($i = 1; $i <= $rounds; $i++) {
            if (empty($remaining)) {
                break;
            }
            
            shuffle($remaining);
            $winner = array_shift($remaining);
            
            $results["第{$i}回"] = $winner;
        }
        
        return $results;
    }
    
    /**
     * グループ抽選
     */
    public static function groupDraw($participants, $groupSize) {
        shuffle($participants);
        
        $groups = [];
        $groupNumber = 1;
        
        foreach (array_chunk($participants, $groupSize) as $group) {
            $groups["グループ{$groupNumber}"] = $group;
            $groupNumber++;
        }
        
        return $groups;
    }
    
    /**
     * ビンゴ番号生成
     */
    public static function generateBingoNumbers($max = 75) {
        $numbers = range(1, $max);
        shuffle($numbers);
        return $numbers;
    }
    
    /**
     * ペア抽選(ランダムペアリング)
     */
    public static function pairDraw($participants) {
        shuffle($participants);
        
        $pairs = [];
        $pairNumber = 1;
        
        for ($i = 0; $i < count($participants); $i += 2) {
            if (isset($participants[$i + 1])) {
                $pairs["ペア{$pairNumber}"] = [
                    $participants[$i],
                    $participants[$i + 1]
                ];
                $pairNumber++;
            } else {
                // 奇数の場合、最後の人は余り
                $pairs["余り"] = [$participants[$i]];
            }
        }
        
        return $pairs;
    }
}

// 使用例
echo "=== 抽選システム ===\n";

$participants = ['田中', '佐藤', '鈴木', '高橋', '伊藤', '渡辺', '山本', '中村'];

// 単純抽選(3名選出)
echo "単純抽選(3名選出):\n";
$winners = LotterySystem::simpleDraw($participants, 3);
foreach ($winners as $i => $winner) {
    echo ($i + 1) . "位: {$winner}\n";
}

// 重み付き抽選
echo "\n=== 重み付き抽選 ===\n";
$items = [
    '1等(旅行券)' => 1,
    '2等(商品券)' => 3,
    '3等(図書カード)' => 10,
    '参加賞' => 50
];

$result = LotterySystem::weightedDraw($items, 5);
echo "当選結果:\n";
foreach ($result as $i => $item) {
    echo ($i + 1) . ". {$item}\n";
}

// 複数回抽選
echo "\n=== 複数回抽選 ===\n";
$rounds = LotterySystem::multipleDraws($participants, 3);
foreach ($rounds as $round => $winner) {
    echo "{$round}: {$winner}\n";
}

// グループ分け
echo "\n=== グループ分け(3人ずつ) ===\n";
$groups = LotterySystem::groupDraw($participants, 3);
foreach ($groups as $groupName => $members) {
    echo "{$groupName}: " . implode(', ', $members) . "\n";
}

// ペア抽選
echo "\n=== ペア抽選 ===\n";
$pairs = LotterySystem::pairDraw($participants);
foreach ($pairs as $pairName => $members) {
    echo "{$pairName}: " . implode(' & ', $members) . "\n";
}

// ビンゴ
echo "\n=== ビンゴ番号生成 ===\n";
$bingoNumbers = LotterySystem::generateBingoNumbers(20);
echo "最初の10個: " . implode(', ', array_slice($bingoNumbers, 0, 10)) . "\n";

例4: プレイリスト・ランダム再生

class Playlist {
    private $songs = [];
    private $currentIndex = 0;
    private $shuffled = false;
    
    /**
     * プレイリストに曲を追加
     */
    public function add($song) {
        $this->songs[] = $song;
        return $this;
    }
    
    /**
     * 複数の曲を追加
     */
    public function addMany($songs) {
        $this->songs = array_merge($this->songs, $songs);
        return $this;
    }
    
    /**
     * シャッフル再生を有効化
     */
    public function enableShuffle() {
        $this->shuffled = true;
        shuffle($this->songs);
        $this->currentIndex = 0;
        return $this;
    }
    
    /**
     * シャッフル再生を無効化
     */
    public function disableShuffle() {
        $this->shuffled = false;
        $this->currentIndex = 0;
        return $this;
    }
    
    /**
     * 次の曲を取得
     */
    public function next() {
        if (empty($this->songs)) {
            return null;
        }
        
        $song = $this->songs[$this->currentIndex];
        
        $this->currentIndex++;
        
        // 最後まで行ったら最初に戻る
        if ($this->currentIndex >= count($this->songs)) {
            $this->currentIndex = 0;
            
            // シャッフルモードなら再シャッフル
            if ($this->shuffled) {
                shuffle($this->songs);
            }
        }
        
        return $song;
    }
    
    /**
     * 現在の曲を取得
     */
    public function current() {
        if (empty($this->songs)) {
            return null;
        }
        
        return $this->songs[$this->currentIndex];
    }
    
    /**
     * プレイリスト全体を取得
     */
    public function getAll() {
        return $this->songs;
    }
    
    /**
     * プレイリストをリセット
     */
    public function reset() {
        $this->currentIndex = 0;
        return $this;
    }
    
    /**
     * ランダムに曲を選択
     */
    public function random() {
        if (empty($this->songs)) {
            return null;
        }
        
        $randomIndex = array_rand($this->songs);
        return $this->songs[$randomIndex];
    }
}

// 使用例
echo "=== プレイリスト ===\n";

$playlist = new Playlist();

$songs = [
    '曲1: Hello World',
    '曲2: PHP Blues',
    '曲3: Code Symphony',
    '曲4: Debug Dance',
    '曲5: Function Funk'
];

$playlist->addMany($songs);

// 通常再生
echo "通常再生:\n";
for ($i = 0; $i < 7; $i++) {
    echo ($i + 1) . ". " . $playlist->next() . "\n";
}

// シャッフル再生
echo "\nシャッフル再生:\n";
$playlist->reset()->enableShuffle();
for ($i = 0; $i < 7; $i++) {
    echo ($i + 1) . ". " . $playlist->next() . "\n";
}

// ランダム選択
echo "\nランダム選択:\n";
for ($i = 0; $i < 3; $i++) {
    echo "  " . $playlist->random() . "\n";
}

例5: ゲーム要素

class GameHelper {
    /**
     * ダイスを振る
     */
    public static function rollDice($sides = 6, $count = 1) {
        $results = [];
        
        for ($i = 0; $i < $count; $i++) {
            $results[] = rand(1, $sides);
        }
        
        return $results;
    }
    
    /**
     * ランダムなアイテムを取得
     */
    public static function getRandomItem($items) {
        shuffle($items);
        return $items[0];
    }
    
    /**
     * ランダムなイベントを発生
     */
    public static function triggerRandomEvent($events) {
        shuffle($events);
        
        foreach ($events as $event) {
            // 確率チェック
            if (rand(1, 100) <= $event['probability']) {
                return $event;
            }
        }
        
        return null;
    }
    
    /**
     * ランダムな敵を出現
     */
    public static function spawnEnemy($enemies, $level) {
        // レベルに応じた敵をフィルタ
        $available = array_filter($enemies, function($enemy) use ($level) {
            return $enemy['min_level'] <= $level && $enemy['max_level'] >= $level;
        });
        
        if (empty($available)) {
            return null;
        }
        
        shuffle($available);
        return $available[0];
    }
    
    /**
     * 宝箱の中身をランダム生成
     */
    public static function generateTreasure($itemPool, $count = 3) {
        shuffle($itemPool);
        return array_slice($itemPool, 0, $count);
    }
    
    /**
     * ランダムマップ生成
     */
    public static function generateMap($width, $height, $tiles) {
        $map = [];
        
        for ($y = 0; $y < $height; $y++) {
            for ($x = 0; $x < $width; $x++) {
                shuffle($tiles);
                $map[$y][$x] = $tiles[0];
            }
        }
        
        return $map;
    }
    
    /**
     * ランダムクエスト生成
     */
    public static function generateQuest($templates, $objectives, $rewards) {
        shuffle($templates);
        shuffle($objectives);
        shuffle($rewards);
        
        return [
            'title' => $templates[0]['title'],
            'description' => $templates[0]['description'],
            'objective' => $objectives[0],
            'reward' => $rewards[0]
        ];
    }
}

// 使用例
echo "=== ゲームヘルパー ===\n";

// ダイス
echo "ダイスロール(2個の6面ダイス):\n";
$diceResults = GameHelper::rollDice(6, 2);
echo "出目: " . implode(', ', $diceResults) . " (合計: " . array_sum($diceResults) . ")\n";

// ランダムアイテム
echo "\n=== ランダムアイテム ===\n";
$items = ['ポーション', '魔法の杖', '鋼の剣', '革の鎧', '金貨'];
echo "獲得アイテム: " . GameHelper::getRandomItem($items) . "\n";

// イベント発生
echo "\n=== イベント発生 ===\n";
$events = [
    ['name' => '宝箱発見', 'probability' => 30],
    ['name' => '敵遭遇', 'probability' => 50],
    ['name' => 'NPC出現', 'probability' => 20]
];

$event = GameHelper::triggerRandomEvent($events);
echo "発生イベント: " . ($event ? $event['name'] : 'なし') . "\n";

// 敵出現
echo "\n=== 敵出現 ===\n";
$enemies = [
    ['name' => 'スライム', 'min_level' => 1, 'max_level' => 5],
    ['name' => 'ゴブリン', 'min_level' => 3, 'max_level' => 10],
    ['name' => 'ドラゴン', 'min_level' => 15, 'max_level' => 99]
];

$playerLevel = 5;
$enemy = GameHelper::spawnEnemy($enemies, $playerLevel);
echo "レベル{$playerLevel}で出現: " . ($enemy ? $enemy['name'] : 'なし') . "\n";

// 宝箱
echo "\n=== 宝箱の中身 ===\n";
$treasure = GameHelper::generateTreasure($items, 3);
echo "獲得品: " . implode(', ', $treasure) . "\n";

// クエスト生成
echo "\n=== ランダムクエスト ===\n";
$templates = [
    ['title' => '討伐依頼', 'description' => 'モンスターを倒せ'],
    ['title' => '収集依頼', 'description' => 'アイテムを集めろ']
];
$objectives = ['10体倒す', '5個集める', 'ボスを倒す'];
$rewards = ['1000ゴールド', 'レアアイテム', '経験値2倍'];

$quest = GameHelper::generateQuest($templates, $objectives, $rewards);
print_r($quest);

例6: ランダムデータジェネレーター

class RandomDataGenerator {
    /**
     * ランダムなパスワード生成
     */
    public static function generatePassword($length = 12) {
        $chars = str_split('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*');
        shuffle($chars);
        
        return substr(implode('', $chars), 0, $length);
    }
    
    /**
     * ランダムな色を生成
     */
    public static function generateColor() {
        $colors = ['赤', '青', '緑', '黄', '紫', 'オレンジ', 'ピンク', '黒', '白', '灰'];
        shuffle($colors);
        return $colors[0];
    }
    
    /**
     * ランダムな名前を生成
     */
    public static function generateName() {
        $firstNames = ['太郎', '次郎', '花子', '美咲', '健太', '愛子'];
        $lastNames = ['田中', '佐藤', '鈴木', '高橋', '伊藤', '渡辺'];
        
        shuffle($firstNames);
        shuffle($lastNames);
        
        return $lastNames[0] . ' ' . $firstNames[0];
    }
    
    /**
     * ランダムな日付を生成
     */
    public static function generateDate($startYear = 2020, $endYear = 2026) {
        $year = rand($startYear, $endYear);
        $month = rand(1, 12);
        $day = rand(1, 28); // 簡略化のため28日まで
        
        return sprintf('%04d-%02d-%02d', $year, $month, $day);
    }
    
    /**
     * ランダムなダミーデータ生成
     */
    public static function generateDummyData($count = 10) {
        $data = [];
        
        for ($i = 0; $i < $count; $i++) {
            $data[] = [
                'id' => $i + 1,
                'name' => self::generateName(),
                'color' => self::generateColor(),
                'date' => self::generateDate(),
                'score' => rand(0, 100)
            ];
        }
        
        return $data;
    }
    
    /**
     * ランダムなテキストを生成
     */
    public static function generateLoremIpsum($wordCount = 50) {
        $words = ['lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 
                  'adipiscing', 'elit', 'sed', 'do', 'eiusmod', 'tempor'];
        
        $text = [];
        for ($i = 0; $i < $wordCount; $i++) {
            shuffle($words);
            $text[] = $words[0];
        }
        
        return implode(' ', $text);
    }
}

// 使用例
echo "=== ランダムデータ生成 ===\n";

// パスワード
echo "ランダムパスワード: " . RandomDataGenerator::generatePassword(16) . "\n";

// 色
echo "ランダム色: " . RandomDataGenerator::generateColor() . "\n";

// 名前
echo "ランダム名前:\n";
for ($i = 0; $i < 3; $i++) {
    echo "  " . RandomDataGenerator::generateName() . "\n";
}

// ダミーデータ
echo "\n=== ダミーデータ ===\n";
$data = RandomDataGenerator::generateDummyData(5);
foreach ($data as $row) {
    echo "ID:{$row['id']} {$row['name']} ({$row['color']}) - {$row['date']} - {$row['score']}点\n";
}

キーを保持したシャッフル

// 連想配列のキーを保持したまま値をシャッフルしたい場合
function shuffle_assoc(&$array) {
    $keys = array_keys($array);
    shuffle($keys);
    
    $new = [];
    foreach ($keys as $key) {
        $new[$key] = $array[$key];
    }
    
    $array = $new;
    return true;
}

// 使用例
$data = [
    'a' => 'Apple',
    'b' => 'Banana',
    'c' => 'Cherry'
];

shuffle_assoc($data);
print_r($data);
/*
Array (
    [b] => Banana
    [c] => Cherry
    [a] => Apple
)
// キーは保持されたまま順序が変わる
*/

まとめ

shuffle()関数の特徴をまとめると:

できること:

  • 配列をランダムに並び替え
  • ゲーム、クイズ、抽選に活用
  • ランダム性が必要な処理

推奨される使用場面:

  • カードゲーム
  • クイズの問題順・選択肢
  • 抽選システム
  • プレイリストのシャッフル
  • ランダムイベント

特徴:

  • 元の配列を直接変更(破壊的)
  • キーは0から振り直される
  • 擬似乱数を使用

注意点:

  • 連想配列のキーは失われる
  • 暗号学的に安全な乱数ではない
  • 実行ごとに結果が変わる

関連関数:

  • array_rand(): ランダムなキーを取得
  • rand(): ランダムな整数
  • random_int(): 暗号学的に安全な乱数
  • mt_rand(): より高速な乱数

使い分け:

// 配列全体をシャッフル
shuffle($array);

// ランダムに1つ選ぶ
$key = array_rand($array);
$value = $array[$key];

// ランダムな整数
$number = rand(1, 100);

// 暗号学的に安全な乱数
$secure = random_int(1, 100);

shuffle()は、配列をランダムに並び替える最もシンプルな方法です。ゲーム、クイズ、抽選など、ランダム性が必要な場面で積極的に活用しましょう!

タイトルとURLをコピーしました