CarolinaTigerRescue
CarolinaTigerRescue

Reputation: 1

Using PHP to randomize 4 weighted items from an array without using the same one twice

I'm creating a php file that displays 4 event sponsor ads. For each sponsor I created an element in an array for their id, name, URL to site, URL for image, level of sponsor, value of sponsorship

I want to create a randomizer that will randomly populate 4 records from the array, but weight the values so that higher level sponsors appear more often than others. I've already created the value for the weight as a percent.

The closest solution I've found to this problem is:

MySQL: Select Random Entry, but Weight Towards Certain Entries

Which suggests including this code, BUT the referenced link for the similar issue doesn't make sense to me. It suggests using ORDER BY -LOG(1.0 – RAND()) / Multiplier but I'm using an array, not record results and I'm not completely clear on how this works...

It looks like array_rand will be helpful to at least generate the 4 different values, but still not sure how to weight them.

Upvotes: 0

Views: 446

Answers (3)

CarolinaTigerRescue
CarolinaTigerRescue

Reputation: 1

OK, I finally worked everything out!

Here is how my final code went (I've separated and simplified the code here to piece-meal the tasks):

  1. Create the array

    $Sponsors=array( array('Sponsor' => 'Sponsor1', 'Value' => '500'), array('Sponsor' => 'Sponsor2', 'Value' => '300'), array('Sponsor' => 'Sponsor3', 'Value' => '300'), array('Sponsor' => 'Sponsor4', 'Value' => '200'),) );

  2. Set SponsorTotalCt = the number of sponsors and create a variable to hold the weighted percentage

    $SponsorTotalCt = count($Sponsors);
    $SponsorWeight = 0;

  3. Get a total value for all the sponsorships

    $SponsorTotalAmt = 0; foreach($Sponsors as $array) { $SponsorTotalAmt += $array['Value']; };

  4. Add the sponsor weight as a percent as another 'field value' in the $Sponsors array

    $i = 0; //initialize i while ($i < $SponsorTotalCt){ foreach($Sponsors as $array) { $SponsorWeight = round($Sponsors[$i]['Value']/$SponsorTotalAmt * 100); $Sponsors[$i]['Weight'] = $SponsorWeight; }; $i++; // count one up };

*Note: at this point the $Sponsors 'record listing' would look kind of like this

$Sponsors =
[0] 'Sponsor1','500', 38 (this last is the percent the sponsorship should be weighted)
[1] 'Sponsor2', '300', 23
[2] 'Sponsor3', '300', 23
[3] 'Sponsor4', '200', 15

Notice that the last set of values adds up to 100, or close to it (because of the rounding)*

  1. Create a new array of 100 'records' where each row of the $Sponsors array is repeated the number of times that reflects the percentage. i.e. Sponsor1 values will be repeated in the array 38 times

    $newSponsors = array(); $i = 0; //initialize i while ($i < $SponsorTotalCt){ foreach($Sponsors as $array) { $a = array_fill(0, $Sponsors[$i]['Weight'], $Sponsors[$i]);

    }; $newSponsors= array_merge($newSponsors,$a); $i++; // count one up };

  2. Finally, randomly select 3 keys from the 4 Sponsors, weighted by the value of their Sponsorships

    $spot = array_rand($newSponsors,3);

Now I only have to create the code and call the value. YAY!

Upvotes: 0

Andrew Mackrodt
Andrew Mackrodt

Reputation: 1826

You may use a custom function to sort the array based on the weight of sponsor. See usort.

usort($data, function ($value) {
    return rand(0, 100 - $value['weight']);
});

Example:

$data = array(
    array('name' => 'low', 'weight' => 5),
    array('name' => 'medium', 'weight' => 35),
    array('name' => 'high', 'weight' => 60)
);

$frequency = array();

for ($i = 0; $i < 1000; $i++) {
    usort($data, function ($value) {
        return rand(0, 100 - $value['weight']);
    });
    $head = reset($data);
    if (!isset($frequency[$head['name']])) {
        $frequency[$head['name']] = 0;
    }
    $frequency[$head['name']]++;
}

print_r($frequency);

/*
Array
(
    [low] => 263
    [medium] => 328
    [high] => 409
)
*/

Upvotes: 3

aefxx
aefxx

Reputation: 25279

This ORDER BY statement will not be executed by your application, it rather tells the DBMS to order the results returned by your query. You add a LIMIT 4 to the query and you're set.

EDIT Oh, I just read that you do not use a database.

EDIT 2 To really answer your question:

  1. Iterate over your full array
  2. Calculate each item's result using the above equation and store that along with the item's key/index in a temporary array
  3. Sort the temporary array (highest value first), then trim it down to X items
  4. You then have the keys/indexes of X items which were randomly chosen

Upvotes: 0

Related Questions