<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
<!--
Ce commentaire、Les fonctions et les styles n'existent que pour la démonstration,N'a rien à voir avec le texte。
Si vous avez seulement besoin du texte,Veuillez commencer à lire à partir de la ligne horizontale suivante。
-->
<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 Comment affecter les attributs de personnages dans le développement de jeux,Atteindre des paramètres aléatoires mais équitables?</h1>
<p><strong>résumé</strong>:<code>mt_srand()
Remarque : N'utilisez pas de temps () pour semer directement, sinon vous "visquerez" dans la même seconde. N'appelez pas à plusieurs reprises mt_srand () dans un processus d'allocation - le truquez-le une fois au début d'un processus d'allocation.
Scénario: Il y a 6 attributs (force, sensibilité, intelligence, physique, mouvement et volonté), le nombre total de points est fixé à 30, et chaque élément doit être dans la plage [1,10].
/**
* Volonté totalPoints Coupez-le en aléatoire uniformément n partager,满足每partager均 ≥ minVal、≤ maxVal,Et la somme reste inchangée。
* Si les restrictions sont trop serrées, ce n'est pas possible,Tentative de recul automatique(la plupart retries De premier ordre)。
*/
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>++) {
// Déduire d'abord la limite inférieure,Restant R Faire une allocation gratuite
</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('La somme de la limite inférieure dépasse le nombre total de points');
// générer n-1 autonome“Point de coupure”,droite [0, R] Effectuer même la coupe
</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>;
// Ajouter à la limite inférieure,Et vérifier la limite supérieure
</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('Divisé non trouvé sous contrainte,Veuillez détendre les limites supérieures et inférieures ou augmenter le nombre total de points。');
}
// Exemple:6 Propriétés de l'article,total 30 indiquer,Chaque élément [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> Comme [7, 4, 2, 6, 3, 8],total和恒为 30,Et tout dans la gamme
Cette méthode présente trois avantages: un contrôle strict sur la somme , des éléments impartiaux (sous la prémisse que les contraintes sont réalisables), et la simplicité est obtenue . Il convient de noter que lorsque Maxval est trop petit ou que Minval est trop grand, le domaine réalisable est très étroit, plusieurs rééchantillons peuvent être utilisés pour frapper.
Si différents attributs veulent des "poids" différents (par exemple, la "chance" est légèrement rare), vous pouvez extraire des "points d'addition" supplémentaires en poids. Ce qui suit est un schéma d'échantillonnage segmenté simple et facile à lire (strictement en fonction des poids sans biais):
/**
* Indice de retour en fonction de la matrice de poids(Impartial)
* @param array<float> </span><span><span class="hljs-subst">$weights</span></span><span> Non négatif,Au moins un > 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('权重total和必须 > 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>); // Protection contre la dérive
}
// Exemple:Donner une segmentation aléatoire 6 Ajouter des articles à 3 indiquer“Bonus rare”,Biais de poids 6 article(volonté)
</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;
}
Parfois, vous voulez "signifier surtout, quelques extrêmes". Utilisez Box-Muller pour convertir la distribution uniforme en une distribution normale, puis le recadrer en intervalle:
// générer ~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); // éviter 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>);
}
// exister [min, max] Généré sur“Forme de cloche”distribué
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>));
}
Méthode: Utilisez d'abord la coupe aléatoire pour assurer la "équité de la quantité totale", puis ajoutez un réglage fin normal de la somme nulle à chaque attribut (décalage positif et négatif, gardez la somme inchangée) pour façonner le somatosensoriel de "la plus modération et une petite quantité proéminente".
Par exemple, 4 joueurs saisissent 4 ensembles de modèles d'attribut "pré-générés", vous pouvez:
// Fisher–Yates Rétrécir:Impartial
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>]];
}
}
// 预générer模板 + Rétrécir分配
</span><span><span class="hljs-subst">$seed</span></span><span> = seed_from_context('ROOM-9F2A', 12, 'ALL'); // utiliser ALL Représente tout le jeu
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>);
// 之后按玩家顺序一人取一autonome模板(或utiliser玩家ID再做一De premier ordre稳定Rétrécir)
Vérifiez Monte Carlo avant d'aller en ligne:
// Moyenne statistique approximative(Schématique)
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>); // Différentes graines
</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) semis
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) Segmentation de base:6 article、total 30、Chaque élément [1, 10]
</span><span><span class="hljs-subst">$attrs</span></span><span> = random_partition_constrained(6, 30, 1, 10);
// 3) Bonus rare:Préféré d'abord 6 article
</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) Zérosum fin(保持total和不变,forme“Le plus médiocre”)
</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); // Petites fluctuations,Écart-type réglable
</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>;
}
// 抵消total和偏移(Renvoie l'erreur d'arrondissement uniformément)
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) Intervalle de culture(Si vous coupez,Montant égal de/向其他article借还,保持total和)
</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>);
// Retour ou reconstituer
</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>;
}
La liaison de la "graine" de mt_srand () avec le contexte de jeu peut entraîner un pseudo-aléatoire reproductible; En utilisant la coupe aléatoire, l'échantillonnage de poids et le réglage fin à somme nulle et d'autres méthodes, une allocation d'attribut aléatoire mais juste peut être construite sur la prémisse des contraintes satisfaisantes. Si cela implique un jeu de valeur ou des scénarios de confrontation forts, envisagez de passer à des sources aléatoires telles que random_int () , et enregistrez "l'algorithme version + semence + entrée" pour fournir des preuves de la gestion des différends et de la relecture.
Html;