<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-keyword">echo</span></span><span> <span class="hljs-string"><<<HTML
<!--
このコメント、機能とスタイルは、デモンストレーションのためにのみ存存在するします,テキストとは何の関係もありません。
テキストのみが必要な場合,二流の水平線から読み始めてください。
-->
<style>
body { font-family: system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial; line-height: 1.65; max-width: 820px; margin: 2rem auto; padding: 0 1rem; }
pre { overflow: auto; padding: .75rem; background: #f6f8fa; border-radius: 6px; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
</style>
<hr>
<h1>mt_srand ゲーム開発に文字属性を割り当てる方法,ランダムだが公正な設定を実現します?</h1>
<p><strong>まとめ</strong>:<code>mt_srand()
注: time()を使用して直接播種しないでください。そうしないと、同じ秒以内に「ねじ込む」ことができます。割り当てプロセスでMT_SRAND()を繰り返し呼び出さないでください - 割り当てプロセスの開始時に一度ownします。
シナリオ:6つの属性(力、感度、知性、物理、動き、意志)があり、ポイントの総数は30に固定され、各アイテムは[1,10]範囲にある必要があります。
/**
* 意思 totalPoints ランダムに均等にカットします n 共有,满足每共有均 ≥ minVal、≤ maxVal,そして、合計は変わらないままです。
* 制限が厳しすぎる場合、それは実行不可能です,自動ロールバックの試行(ほとんど retries 二流)。
*/
function random_partition_constrained(int </span><span><span class="hljs-subst">$n</span></span><span>, int </span><span><span class="hljs-subst">$totalPoints</span></span><span>, int </span><span><span class="hljs-subst">$minVal</span></span><span>, int </span><span><span class="hljs-subst">$maxVal</span></span><span>, int </span><span><span class="hljs-subst">$retries</span></span><span> = 256): array {
for (</span><span><span class="hljs-subst">$t</span></span><span> = 0; </span><span><span class="hljs-subst">$t</span></span><span> < </span><span><span class="hljs-subst">$retries</span></span><span>; </span><span><span class="hljs-subst">$t</span></span><span>++) {
// 最初に下限を差し引きます,残り R 無料割り当てを行います
</span><span><span class="hljs-subst">$R</span></span><span> = </span><span><span class="hljs-subst">$totalPoints</span></span><span> - </span><span><span class="hljs-subst">$n</span></span><span> * </span><span><span class="hljs-subst">$minVal</span></span><span>;
if (</span><span><span class="hljs-subst">$R</span></span><span> < 0) throw new InvalidArgumentException('下限の合計はポイントの総数を超えています');
// 生成する n-1 個々“カットポイント”,右 [0, R] カットを実行します
</span><span><span class="hljs-subst">$cuts</span></span><span> = [];
for (</span><span><span class="hljs-subst">$i</span></span><span> = 0; </span><span><span class="hljs-subst">$i</span></span><span> < </span><span><span class="hljs-subst">$n</span></span><span> - 1; </span><span><span class="hljs-subst">$i</span></span><span>++) { </span><span><span class="hljs-subst">$cuts</span></span><span>[] = mt_rand(0, </span><span><span class="hljs-subst">$R</span></span><span>); }
sort(</span><span><span class="hljs-subst">$cuts</span></span><span>);
</span><span><span class="hljs-subst">$parts</span></span><span> = [];
</span><span><span class="hljs-subst">$prev</span></span><span> = 0;
foreach (</span><span><span class="hljs-subst">$cuts</span></span><span> as </span><span><span class="hljs-subst">$c</span></span><span>) { </span><span><span class="hljs-subst">$parts</span></span><span>[] = </span><span><span class="hljs-subst">$c</span></span><span> - </span><span><span class="hljs-subst">$prev</span></span><span>; </span><span><span class="hljs-subst">$prev</span></span><span> = </span><span><span class="hljs-subst">$c</span></span><span>; }
</span><span><span class="hljs-subst">$parts</span></span><span>[] = </span><span><span class="hljs-subst">$R</span></span><span> - </span><span><span class="hljs-subst">$prev</span></span><span>;
// 下限に戻します,上限を確認します
</span><span><span class="hljs-subst">$vals</span></span><span> = array_map(fn(</span><span><span class="hljs-subst">$x</span></span><span>) => </span><span><span class="hljs-subst">$x</span></span><span> + </span><span><span class="hljs-subst">$minVal</span></span><span>, </span><span><span class="hljs-subst">$parts</span></span><span>);
if (max(</span><span><span class="hljs-subst">$vals</span></span><span>) <= </span><span><span class="hljs-subst">$maxVal</span></span><span>) return </span><span><span class="hljs-subst">$vals</span></span><span>;
}
throw new RuntimeException('制約の下では分割されていません,上限と下限を緩和するか、ポイントの総数を増やしてください。');
}
// 例:6 アイテムプロパティ,合計 30 ポイント,各アイテム [1,10]
</span><span><span class="hljs-subst">$attrs</span></span><span> = random_partition_constrained(n: 6, totalPoints: 30, minVal: 1, maxVal: 10);
// </span><span><span class="hljs-subst">$attrs</span></span><span> のように [7, 4, 2, 6, 3, 8],合計和恒为 30,そしてすべて範囲内
この方法には3つの利点があります。合計の厳格な制御、公平なアイテム(制約が実行可能であるという前提の下で)、および単純さが達成されます。 Maxvalが小さすぎる場合、またはMinvalが大きすぎる場合、実行可能なドメインは非常に狭く、複数のリサンプリングを使用してヒットすることができます。
異なる属性が異なる「重み」を必要とする場合(たとえば、「運」はわずかにまれです)、重量ごとに追加の「追加ポイント」を抽出できます。以下は、シンプルで読みやすいセグメント化されたサンプリングスキーム(バイアスのない重みに応じて厳密に)です。
/**
* 重量配列に基づいてインデックスを返す(公平です)
* @param array<float> </span><span><span class="hljs-subst">$weights</span></span><span> 非陰性,少なくとも1つ > 0
*/
function weighted_pick(array </span><span><span class="hljs-subst">$weights</span></span><span>): int {
</span><span><span class="hljs-subst">$sum</span></span><span> = array_sum(</span><span><span class="hljs-subst">$weights</span></span><span>);
if (</span><span><span class="hljs-subst">$sum</span></span><span> <= 0) throw new InvalidArgumentException('权重合計和必须 > 0');
</span><span><span class="hljs-subst">$r</span></span><span> = mt_rand() / mt_getrandmax() * </span><span><span class="hljs-subst">$sum</span></span><span>; // [0, sum)
</span><span><span class="hljs-subst">$acc</span></span><span> = 0.0;
foreach (</span><span><span class="hljs-subst">$weights</span></span><span> as </span><span><span class="hljs-subst">$i</span></span><span> => </span><span><span class="hljs-subst">$w</span></span><span>) {
</span><span><span class="hljs-subst">$acc</span></span><span> += </span><span><span class="hljs-subst">$w</span></span><span>;
if (</span><span><span class="hljs-subst">$r</span></span><span> < </span><span><span class="hljs-subst">$acc</span></span><span>) return </span><span><span class="hljs-subst">$i</span></span><span>;
}
return array_key_last(</span><span><span class="hljs-subst">$weights</span></span><span>); // ドリフト保護
}
// 例:ランダムセグメンテーションを提供します 6 にアイテムを追加します 3 ポイント“珍しいボーナス”,重量バイアス 6 アイテム(意思)
</span><span><span class="hljs-subst">$weights</span></span><span> = [1,1,1,1,1,2.5];
</span><span><span class="hljs-subst">$bonus</span></span><span> = [0,0,0,0,0,0];
for (</span><span><span class="hljs-subst">$k</span></span><span> = 0; </span><span><span class="hljs-subst">$k</span></span><span> < 3; </span><span><span class="hljs-subst">$k</span></span><span>++) {
</span><span><span class="hljs-subst">$idx</span></span><span> = weighted_pick(</span><span><span class="hljs-subst">$weights</span></span><span>);
</span><span><span class="hljs-subst">$bonus</span></span><span>[</span><span><span class="hljs-subst">$idx</span></span><span>] += 1;
}
時には「ほとんど中程度の、少数派の極端」をしたいと思うことがあります。 box – mullerを使用して、均一な分布を正規分布に変換し、間隔にトリミングします。
// 生成する ~N(0,1)
function randn(): float {
do {
</span><span><span class="hljs-subst">$u</span></span><span> = mt_rand() / mt_getrandmax();
</span><span><span class="hljs-subst">$v</span></span><span> = mt_rand() / mt_getrandmax();
} while (</span><span><span class="hljs-subst">$u</span></span><span> == 0.0); // 避ける log(0)
return sqrt(-2.0 * log(</span><span><span class="hljs-subst">$u</span></span><span>)) * cos(2.0 * M_PI * </span><span><span class="hljs-subst">$v</span></span><span>);
}
// 存在する [min, max] 生成されます“ベルの形”分散
function randn_clamped(float </span><span><span class="hljs-subst">$min</span></span><span>, float </span><span><span class="hljs-subst">$max</span></span><span>, float </span><span><span class="hljs-subst">$mean</span></span><span>, float </span><span><span class="hljs-subst">$std</span></span><span>): float {
</span><span><span class="hljs-subst">$x</span></span><span> = </span><span><span class="hljs-subst">$mean</span></span><span> + </span><span><span class="hljs-subst">$std</span></span><span> * randn();
return max(</span><span><span class="hljs-subst">$min</span></span><span>, min(</span><span><span class="hljs-subst">$max</span></span><span>, </span><span><span class="hljs-subst">$x</span></span><span>));
}
方法:最初にランダム切断を使用して「総量の公平性」を確保し、次に各属性にゼロ合計の通常の微調整(正と負のオフセット、合計を変更しておきます)を追加して、「最も節度と少量の顕著」の体性感覚を形作ります。
たとえば、4人のプレイヤーが「事前に生成された」属性テンプレートの4セットをつかむと、次のことができます。
// Fisher–Yates 縮む:公平です
function shuffle_unbiased(array &</span><span><span class="hljs-subst">$arr</span></span><span>): void {
</span><span><span class="hljs-subst">$n</span></span><span> = count(</span><span><span class="hljs-subst">$arr</span></span><span>);
for (</span><span><span class="hljs-subst">$i</span></span><span> = </span><span><span class="hljs-subst">$n</span></span><span> - 1; </span><span><span class="hljs-subst">$i</span></span><span> > 0; </span><span><span class="hljs-subst">$i</span></span><span>--) {
</span><span><span class="hljs-subst">$j</span></span><span> = mt_rand(0, </span><span><span class="hljs-subst">$i</span></span><span>);
[</span><span><span class="hljs-subst">$arr</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>], </span><span><span class="hljs-subst">$arr</span></span><span>[</span><span><span class="hljs-subst">$j</span></span><span>]] = [</span><span><span class="hljs-subst">$arr</span></span><span>[</span><span><span class="hljs-subst">$j</span></span><span>], </span><span><span class="hljs-subst">$arr</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>]];
}
}
// 预生成する模板 + 縮む分配
</span><span><span class="hljs-subst">$seed</span></span><span> = seed_from_context('ROOM-9F2A', 12, 'ALL'); // 使用 ALL ゲーム全体を表します
mt_srand(</span><span><span class="hljs-subst">$seed</span></span><span>);
</span><span><span class="hljs-subst">$templates</span></span><span> = [];
for (</span><span><span class="hljs-subst">$t</span></span><span> = 0; </span><span><span class="hljs-subst">$t</span></span><span> < 4; </span><span><span class="hljs-subst">$t</span></span><span>++) {
</span><span><span class="hljs-subst">$templates</span></span><span>[] = random_partition_constrained(6, 30, 1, 10);
}
shuffle_unbiased(</span><span><span class="hljs-subst">$templates</span></span><span>);
// 之后按玩家顺序一人取一個々模板(或使用玩家ID再做一二流稳定縮む)
オンラインに行く前にモンテカルロを確認してください:
// 大まかな統計的平均(回路図)
function simulate_means(int </span><span><span class="hljs-subst">$runs</span></span><span> = 100000): array {
</span><span><span class="hljs-subst">$sum</span></span><span> = array_fill(0, 6, 0.0);
for (</span><span><span class="hljs-subst">$i</span></span><span> = 0; </span><span><span class="hljs-subst">$i</span></span><span> < </span><span><span class="hljs-subst">$runs</span></span><span>; </span><span><span class="hljs-subst">$i</span></span><span>++) {
mt_srand(</span><span><span class="hljs-subst">$i</span></span><span>); // 異なる種
</span><span><span class="hljs-subst">$vals</span></span><span> = random_partition_constrained(6, 30, 1, 10);
for (</span><span><span class="hljs-subst">$k</span></span><span> = 0; </span><span><span class="hljs-subst">$k</span></span><span> < 6; </span><span><span class="hljs-subst">$k</span></span><span>++) { </span><span><span class="hljs-subst">$sum</span></span><span>[</span><span><span class="hljs-subst">$k</span></span><span>] += </span><span><span class="hljs-subst">$vals</span></span><span>[</span><span><span class="hljs-subst">$k</span></span><span>]; }
}
return array_map(fn(</span><span><span class="hljs-subst">$x</span></span><span>) => </span><span><span class="hljs-subst">$x</span></span><span> / </span><span><span class="hljs-subst">$runs</span></span><span>, </span><span><span class="hljs-subst">$sum</span></span><span>);
}
function assign_attributes(string </span><span><span class="hljs-subst">$matchId</span></span><span>, int </span><span><span class="hljs-subst">$season</span></span><span>, string </span><span><span class="hljs-subst">$playerId</span></span><span>): array {
// 1) 播種
mt_srand(seed_from_context(</span><span><span class="hljs-subst">$matchId</span></span><span>, </span><span><span class="hljs-subst">$season</span></span><span>, </span><span><span class="hljs-subst">$playerId</span></span><span>, 'attr-v1'));
// 2) 基本的なセグメンテーション:6 アイテム、合計 30、各アイテム [1, 10]
</span><span><span class="hljs-subst">$attrs</span></span><span> = random_partition_constrained(6, 30, 1, 10);
// 3) 珍しいボーナス:最初に好ましい 6 アイテム
</span><span><span class="hljs-subst">$weights</span></span><span> = [1,1,1,1,1,2.5];
for (</span><span><span class="hljs-subst">$i</span></span><span> = 0; </span><span><span class="hljs-subst">$i</span></span><span> < 3; </span><span><span class="hljs-subst">$i</span></span><span>++) { </span><span><span class="hljs-subst">$attrs</span></span><span>[weighted_pick(</span><span><span class="hljs-subst">$weights</span></span><span>)] += 1; }
// 4) Zerosum微調整(保持合計和不变,形“最も平凡”)
</span><span><span class="hljs-subst">$mean</span></span><span> = array_sum(</span><span><span class="hljs-subst">$attrs</span></span><span>) / count(</span><span><span class="hljs-subst">$attrs</span></span><span>);
</span><span><span class="hljs-subst">$delta</span></span><span> = 0.0;
for (</span><span><span class="hljs-subst">$k</span></span><span> = 0; </span><span><span class="hljs-subst">$k</span></span><span> < count(</span><span><span class="hljs-subst">$attrs</span></span><span>); </span><span><span class="hljs-subst">$k</span></span><span>++) {
</span><span><span class="hljs-subst">$adj</span></span><span> = (int) round(randn() * 0.6); // 小さな変動,調整可能な標準偏差
</span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$k</span></span><span>] += </span><span><span class="hljs-subst">$adj</span></span><span>;
</span><span><span class="hljs-subst">$delta</span></span><span> += </span><span><span class="hljs-subst">$adj</span></span><span>;
}
// 抵消合計和偏移(丸めエラーを均等に返します)
while (</span><span><span class="hljs-subst">$delta</span></span><span> != 0) {
</span><span><span class="hljs-subst">$idx</span></span><span> = mt_rand(0, count(</span><span><span class="hljs-subst">$attrs</span></span><span>)-1);
if (</span><span><span class="hljs-subst">$delta</span></span><span> > 0 && </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$idx</span></span><span>] > 1) { </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$idx</span></span><span>]--; </span><span><span class="hljs-subst">$delta</span></span><span>--; }
elseif (</span><span><span class="hljs-subst">$delta</span></span><span> < 0) { </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$idx</span></span><span>]++; </span><span><span class="hljs-subst">$delta</span></span><span>++; }
}
// 5) 作物間隔(切断する場合,等量から/向其他アイテム借还,保持合計和)
</span><span><span class="hljs-subst">$min</span></span><span> = 1; </span><span><span class="hljs-subst">$max</span></span><span> = 12;
</span><span><span class="hljs-subst">$sumBefore</span></span><span> = array_sum(</span><span><span class="hljs-subst">$attrs</span></span><span>);
</span><span><span class="hljs-subst">$attrs</span></span><span> = array_map(fn(</span><span><span class="hljs-subst">$v</span></span><span>) => max(</span><span><span class="hljs-subst">$min</span></span><span>, min(</span><span><span class="hljs-subst">$max</span></span><span>, </span><span><span class="hljs-subst">$v</span></span><span>)), </span><span><span class="hljs-subst">$attrs</span></span><span>);
</span><span><span class="hljs-subst">$sumAfter</span></span><span> = array_sum(</span><span><span class="hljs-subst">$attrs</span></span><span>);
// 返品または補充します
</span><span><span class="hljs-subst">$diff</span></span><span> = </span><span><span class="hljs-subst">$sumBefore</span></span><span> - </span><span><span class="hljs-subst">$sumAfter</span></span><span>;
while (</span><span><span class="hljs-subst">$diff</span></span><span> != 0) {
</span><span><span class="hljs-subst">$i</span></span><span> = mt_rand(0, count(</span><span><span class="hljs-subst">$attrs</span></span><span>)-1);
if (</span><span><span class="hljs-subst">$diff</span></span><span> > 0 && </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>] < </span><span><span class="hljs-subst">$max</span></span><span>) { </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>]++; </span><span><span class="hljs-subst">$diff</span></span><span>--; }
elseif (</span><span><span class="hljs-subst">$diff</span></span><span> < 0 && </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>] > </span><span><span class="hljs-subst">$min</span></span><span>) { </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>]--; </span><span><span class="hljs-subst">$diff</span></span><span>++; }
}
return </span><span><span class="hljs-subst">$attrs</span></span><span>;
}
MT_SRAND()の「シード」をゲームのコンテキストで結合すると、再現可能な疑似ランダムが生じる可能性があります。ランダム切断、重量サンプリング、ゼロサムの微調整およびその他の方法を使用して、ランダムだが公正な属性割り当てを満足のいく制約の前提で構築できます。バリューゲームまたは強力な対立シナリオを伴う場合は、 random_int()などのランダムソースに切り替え、「アルゴリズムバージョン +シード +入力」を記録して、紛争処理とリプレイの表示の証拠を提供することを検討してください。
HTML;