PHP에서 가중치별로 랜덤 결과를 생성하시겠습니까?
저는 PHP에서 난수를 생성하는 방법을 알고 있지만, 1에서 10 사이의 난수를 원하지만 3,4,5는 8,9,10을 더 원합니다.이것이 어떻게 가능한 걸까요?제가 노력한 걸 올리려고 하는데 솔직히 어디서부터 시작해야 할지 모르겠어요.
@Allain의 응답/링크를 기반으로 PHP에서 이 빠른 기능을 개발했습니다.정수 이외의 가중치를 사용하려면 이 가중치를 변경해야 합니다.
/**
* getRandomWeightedElement()
* Utility function for getting random values with weighting.
* Pass in an associative array, such as array('A'=>5, 'B'=>45, 'C'=>50)
* An array like this means that "A" has a 5% chance of being selected, "B" 45%, and "C" 50%.
* The return value is the array key, A, B, or C in this case. Note that the values assigned
* do not have to be percentages. The values are simply relative to each other. If one value
* weight was 2, and the other weight of 1, the value with the weight of 2 has about a 66%
* chance of being selected. Also note that weights should be integers.
*
* @param array $weightedValues
*/
function getRandomWeightedElement(array $weightedValues) {
$rand = mt_rand(1, (int) array_sum($weightedValues));
foreach ($weightedValues as $key => $value) {
$rand -= $value;
if ($rand <= 0) {
return $key;
}
}
}
효율적인 난수가 척도의 한쪽 끝으로 일관되게 치우치도록 하려면:
- 0 사이의 연속형 난수를 선택합니다.1
- ,의 거듭제곱으로 올려 치우침. 1은 무가중치이며, 작을수록 더 많은 숫자가 표시되며, 그 반대도 마찬가지입니다.
- 원하는 범위로 축소하고 정수로 반올림
예: PHP(테스트되지 않음):
function weightedrand($min, $max, $gamma) {
$offset= $max-$min+1;
return floor($min+pow(lcg_value(), $gamma)*$offset);
}
echo(weightedrand(1, 10, 1.5));
너한테 좋은 튜토리얼이 있어.
기본적으로:
- 모든 숫자의 무게를 합하시오.
- 그보다 작은 숫자를 무작위로 고르세요.
- 결과가 음수일 때까지 가중치를 순서대로 뺀 후 음수일 경우 해당 수치를 반환합니다.
이 튜토리얼에서는, 복수의 컷 앤 페이스트 솔루션에 대해 PHP로 설명합니다.이 루틴은 아래의 코멘트로 인해 해당 페이지에서 볼 수 있는 내용보다 약간 변경되었습니다.
포스트에서 가져온 기능:
/**
* weighted_random_simple()
* Pick a random item based on weights.
*
* @param array $values Array of elements to choose from
* @param array $weights An array of weights. Weight must be a positive number.
* @return mixed Selected element.
*/
function weighted_random_simple($values, $weights){
$count = count($values);
$i = 0;
$n = 0;
$num = mt_rand(1, array_sum($weights));
while($i < $count){
$n += $weights[$i];
if($n >= $num){
break;
}
$i++;
}
return $values[$i];
}
/**
* @param array $weightedValues
* @return string
*/
function getRandomWeightedElement(array $weightedValues)
{
$array = array();
foreach ($weightedValues as $key => $weight) {
$array = array_merge(array_fill(0, $weight, $key), $array);
}
return $array[array_rand($array)];
}
getRandomWeightedElement(array('A'=>10, 'B'=>90));
이것은 매우 쉬운 방법입니다.랜덤 가중 요소를 얻는 방법.배열 변수 $key를 채웁니다.어레이 $weight x에 $key를 가져옵니다.그 후 array_rand를 사용하여 어레이를 만듭니다.그리고 랜덤 값도 있어요;)
명료하고 공정하다.복사/붙여넣기 및 테스트만 하면 됩니다.
/**
* Return weighted probability
* @param (array) prob=>item
* @return key
*/
function weightedRand($stream) {
$pos = mt_rand(1,array_sum(array_keys($stream)));
$em = 0;
foreach ($stream as $k => $v) {
$em += $k;
if ($em >= $pos)
return $v;
}
}
$item['30'] = 'I have more chances than everybody :]';
$item['10'] = 'I have good chances';
$item['1'] = 'I\'m difficult to appear...';
for ($i = 1; $i <= 10; $i++) {
echo weightedRand($item).'<br />';
}
편집: 끝에 누락된 괄호가 추가되었습니다.
비표준 PHP 라이브러리에서 weightedChoice를 사용할 수 있습니다.어레이 키가 될 수 없는 아이템을 조작할 수 있도록, 페어(항목, 중량)의 리스트를 받아들입니다.쌍 함수를 사용하여 변환할 수 있습니다.array(item => weight)
이치노
use function \nspl\a\pairs;
use function \nspl\rnd\weightedChoice;
$weights = pairs(array(
1 => 10,
2 => 15,
3 => 15,
4 => 15,
5 => 15,
6 => 10,
7 => 5,
8 => 5,
9 => 5,
10 => 5
));
$number = weightedChoice($weights);
이 예에서는 2-5가 7-10보다 3배 더 자주 표시됩니다.
이아인을 썼으니까MH의 솔루션이라면 PHP 코드를 공유해도 좋습니다.
<pre><?php
// Set total number of iterations
$total = 1716;
// Set array of random number
$arr = array(1, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5);
$arr2 = array(0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 5);
// Print out random numbers
for ($i=0; $i<$total; $i++){
// Pick random array index
$rand = array_rand($arr);
$rand2 = array_rand($arr2);
// Print array values
print $arr[$rand] . "\t" . $arr2[$rand2] . "\r\n";
}
?></pre>
가중치 정렬을 쉽게 할 수 있는 수업을 방금 내놨어요.
Brad와 Alain의 답변에서 언급된 것과 동일한 알고리즘을 기반으로 하며 속도에 최적화되어 균일한 배포에 대해 유닛 테스트를 거쳤으며 모든 PHP 유형의 요소를 지원합니다.
사용법은 간단합니다.인스턴스화:
$picker = new Brick\Random\RandomPicker();
그런 다음 요소를 가중치 값의 배열로 추가합니다(요소가 문자열 또는 정수인 경우에만 해당).
$picker->addElements([
'foo' => 25,
'bar' => 50,
'baz' => 100
]);
해 「」를 참조해 주세요.addElement()
는 배열접근법이 아닌 숫자, 객체
$picker->addElement($object1, $weight1);
$picker->addElement($object2, $weight2);
다음으로 랜덤 요소를 가져옵니다.
$element = $picker->getRandomElement();
요소 중 하나를 얻을 확률은 관련된 무게에 따라 달라집니다.유일한 제약사항은 가중치가 정수여야 한다는 것입니다.
이 페이지의 답변 대부분은 어레이의 팽창, 과도한 반복, 라이브러리 또는 읽기 어려운 프로세스를 사용하고 있는 것 같습니다.물론 누구나 자신의 아기가 가장 귀엽다고 생각하지만, 저는 솔직히 제 접근법이 날씬하고 단순하며 읽기/수정하기 쉽다고 생각합니다.
OP에 따라 1부터10까지의 값 배열(키라고 선언됨)을 작성합니다.3, 4, 및 5는 다른 값(값이라고 선언됨)의 2배의 무게를 가집니다.
$values_and_weights=array(
1=>1,
2=>1,
3=>2,
4=>2,
5=>2,
6=>1,
7=>1,
8=>1,
9=>1,
10=>1
);
랜덤으로 선택할 수 있는 어레이가 1개밖에 없는 경우나 어레이가 비교적 작을 경우*(확실히 독자적인 벤치마킹 실행)는 다음과 같이 하는 것이 가장 좋습니다.
$pick=mt_rand(1,array_sum($values_and_weights));
$x=0;
foreach($values_and_weights as $val=>$wgt){
if(($x+=$wgt)>=$pick){
echo "$val";
break;
}
}
이 방법에서는 어레이를 변경할 필요가 없으며 어레이 전체를 반복할 필요도 없습니다(단, 그럴 수도 있습니다).
한편 어레이에서 여러 개의 랜덤 선택을 하는 경우 또는 어레이의 크기가 충분히 큰 경우*(확실히 자체 벤치마킹 실행) 어레이 재구축이 더 나을 수 있습니다.
새로운 어레이를 생성하기 위한 메모리 비용은 다음과 같이 더욱 정당화될 것입니다.
- 어레이 사이즈가 증가하여
- 랜덤 선택 수가 증가합니다.
새 배열에서는 이전 요소의 가중치를 현재 요소의 가중치에 추가하여 각 값의 "weight"를 "limit"로 대체해야 합니다.
그런 다음 어레이를 뒤집어서 제한이 어레이 키이고 값이 어레이 값이 되도록 합니다.논리적으로 말하면, 선택한 값의 하한은 >= $pick입니다.
// Declare new array using array_walk one-liner:
array_walk($values_and_weights,function($v,$k)use(&$limits_and_values,&$x){$limits_and_values[$x+=$v]=$k;});
//Alternative declaration method - 4-liner, foreach() loop:
/*$x=0;
foreach($values_and_weights as $val=>$wgt){
$limits_and_values[$x+=$wgt]=$val;
}*/
var_export($limits_and_values);
다음 배열을 만듭니다.
array (
1 => 1,
2 => 2,
4 => 3,
6 => 4,
8 => 5,
9 => 6,
10 => 7,
11 => 8,
12 => 9,
13 => 10,
)
서 랜덤 " " " " 를 합니다.$pick
값을 선택합니다.
// $x (from walk/loop) is the same as writing: end($limits_and_values); $x=key($limits_and_values);
$pick=mt_rand(1,$x); // pull random integer between 1 and highest limit/key
while(!isset($limits_and_values[$pick])){++$pick;} // smallest possible loop to find key
echo $limits_and_values[$pick]; // this is your random (weighted) value
.isset()
으로, 는 「」 「」 「」 「」입니다.isset()
while 루프의 콜은 어레이 내에서 가장 큰 무게(제한과 혼동하지 않음)의 수 밖에 할 수 없습니다. 횟수 =회 반복 횟수 = 2!회 반복 횟수!
이 접근방식은 어레이 전체를 반복할 필요가 없습니다.
저는 이걸 썼어요
mt_rand($min, mt_rand($min, $max));
값이 높을수록 mt_rand 중 하나에 의해 더 많은 값이 잘라지기 때문에 더 낮은 값과 더 낮은 값을 제공합니다.
낮은 값에서 확률이 선형적으로 증가하여 정사각형 대각선을 형성합니다(하위 산술 참조).
PRO: 간단하고 간단함
단점: 너무 단순해서 일부 사용 사례에 대해 가중치나 밸런스가 충분하지 않을 수 있습니다.
산술:
최소값부터 최대값까지의 i-n번째 값의 인덱스를 지정합니다.
P(i)가 i-n번째 값을 얻을 확률로 한다.
N=max-min:
P(i)=(1+N-i)/sum(1,N)
N은 모든 항에 대해 같기 때문에:
P(i) is proportional to N-i
그래서, 사실, 낮은 값에서 확률이 선형적으로 증가하여 정사각형 대각선을 형성하고 있다.
종류:
바리안트를 쓸 수 있습니다.
mt_rand($min, mt_rand(1, mt_rand(1, $max))); //value more given in low part
mt_rand(mt_rand($min, $max), $max); //mirrored, more upper values than lower
...
브래드의 앤스워를 사용해서 내 상황에 맞게 조금 바꿔서 유연성을 더했다.
어레이 값을 가진 어레이가 있습니다.
$products = [
['id'=>1,'name'=> 'product1' , 'chance'=>2] ,
['id'=>2,'name'=> 'product2' , 'chance'=>7]
]
먼저 제품군을 섞습니다.
shuffle($products );
그러면 함수에 전달할 수 있습니다.
function getRandomWeightedElement(array $products) {
$chancesSum = 0;
foreach ($products as $product){
$chancesSum += (int) $product['chance'];
}
$rand = mt_rand(1, $chancesSum);
$range = 0;
foreach ($products as $product) {
$range += (int) $product['chance'];
$compare = $rand - $range;
if ($compare <= 0){
return (int) $product['id'];
}
}}
함수 getBucketFromWeights($values) { $total = $currentTotal = $currentTotal = 0;
foreach ($values as $amount) {
$total += $amount;
}
$rand = mt_rand(0, $total-1);
foreach ($values as $amount) {
$currentTotal += $amount;
if ($rand => $currentTotal) {
$bucket++;
}
else {
break;
}
}
return $bucket;
}
사용자 정의 가중치로 랜덤 요소를 선택한다는 답변에서 이를 수정했습니다.
내가 이 글을 쓰고 난 후 나는 다른 누군가가 훨씬 더 우아한 답을 가지고 있는 것을 보았다.헤헤헤헤.
언급URL : https://stackoverflow.com/questions/445235/generating-random-results-by-weight-in-php
'source' 카테고리의 다른 글
php로 날짜/시간에서 년/월/일을 가져오시겠습니까? (0) | 2023.01.15 |
---|---|
Python은 MySQL 준비문을 지원합니까? (0) | 2023.01.15 |
php의 "include"와 "require"의 차이 (0) | 2023.01.15 |
하위 쿼리에 그룹 열이 있을 때 MySQL 8이 INDEX를 사용하지 않습니다. (0) | 2023.01.15 |
Java socket API: 연결이 종료되었는지 확인하는 방법 (0) | 2023.01.15 |