Jump to content

Recommended Posts

I've currently been using something like this:

 

$array = array();

$monster[1] = "spirit";
$times[1] = "18";
$monster[2] = "golden spirit";
$times[2] = "18";
$monster[3] = "hank";
$times[3] = "15";
$monster[4] = "brown fluffy";
$times[4] = "40";
$monster[5] = "dralmont";
$times[5] = "5";


foreach ($monster as $index => $value)
{
    $array = array_merge($array, array_fill(0, $times[$index], $value));
}

srand((float) microtime() * 10000000);
$input = $array;
$rand_keys = array_rand($input, 3);
$monster = $input[$rand_keys[0]];

 

Which is supposed to give certain things a higher probability of being picked from the array, because some times are placed more than other in there. However I tested it, and it doesn't seem to work very good at all. I set one of the options to like 40000000000 yet I tested it 5 times, and that option didn't even come up. When the odds should've made it come up.

 

So I'm guessing there has to be a better way where I can generate a random result, but still control the probability somewhat. Any help?

your way is very inefficient... not that I can think of a way of doing it any better.

 

<?php
$array = array();

$monster[1] = "spirit";
$times[1] = 18;
$monster[2] = "golden spirit";
$times[2] = 18;
$monster[3] = "hank";
$times[3] = 15;
$monster[4] = "brown fluffy";
$times[4] = 40;
$monster[5] = "dralmont";
$times[5] = 5;


foreach ($monster as $index => $value)
{
    $array = array_merge($array, array_fill(0, $times[$index], $value));
}

$monster = $array[array_rand($array)];
echo $monster;
?>

 

is a tad more efficient and worked for me, but it still is incredibly inefficient. I'm not well versed enough in randomization to provide you a better solution, sorry.

 

edit: wait, nvm, I think I may have thought of something simple :P

How about this?

 

<?php
$monster[1] = "spirit";
$times[1] = 18;
$monster[2] = "golden spirit";
$times[2] = 18;
$monster[3] = "hank";
$times[3] = 15;
$monster[4] = "brown fluffy";
$times[4] = 40;
$monster[5] = "dralmont";
$times[5] = 5;

$rand = mt_rand(1, array_sum($times));
$timeSum = 0;
foreach($times as $key => $value) {
$timeSum += $value;
if($rand <= $timeSum) {
	$monsterName = $monster[$key];
	break;
}
}

echo $monsterName."\n";
?>

 

There might be a simpler method than this, but it basically assigns a range of numbers for each monster and generates a number inside the range min (1) and range max (the sum of all the times) it will then check to see which monster's range the number falls in.

 

It cuts out that ridiculous overhead of loading a huge array with multiple values hundreds of times.

lol wow genericnumber1 we came up with the same solution (you beat me by far hehe.... I should refresh more often, or not get distracted....)

 

 

I thought it was ridiculous to array_fill too.

 

<?php

$a = "abcdefghij";
$a = str_split($a);
$p = array(
10, 10, 10, 10, 10, 10, 10, 10, 10, 10
);

$return = array();

foreach($a as $val) {
$return[$val] = 0;
}

for($i = 0; $i < 1000000; ++$i) {
$return[array_get_rand($a, $p)]++;
}

print_r($return);

function array_get_rand($a, $p) {
if(($c = count($a)) != count($p))
	return false;

srand(microtime(true));
$max = array_sum($p);
$r = mt_rand(0, $max);
$x = 0;
for($i = 0; $i < $c; ++$i) {
	$x += $p[$i];
	if($r <= $x) {
		return $a[$i];
	}
}
}

 

 

Could easily be adapted to this situation.

 

 

By the way Michdd, array indexes start with 0, so unless you only posted part of the code, why not start with 0?  Better yet, why not just do $a[] = "blah"; $a[] = "bleh" so on?  Also, why are you quoting numbers?  That's kind of weird if you're planning on using them as numbers.  Also, I think it's a notice to set a key in a variable that hasn't been previously declared an array.

This is a fun problem so I am throwing my 2 cents into the discussion.  This is what I have used for the weighted random problem in the past.  I think I ported it from a PERL solution I ran across at some point, but I can't completely remember.  Would be cool to see someone profile the solutions we have listed so far.  I think this solution is O(n).  This is the function:

 

<?php
function wrand($data) {
$total = array_sum($data);

foreach ($data as $key => $weight) {
	$dist[$key] = $weight/$total; 
}

$rand = lcg_value();
foreach ($dist as $key => $weight) {
	if (($rand -= $weight) < 0) {
		return $key;
	}
}
}
?>

 

And this is how you would use it:

<?php
$dinos['t rex'] = 1;
$dinos['pterodactyl'] = 2;
$dinos['raptor'] = 5;
$dinos['dennis nedry'] = 10;

$best_thing_about_jurassic_park = wrand($dinos);
?>

 

And here are some stats after running wrand() 10,000 times:

 

               probability	expected
pterodactyl	 10.89%        11.1%	
dennis nedry   55.79%        55.6%	
raptor         28.16%        27.8%	
t rex          5.15%         5.6%	
0.030737 ms avg. runtime

 

* edit *

I think I jacked this code from this perl post of someone who jacked the code from a perl book:

http://www.perlmonks.com/index.pl?node_id=158682

I use a similar system, this is wut i got

<?php

$monsters= array(
'spirit' => 18,
'golden spirit'=>18,
'hank'=>15,
'brown fluffy'=>40,
'dralmont'=>5
);

// First add the weights
$tweight=array_sum($monsters);

//Create a table of roll weights
$atpercent=0;
foreach ($monsters as $key=>$weight)
{
$ptable[$key]=round(($atpercent*100),2);
$atpercent+=($weight/$tweight);
}

// Reverse are table so the highest weights get compared first
$ptable=array_reverse($ptable);

// Ahhh, now ya almost get it 
// Now random rolls


// just a sample array to hold our counters
foreach ($monsters as $key=>$weight) {
$rolldata[$key]=0;
}

$numrolls=5000;
for($i=0;$i<$numrolls;$i++)
{
$roll=rand(0,100);
foreach ($ptable as $key=>$percent)
	if($roll>$percent) {$rolldata[$key]++; break;}
}

//show stats
foreach($rolldata as $key=>$data)
echo $key .' = ('.$monsters[$key]/$tweight .'%) '. ($data/$numrolls)."%\r\n";

Results are predicatable according to the weights

spirit = (0.1875%) 0.18%

golden spirit = (0.1875%) 0.1904%

hank = (0.15625%) 0.1556%

brown fluffy = (0.41666666666667%) 0.41%

dralmont = (0.052083333333333%) 0.053%

Same as my previous post but wrapped in a nice little class.

 

<?php
class WeightedRandom {

protected $distribution = array();

function __construct($weights) {
	$total = array_sum($weights);

	foreach ($weights as $key => $weight) {
		$this->distribution[$key] = $weight/$total; 
	}
}

function rand() {
   $rand = lcg_value();
   foreach ($this->distribution as $key => $weight) {
      if (($rand -= $weight) < 0) {
         return $key;
      }
   }
}

}

$weights = array(
't rex'        => 1,
'pterodactyl'  => 2,
'raptor'       => 5,
'dennis nedry' => 10
);

$weights = new WeightedRandom($weights);

echo $weights->rand();
?>

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.