[PHP]rand関数の使い方を徹底解説!乱数生成の基本をマスター

PHP

PHPでランダムな値が必要な場面は数多くあります。ゲーム開発、サンプリング、シャッフル、ランダムな色の生成など、様々な用途で乱数が活躍します。この記事では、PHPのrand関数について、基本的な使い方から実践的な活用方法まで詳しく解説していきます。

rand関数とは?

randは、疑似乱数を生成する関数です。指定した範囲内のランダムな整数を返します。

基本的な構文

int rand()
int rand(int $min, int $max)

パラメータ:

  • $min: 最小値(省略可)
  • $max: 最大値(省略可)

戻り値:

  • 引数なし: 0からgetrandmax()の間の整数
  • 引数あり: $minから$maxの間の整数

重要な注意:

  • rand()は暗号学的に安全ではありません
  • セキュリティが必要な場合はrandom_int()を使用してください

基本的な使い方

シンプルな乱数生成

<?php
// 0からgetrandmax()までの乱数
$random1 = rand();
echo "ランダムな値: {$random1}\n";

// 1から10までの乱数
$random2 = rand(1, 10);
echo "1-10の乱数: {$random2}\n";

// 1から100までの乱数
$random3 = rand(1, 100);
echo "1-100の乱数: {$random3}\n";

// 0または1(コイントス)
$coin = rand(0, 1);
echo "コイントス: " . ($coin ? '表' : '裏') . "\n";
?>

最大値の確認

<?php
// システムの最大乱数値を取得
$max = getrandmax();
echo "最大乱数値: {$max}\n";

// Windowsでは32767、Unix系では通常2147483647
?>

randとmt_randの違い

比較表

項目rand()mt_rand()random_int()
速度遅い速いやや遅い
品質低い高い最高
セキュリティ
推奨用途非推奨一般用途セキュリティ用途
<?php
// rand: 古い線形合同法
$r1 = rand(1, 100);

// mt_rand: メルセンヌ・ツイスター(推奨)
$r2 = mt_rand(1, 100);

// random_int: 暗号学的に安全(セキュリティ用途)
$r3 = random_int(1, 100);

echo "rand: {$r1}\n";
echo "mt_rand: {$r2}\n";
echo "random_int: {$r3}\n";
?>

使い分けの指針

<?php
// ❌ セキュリティ用途でrand()を使用
$token = rand(); // 推測可能なので危険

// ✅ セキュリティ用途ではrandom_int()
$token = random_int(100000, 999999);

// ✅ 一般的な用途ではmt_rand()(より高速・高品質)
$diceRoll = mt_rand(1, 6);
?>

実践的な使用例

1. ゲーム開発

<?php
/**
 * サイコロゲームクラス
 */
class DiceGame {
    
    /**
     * サイコロを振る
     */
    public static function rollDice($sides = 6) {
        return rand(1, $sides);
    }
    
    /**
     * 複数のサイコロを振る
     */
    public static function rollMultipleDice($count, $sides = 6) {
        $results = [];
        $total = 0;
        
        for ($i = 0; $i < $count; $i++) {
            $roll = self::rollDice($sides);
            $results[] = $roll;
            $total += $roll;
        }
        
        return [
            'rolls' => $results,
            'total' => $total,
            'average' => $total / $count
        ];
    }
    
    /**
     * クリティカルヒット判定
     */
    public static function isCriticalHit($chance = 10) {
        $roll = rand(1, 100);
        return $roll <= $chance;
    }
    
    /**
     * ランダムな敵を選択
     */
    public static function randomEnemy($enemies) {
        $index = array_rand($enemies);
        return $enemies[$index];
    }
    
    /**
     * ランダムなアイテムをドロップ
     */
    public static function dropItem($dropTable) {
        $total = array_sum(array_column($dropTable, 'chance'));
        $roll = rand(1, $total);
        
        $current = 0;
        foreach ($dropTable as $item) {
            $current += $item['chance'];
            if ($roll <= $current) {
                return $item;
            }
        }
        
        return null;
    }
}

// 使用例
echo "=== サイコロゲーム ===\n";
echo "1つのサイコロ: " . DiceGame::rollDice() . "\n";

$result = DiceGame::rollMultipleDice(3);
echo "3つのサイコロ: " . implode(', ', $result['rolls']) . "\n";
echo "合計: {$result['total']}\n";
echo "平均: {$result['average']}\n\n";

echo "=== クリティカルヒット ===\n";
for ($i = 0; $i < 5; $i++) {
    $critical = DiceGame::isCriticalHit(20);
    echo "攻撃" . ($i + 1) . ": " . ($critical ? "クリティカル!" : "通常") . "\n";
}

echo "\n=== ランダムな敵 ===\n";
$enemies = ['スライム', 'ゴブリン', 'ドラゴン', 'オーク'];
$enemy = DiceGame::randomEnemy($enemies);
echo "出現した敵: {$enemy}\n";

echo "\n=== アイテムドロップ ===\n";
$dropTable = [
    ['name' => 'ポーション', 'chance' => 50],
    ['name' => 'レアソード', 'chance' => 10],
    ['name' => '金貨', 'chance' => 30],
    ['name' => 'なし', 'chance' => 10]
];

for ($i = 0; $i < 5; $i++) {
    $item = DiceGame::dropItem($dropTable);
    echo "ドロップ" . ($i + 1) . ": {$item['name']}\n";
}
?>

2. ランダムデータ生成

<?php
/**
 * ランダムデータジェネレータークラス
 */
class RandomDataGenerator {
    
    /**
     * ランダムな名前を生成
     */
    public static function randomName() {
        $firstNames = ['太郎', '花子', '次郎', '美咲', '健太', '由美'];
        $lastNames = ['山田', '佐藤', '田中', '鈴木', '高橋', '渡辺'];
        
        return $lastNames[array_rand($lastNames)] . ' ' . 
               $firstNames[array_rand($firstNames)];
    }
    
    /**
     * ランダムなメールアドレスを生成
     */
    public static function randomEmail() {
        $domains = ['example.com', 'test.com', 'sample.jp', 'demo.net'];
        $username = 'user' . rand(1000, 9999);
        $domain = $domains[array_rand($domains)];
        
        return "{$username}@{$domain}";
    }
    
    /**
     * ランダムな電話番号を生成
     */
    public static function randomPhone() {
        $area = rand(10, 99);
        $exchange = rand(1000, 9999);
        $number = rand(1000, 9999);
        
        return sprintf('0%d-%d-%d', $area, $exchange, $number);
    }
    
    /**
     * ランダムな日付を生成
     */
    public static function randomDate($startDate, $endDate) {
        $startTimestamp = strtotime($startDate);
        $endTimestamp = strtotime($endDate);
        
        $randomTimestamp = rand($startTimestamp, $endTimestamp);
        
        return date('Y-m-d', $randomTimestamp);
    }
    
    /**
     * ランダムな色コードを生成
     */
    public static function randomColor() {
        $r = rand(0, 255);
        $g = rand(0, 255);
        $b = rand(0, 255);
        
        return sprintf('#%02X%02X%02X', $r, $g, $b);
    }
    
    /**
     * ランダムなパスワードを生成(セキュリティ用途には不適)
     */
    public static function randomPassword($length = 8) {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $password = '';
        
        for ($i = 0; $i < $length; $i++) {
            $password .= $chars[rand(0, strlen($chars) - 1)];
        }
        
        return $password;
    }
    
    /**
     * ランダムなIPアドレスを生成
     */
    public static function randomIPAddress() {
        return sprintf(
            '%d.%d.%d.%d',
            rand(1, 255),
            rand(0, 255),
            rand(0, 255),
            rand(1, 255)
        );
    }
    
    /**
     * テストユーザーデータを生成
     */
    public static function generateTestUser() {
        return [
            'id' => rand(1000, 9999),
            'name' => self::randomName(),
            'email' => self::randomEmail(),
            'phone' => self::randomPhone(),
            'birth_date' => self::randomDate('1970-01-01', '2005-12-31'),
            'registered_at' => self::randomDate('2020-01-01', date('Y-m-d')),
            'score' => rand(0, 100),
            'active' => (bool)rand(0, 1)
        ];
    }
}

// 使用例
echo "=== ランダムデータ生成 ===\n";
echo "名前: " . RandomDataGenerator::randomName() . "\n";
echo "メール: " . RandomDataGenerator::randomEmail() . "\n";
echo "電話: " . RandomDataGenerator::randomPhone() . "\n";
echo "生年月日: " . RandomDataGenerator::randomDate('1980-01-01', '2000-12-31') . "\n";
echo "色: " . RandomDataGenerator::randomColor() . "\n";
echo "パスワード: " . RandomDataGenerator::randomPassword(12) . "\n";
echo "IP: " . RandomDataGenerator::randomIPAddress() . "\n\n";

echo "=== テストユーザーデータ ===\n";
$users = [];
for ($i = 0; $i < 3; $i++) {
    $users[] = RandomDataGenerator::generateTestUser();
}

foreach ($users as $i => $user) {
    echo "\nユーザー" . ($i + 1) . ":\n";
    foreach ($user as $key => $value) {
        $displayValue = is_bool($value) ? ($value ? 'true' : 'false') : $value;
        echo "  {$key}: {$displayValue}\n";
    }
}
?>

3. 配列のシャッフルとサンプリング

<?php
/**
 * 配列操作クラス
 */
class ArrayRandomizer {
    
    /**
     * 配列をシャッフル(Fisher-Yatesアルゴリズム)
     */
    public static function shuffle($array) {
        $result = $array;
        $count = count($result);
        
        for ($i = $count - 1; $i > 0; $i--) {
            $j = rand(0, $i);
            
            // 要素を交換
            $temp = $result[$i];
            $result[$i] = $result[$j];
            $result[$j] = $temp;
        }
        
        return $result;
    }
    
    /**
     * ランダムに要素を選択
     */
    public static function randomElement($array) {
        if (empty($array)) {
            return null;
        }
        
        $index = rand(0, count($array) - 1);
        return $array[$index];
    }
    
    /**
     * 重複なしでランダムに複数要素を選択
     */
    public static function randomSample($array, $count) {
        if ($count > count($array)) {
            $count = count($array);
        }
        
        $shuffled = self::shuffle($array);
        return array_slice($shuffled, 0, $count);
    }
    
    /**
     * 重み付きランダム選択
     */
    public static function weightedRandom($items, $weights) {
        $totalWeight = array_sum($weights);
        $random = rand(1, $totalWeight);
        
        $current = 0;
        foreach ($items as $index => $item) {
            $current += $weights[$index];
            if ($random <= $current) {
                return $item;
            }
        }
        
        return end($items);
    }
    
    /**
     * ランダムな組み合わせを生成
     */
    public static function randomPairs($array) {
        $shuffled = self::shuffle($array);
        $pairs = [];
        
        for ($i = 0; $i < count($shuffled) - 1; $i += 2) {
            $pairs[] = [$shuffled[$i], $shuffled[$i + 1]];
        }
        
        return $pairs;
    }
}

// 使用例
echo "=== 配列のシャッフル ===\n";
$cards = ['A', 'K', 'Q', 'J', '10', '9', '8', '7'];
$shuffled = ArrayRandomizer::shuffle($cards);
echo "元: " . implode(', ', $cards) . "\n";
echo "シャッフル後: " . implode(', ', $shuffled) . "\n\n";

echo "=== ランダム選択 ===\n";
$fruits = ['りんご', 'バナナ', 'オレンジ', 'ぶどう', 'いちご'];
echo "ランダムな果物: " . ArrayRandomizer::randomElement($fruits) . "\n\n";

echo "=== ランダムサンプリング ===\n";
$sample = ArrayRandomizer::randomSample($fruits, 3);
echo "3つの果物: " . implode(', ', $sample) . "\n\n";

echo "=== 重み付きランダム ===\n";
$rewards = ['金貨', '銀貨', '銅貨', 'レアアイテム'];
$weights = [30, 40, 25, 5]; // 確率の重み

echo "10回のガチャ結果:\n";
for ($i = 0; $i < 10; $i++) {
    $reward = ArrayRandomizer::weightedRandom($rewards, $weights);
    echo ($i + 1) . ": {$reward}\n";
}

echo "\n=== ランダムなペア作成 ===\n";
$players = ['太郎', '花子', '次郎', '美咲', '健太', '由美', '大輔', '愛'];
$pairs = ArrayRandomizer::randomPairs($players);
echo "チーム分け:\n";
foreach ($pairs as $i => $pair) {
    echo "チーム" . ($i + 1) . ": " . implode(' & ', $pair) . "\n";
}
?>

4. シミュレーションとテスト

<?php
/**
 * モンテカルロシミュレーションクラス
 */
class MonteCarloSimulation {
    
    /**
     * 円周率πを推定(モンテカルロ法)
     */
    public static function estimatePi($iterations = 10000) {
        $insideCircle = 0;
        
        for ($i = 0; $i < $iterations; $i++) {
            // -1から1の範囲でランダムな点を生成
            $x = (rand(0, 1000000) / 1000000) * 2 - 1;
            $y = (rand(0, 1000000) / 1000000) * 2 - 1;
            
            // 原点からの距離が1以下なら円の内側
            if (($x * $x + $y * $y) <= 1) {
                $insideCircle++;
            }
        }
        
        // π ≈ 4 × (円内の点数 / 総点数)
        $pi = 4 * ($insideCircle / $iterations);
        
        return [
            'estimated_pi' => $pi,
            'actual_pi' => M_PI,
            'error' => abs($pi - M_PI),
            'iterations' => $iterations
        ];
    }
    
    /**
     * ランダムウォークシミュレーション
     */
    public static function randomWalk($steps = 100) {
        $x = 0;
        $y = 0;
        $path = [['x' => $x, 'y' => $y]];
        
        for ($i = 0; $i < $steps; $i++) {
            $direction = rand(0, 3);
            
            switch ($direction) {
                case 0: $y++; break; // 上
                case 1: $x++; break; // 右
                case 2: $y--; break; // 下
                case 3: $x--; break; // 左
            }
            
            $path[] = ['x' => $x, 'y' => $y];
        }
        
        $distance = sqrt($x * $x + $y * $y);
        
        return [
            'path' => $path,
            'final_position' => ['x' => $x, 'y' => $y],
            'distance_from_origin' => $distance,
            'steps' => $steps
        ];
    }
    
    /**
     * コイン投げの統計
     */
    public static function coinFlipStatistics($flips = 1000) {
        $heads = 0;
        $tails = 0;
        $longestHeadsStreak = 0;
        $longestTailsStreak = 0;
        $currentHeadsStreak = 0;
        $currentTailsStreak = 0;
        
        for ($i = 0; $i < $flips; $i++) {
            $result = rand(0, 1);
            
            if ($result === 1) {
                $heads++;
                $currentHeadsStreak++;
                $currentTailsStreak = 0;
                $longestHeadsStreak = max($longestHeadsStreak, $currentHeadsStreak);
            } else {
                $tails++;
                $currentTailsStreak++;
                $currentHeadsStreak = 0;
                $longestTailsStreak = max($longestTailsStreak, $currentTailsStreak);
            }
        }
        
        return [
            'total_flips' => $flips,
            'heads' => $heads,
            'tails' => $tails,
            'heads_percentage' => ($heads / $flips) * 100,
            'tails_percentage' => ($tails / $flips) * 100,
            'longest_heads_streak' => $longestHeadsStreak,
            'longest_tails_streak' => $longestTailsStreak
        ];
    }
}

// 使用例
echo "=== 円周率の推定 ===\n";
$piEstimate = MonteCarloSimulation::estimatePi(100000);
echo "反復回数: {$piEstimate['iterations']}\n";
echo "推定値: {$piEstimate['estimated_pi']}\n";
echo "実際の値: {$piEstimate['actual_pi']}\n";
echo "誤差: {$piEstimate['error']}\n\n";

echo "=== ランダムウォーク ===\n";
$walk = MonteCarloSimulation::randomWalk(50);
echo "ステップ数: {$walk['steps']}\n";
echo "最終位置: ({$walk['final_position']['x']}, {$walk['final_position']['y']})\n";
echo "原点からの距離: " . round($walk['distance_from_origin'], 2) . "\n\n";

echo "=== コイン投げ統計 ===\n";
$stats = MonteCarloSimulation::coinFlipStatistics(10000);
echo "総投げ回数: {$stats['total_flips']}\n";
echo "表: {$stats['heads']} 回 (" . round($stats['heads_percentage'], 2) . "%)\n";
echo "裏: {$stats['tails']} 回 (" . round($stats['tails_percentage'], 2) . "%)\n";
echo "最長表連続: {$stats['longest_heads_streak']} 回\n";
echo "最長裏連続: {$stats['longest_tails_streak']} 回\n";
?>

5. バナー広告のランダム表示

<?php
/**
 * 広告ローテーションクラス
 */
class AdRotator {
    private $ads = [];
    
    /**
     * 広告を追加
     */
    public function addAd($id, $html, $weight = 1) {
        $this->ads[] = [
            'id' => $id,
            'html' => $html,
            'weight' => $weight,
            'impressions' => 0,
            'clicks' => 0
        ];
    }
    
    /**
     * ランダムに広告を選択(重み付き)
     */
    public function selectAd() {
        if (empty($this->ads)) {
            return null;
        }
        
        $totalWeight = array_sum(array_column($this->ads, 'weight'));
        $random = rand(1, $totalWeight);
        
        $current = 0;
        foreach ($this->ads as &$ad) {
            $current += $ad['weight'];
            if ($random <= $current) {
                $ad['impressions']++;
                return $ad;
            }
        }
        
        return null;
    }
    
    /**
     * 統計情報を取得
     */
    public function getStatistics() {
        $totalImpressions = array_sum(array_column($this->ads, 'impressions'));
        $totalClicks = array_sum(array_column($this->ads, 'clicks'));
        
        $stats = [];
        foreach ($this->ads as $ad) {
            $ctr = $ad['impressions'] > 0 
                ? ($ad['clicks'] / $ad['impressions']) * 100 
                : 0;
            
            $stats[] = [
                'id' => $ad['id'],
                'impressions' => $ad['impressions'],
                'clicks' => $ad['clicks'],
                'ctr' => round($ctr, 2) . '%',
                'weight' => $ad['weight']
            ];
        }
        
        return [
            'total_impressions' => $totalImpressions,
            'total_clicks' => $totalClicks,
            'ads' => $stats
        ];
    }
}

// 使用例
$rotator = new AdRotator();
$rotator->addAd('ad1', '<div>広告A</div>', 50);  // 50%
$rotator->addAd('ad2', '<div>広告B</div>', 30);  // 30%
$rotator->addAd('ad3', '<div>広告C</div>', 20);  // 20%

echo "=== 広告ローテーション(100回表示) ===\n";
for ($i = 0; $i < 100; $i++) {
    $ad = $rotator->selectAd();
}

$stats = $rotator->getStatistics();
echo "総表示回数: {$stats['total_impressions']}\n\n";

foreach ($stats['ads'] as $adStat) {
    echo "広告ID: {$adStat['id']}\n";
    echo "  表示回数: {$adStat['impressions']}\n";
    echo "  重み: {$adStat['weight']}\n";
    echo "  理論値: " . ($adStat['weight'] / 100 * 100) . "%\n";
    echo "  実測値: " . ($adStat['impressions']) . "%\n\n";
}
?>

よくある間違いと注意点

間違い1: セキュリティ用途での使用

<?php
// ❌ セッショントークンにrand()を使用(危険)
$token = rand();

// ❌ パスワードリセットトークンにrand()を使用(危険)
$resetToken = rand(100000, 999999);

// ✅ セキュリティ用途にはrandom_int()またはrandom_bytes()
$token = bin2hex(random_bytes(32));
$resetToken = random_int(100000, 999999);
?>

間違い2: 範囲指定のミス

<?php
// ❌ 最小値と最大値が逆
$wrong = rand(100, 1); // エラーまたは予期しない結果

// ✅ 最小値 < 最大値
$correct = rand(1, 100);

// ❌ 浮動小数点を期待
$float = rand(0.0, 1.0); // 整数0または1が返る

// ✅ 浮動小数点の乱数が必要な場合
$float = rand(0, 1000000) / 1000000; // 0.0〜1.0
?>

間違い3: シード設定の誤解

<?php
// ❌ 毎回同じシードを設定(同じ乱数列になる)
srand(12345);
$r1 = rand(); // 常に同じ値
$r2 = rand(); // 常に同じ値

// ✅ 通常はシード設定は不要(自動的に設定される)
// シード設定は再現性が必要なテストでのみ使用
?>

まとめ

rand関数は、PHPで乱数を生成するための基本的な関数です。以下のポイントを押さえておきましょう。

  • 指定範囲内のランダムな整数を生成
  • セキュリティ用途には使用しないrandom_int()を使用)
  • 一般的な用途では**mt_rand()の方が高品質**
  • ゲーム開発、シミュレーション、テストデータ生成に活用
  • 配列のシャッフルやランダムサンプリングに便利
  • 重み付きランダム選択で確率制御が可能
  • 範囲指定は最小値から最大値の順で指定

乱数は多くのアプリケーションで重要な役割を果たします。用途に応じて適切な乱数生成関数を選択し、安全で効果的なコードを書きましょう!

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