0

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.

Community
  • 1
  • 1

3 Answers3

3

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
)
*/
Andrew Mackrodt
  • 1,806
  • 14
  • 10
  • -1 | Really, you're suggesting running a closure (not to mention defining it) a 1000 times to do this simple task? Oh my! – aefxx Feb 14 '14 at 16:27
  • Not at all @aefxx - the entire code is 3 lines. The 1000 iteration loop is to show that the function works. This is obvious from the print_r dump. – Andrew Mackrodt Feb 14 '14 at 16:29
  • You're right, my bad. I'll take the downvote back. Here, have an upvote, instead! – aefxx Feb 14 '14 at 16:31
  • I've actually found a way to randomize and uniquely add 4 spaces, and weight the results. It goes something like this: – CarolinaTigerRescue Feb 14 '14 at 22:24
0

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
aefxx
  • 24,835
  • 6
  • 45
  • 55
0

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!

  • uh oh, I still have a little work to do- the array of "100" certainly weights the results by making a value appear more times, and the 'array_rand' certainly does not select the same number twice; however, there is still nothing to stop the same sponsor from appearing twice if the random selection is a duplicate of a previous selection. Does it ever end? – CarolinaTigerRescue Feb 16 '14 at 15:51
  • what finally worked for randomizing: 'do { if ($A0['Sponsor'] == $A1['Sponsor'] | $A0['Sponsor'] == $A2['Sponsor'] | $A0['Sponsor'] == $A3['Sponsor']| $A1['Sponsor'] == $A2['Sponsor'] | $A1['Sponsor'] == $A3['Sponsor'] | $A2['Sponsor'] == $A3['Sponsor']) { $pass = 'false'; $spot = array_rand($newSponsors,4); $A0 = ($newSponsors[$spot[0]]); $A1 = ($newSponsors[$spot[1]]); $A2 = ($newSponsors[$spot[2]]); $A3 = ($newSponsors[$spot[3]]); } else { $pass = 'true'; }; } while ($pass <> 'true'); ' – CarolinaTigerRescue Feb 23 '14 at 14:03