Chura
Chura

Reputation: 23

Random redirect evenly php

I have a simple php code that redirect to random uri from array. However, after a while i've noticed its not evenly load balanced. Can someone suggest tweaking so it will be evenly redirecting between the links ?

<?php 
$urls = array('https://a.com', 
               'http://b.com', 
               'http://c.com', 
               'http://d.com',
               'https://e.com',
               'https://f.com'
               ); 
shuffle($urls);
header('Location: ' . $urls[0]);
exit();
?>

Thanks

Upvotes: 0

Views: 2875

Answers (3)

Scott Arciszewski
Scott Arciszewski

Reputation: 34093

The problem: shuffle() internally uses the same random number generator as rand(), which is biased, predictable, and has a small set of possible sequences (about 2^32, if I'm not mistaken).

There are two ways to solve this problem:

  1. Persist some information across requests to guarantee the distribution is even (which is what Guillaume's answer did).

  2. Use an unbiased random number generator, such as random_int() (PHP 7, though a (currently experimental) polyfill for 5.x is in the works), which will in the aggregate produce an even distribution of outcomes.

For small sample sizes, solution #1 is probably the one you want. Over millions of requests, both should solve the problem.

If you're feeling adventurous, you could combine the two.

/**
 * An implementation of shuffle() that uses a CSPRNG to select indices
 * 
 * @param reference $array (pass a reference to an array)
 */
function unbiased_shuffle(&$array)
{
    $size = count($array);
    for ($i = $size - 1; $i > 0; --$i) {
        $r = random_int(0, $i);
        if ($r !== $i) {
            $temp = $array[$r];
            $array[$r] = $array[$i];
            $array[$i] = $temp;
        }
    }
    // Reset indices:
    $array = array_values($array);
}

Upvotes: 0

Daniel Waghorn
Daniel Waghorn

Reputation: 2985

Change your header line to choose a random index as well:

header('Location: ' . $urls[rand(0,count($urls) - 1)]);

Upvotes: -1

Guillaume Pagnard
Guillaume Pagnard

Reputation: 126

Evenly and randomly are contradictory. But you can use a stack instead of an array to limit the randomness. Drawback is that you then need storage between script calls. For example :

<?php
$urls=readStack();
if(empty($urls)) {
         $urls = shuffle(array('https://a.com', 
                'http://b.com', 
                'http://c.com', 
                'http://d.com',
                'https://e.com',
                'https://f.com'
               )); 
$url=array_pop($urls);
storeStack($urls);
header('Location: ' . $url);

Where storeStack() and readStack() functions are intended to store and retrieve your stack status from persistent storage. This could be done via file system storage, database access, or a memcache mechanism,... whatever is at your disposal and fast enough for your app. Here is a basic example with file system and serialize :

<?php
define( 'STOREFILENAME', '/tmp/urlstack');

function storeStack($value){
     file_put_contents(STOREFILENAME, serialize( $value ));
}

function readStack($varname){
     return unserialize(file_get_contents(STOREFILENAME));
}

Upvotes: 2

Related Questions