source

PHP에서 가중치별로 랜덤 결과를 생성하시겠습니까?

lovecheck 2023. 1. 15. 17:00
반응형

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));

너한테 좋은 튜토리얼이 있어.

기본적으로:

  1. 모든 숫자의 무게를 합하시오.
  2. 그보다 작은 숫자를 무작위로 고르세요.
  3. 결과가 음수일 때까지 가중치를 순서대로 뺀 후 음수일 경우 해당 수치를 반환합니다.

튜토리얼에서는, 복수의 컷 앤 페이스트 솔루션에 대해 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;
    }
}

이 방법에서는 어레이를 변경할 필요가 없으며 어레이 전체를 반복할 필요도 없습니다(단, 그럴 수도 있습니다).


한편 어레이에서 여러 개의 랜덤 선택을 하는 경우 또는 어레이의 크기가 충분히 큰 경우*(확실히 자체 벤치마킹 실행) 어레이 재구축이 더 나을 수 있습니다.

새로운 어레이를 생성하기 위한 메모리 비용은 다음과 같이 더욱 정당화될 것입니다.

  1. 어레이 사이즈가 증가하여
  2. 랜덤 선택 수가 증가합니다.

새 배열에서는 이전 요소의 가중치를 현재 요소의 가중치에 추가하여 각 값의 "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

반응형