Solved

Php Array Shuffle - shuffling totally all results

Posted on 2013-01-23
12
536 Views
Last Modified: 2013-01-24
Hi E's, I have this code:
<?
$arraycerta = array("0-0", "75-0", "150-0", "225-0", "300-0");
shuffle($arraycerta);
print_r($arraycerta);
?>

Open in new window

The array $arraycerta have five values, each one with number combination.
I use php "shuffle" to shuffling all the values in array. So far so good.
The problem is in some "shuffle" some array keys assumes the same position of the initial array: like:
initial array "0-0", "75-0", "150-0", "225-0", "300-0"
array after shuffle "150-0", "75-0", "225-0", "300-0", "0-0"
In this case $arraycerta[1] have the same position in array.

Other example:
initial array "0-0", "75-0", "150-0", "225-0", "300-0"
array after shuffle "0-0", "150-0", "75-0", "225-0", "300-0"
In this case $arraycerta[0] and [3] and [4] assumes the same position in array.

These two examples represent what I do not want.

What I need is to know how I can do to the array come fully shuffled?

The best regards, JC
0
Comment
Question by:Pedro Chagas
  • 4
  • 4
  • 2
  • +2
12 Comments
 
LVL 10

Assisted Solution

by:acbxyz
acbxyz earned 220 total points
ID: 38812225
If you don't want a value at the same position, you have to remember the old state and shuffle in a loop. After each shuffle, you have to compare each value from the original array with the shuffled one. If there are any equal values, shuffle again. Here is an untested example:
$array = array(...values...);
$original = $array
$i = 0;
do {
    $i++;
    $reshuffle = false
    $array = shuffle($array);
    for each ($array as $key => $value) {
        if ($array[$key] == $original[$key]) $reshuffle = true;
    }
} while ($reshuffle && $i < 100);

Open in new window

Remember that $array must has more different than same values. $i is to prevent an endless loop.

But is it really random if you disallow some values to stay the same?
0
 
LVL 17

Expert Comment

by:nanharbison
ID: 38812271
Shuffling is random, so some elements will stay in the same place, especially with such a small sample of 5.
0
 
LVL 34

Expert Comment

by:gr8gonzo
ID: 38812287
You can create a shuffled array that contains shuffled positions, which will be much faster and more efficient than reshuffling until you get unique positions:

// Example: $shuffled = fullShuffle($array);

function fullShuffle($original)
{
$positions = shuffle(array_keys($original));
$shuffled_array = array();

foreach($original as $original_position => $original_value)
{
  $new_position = array_pop($positions);
  $positions_left = count($positions);
  while(($new_position == $original_position) && $positions_left)
  {
    array_unshift($positions,$new_position); // Move position back into queue
    $new_position = array_pop($positions); // Get next position
  }

  $shuffled_array[$new_position] = $original_value;
  
}
return $shuffled_array;
}

Open in new window


I haven't tested this code yet, but it should do the job.

Also, there's a slight chance that the very last element could be in the same position, if all of the other spots are taken.
0
Does Powershell have you tied up in knots?

Managing Active Directory does not always have to be complicated.  If you are spending more time trying instead of doing, then it's time to look at something else. For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why

 
LVL 3

Author Comment

by:Pedro Chagas
ID: 38812294
Hi, in real situation will be 48 keys.

~JC
0
 
LVL 34

Expert Comment

by:gr8gonzo
ID: 38812297
Oops, here's the working version:

<?php

$arraycerta = array("0-0", "75-0", "150-0", "225-0", "300-0");

$shuffled = fullShuffle($arraycerta);
print_r($shuffled);


function fullShuffle($original)
{
$positions = array_keys($original);
shuffle($positions);
$shuffled_array = array();

foreach($original as $original_position => $original_value)
{
  $new_position = array_pop($positions);
  $positions_left = count($positions);
  while(($new_position == $original_position) && $positions_left)
  {
    array_unshift($positions,$new_position); // Move position back into queue
    $new_position = array_pop($positions); // Get next position
  }

  $shuffled_array[$new_position] = $original_value;
}
ksort($shuffled_array);
return $shuffled_array;
}
?>

Open in new window

0
 
LVL 3

Author Comment

by:Pedro Chagas
ID: 38812334
Hi @gr8gonzo,
Your solution not work. I changed the first line to be able to compare arrays to:
<?php

$arraycerta = array("0-0", "75-0", "150-0", "225-0", "300-0");
$shuffled = fullShuffle($arraycerta);
print_r($arraycerta);
echo "<br>";
print_r($shuffled);
........................

Open in new window

And after some shuffles.......
Array ( [0] => 0-0 [1] => 75-0 [2] => 150-0 [3] => 225-0 [4] => 300-0 )
Array ( [0] => 150-0 [1] => 225-0 [2] => 75-0 [3] => 0-0 [4] => 300-0 )
In this case the key[4] have the same position in array.

I believe is just a little adjustment in the code.

~JC
0
 
LVL 3

Author Comment

by:Pedro Chagas
ID: 38812362
Hi @acbxyz, I try to use your code, and I do some basic adjustments:
<?
$arraycerta = array("0-0", "75-0", "150-0", "225-0", "300-0");
$original = $arraycerta;
$i = 0;
do {
    $i++;
    $reshuffle = false;
    $arraycerta = shuffle($arraycerta);
    foreach($arraycerta as $key => $value) {
        if ($arraycerta[$key] == $original[$key]) $reshuffle = true;
    }
} while ($reshuffle && $i < 100);
print_r($arraycerta);
?>

Open in new window

But I get this warning: "Warning: Invalid argument supplied for foreach() in /home/pedro/public_html/biblioteca/tutoriais/1206/index.php on line 9
1"

Can you check please?

~JC
0
 
LVL 9

Expert Comment

by:rinfo
ID: 38813748
Why you are insisting on using array shuffle.
No doubt it is a good function to achieve a shuffled array.
But your is not a normal shuffling.
So in your case you should consider writing a simple shuffle proc.
Workflow could be something like this.
1.Iterate over all the elements of the array.
2.Get an element.
3 call a function to get a random number that will check if it is the same old number
   or exists in an array of already used number.
4.if found assigned  the element to it.Put this number in an array so you can check if   number    is already done.
5.if not call function to get another number
0
 
LVL 10

Assisted Solution

by:acbxyz
acbxyz earned 220 total points
ID: 38813815
I messed up with the using of shuffle(). It returns a bool value and shuffles the array given as parameter.
Just replace
$arraycerta = shuffle($arraycerta);
with
shuffle($arraycerta);

I can test it this evening (in my time zone, in about 5-6 hours)
0
 
LVL 34

Accepted Solution

by:
gr8gonzo earned 280 total points
ID: 38813880
Okay, here is a working version and I've put it through 3 million and some rounds of testing:

Testing code:
<?php

// Testing harness - perform 1,000,000 executions of the function and look for failures
$time_start = time();
$max_tests = 1000000;
$failed_tests = 0;
for($i = 0; $i < $max_tests; $i++)
{
	$arraycerta = array("0-0", "75-0", "150-0", "225-0", "300-0");
	$shuffled = fullShuffle($arraycerta);
	
	// Look for any EXACT matches between the shuffled and original arrays
	$matching = array_intersect_assoc($arraycerta,$shuffled);
	if(count($matching))
	{
		echo "Found matching on attempt #{$i}\n";
		$failed_tests++;
	}
}

// Display test results
echo "Finished {$i} tests in " . (time()-$time_start) . " seconds! Of the $i tests, $failed_tests were not fully shuffled.\n";

Open in new window


Function:
// Function to fully shuffle
function fullShuffle($original)
{
  // Get new initial positions
  $positions = array_keys($original);
  shuffle($positions);
  $shuffled_array = array();
  
  // Start filling array with new positions
  foreach($original as $original_position => $original_value)
  {
    // Get new position, making sure it's not the same position as the original array
    $new_position = array_pop($positions);
    $positions_left = count($positions);
    while(($new_position == $original_position) && $positions_left)
    {
      array_unshift($positions,$new_position); // Move position back into queue
      $new_position = array_pop($positions); // Get next position
    }
    
    // True when the last element is the same but has nowhere to go
    $requires_swapping = (($new_position == $original_position) && ($positions_left == 0));
    
    // Place element into the new shuffled array, in the new position
    $shuffled_array[$new_position] = $original_value;
  }
  
  // Sort the semi-final results
  ksort($shuffled_array);
  
  // Fix for the same ending element - swap down until we're unique
  if($requires_swapping)
  {
    for($i = count($shuffled_array)-1; $i > 0; $i--)
    {
      // Swap element with the one before it
      $swap_element = $shuffled_array[$i];
      $with_element = $shuffled_array[$i-1];
      $shuffled_array[$i] = $with_element;
      $shuffled_array[$i-1] = $swap_element;
      
      // Check against original once fully shuffled
      if(($shuffled_array[$i] != $original[$i]) && ($shuffled_array[$i-1] != $original[$i-1]))
      {
        break;
      }
    }
  }
  
  return $shuffled_array;
}

Open in new window

0
 
LVL 34

Expert Comment

by:gr8gonzo
ID: 38813894
The above testing harness will shuffle the array 1 million times and then check for exact matches between the shuffled and original array. I ran it 3 times and tested the performance. You're looking at a run time of 8 microseconds per fullShuffle() call, with NO same matches between the shuffled and original arrays over every single test, meaning it worked every time.
0
 
LVL 3

Author Comment

by:Pedro Chagas
ID: 38815353
The code of @acbxyz, for future reference:
<?
$original = array("0-0", "75-0", "150-0", "225-0", "300-0");
$arraycerta = array("0-0", "75-0", "150-0", "225-0", "300-0");
$original = $arraycerta;
$i = 0;
do {
    $i++;
    $reshuffle = false;
    shuffle($arraycerta);
    foreach($arraycerta as $key => $value) {
        if ($arraycerta[$key] == $original[$key]) $reshuffle = true;
    }
} while ($reshuffle && $i < 100);
print_r($original);
echo "<br>";
print_r($arraycerta);
?>

Open in new window


Thank you for your attention.
~JC
0

Featured Post

PRTG Network Monitor: Intuitive Network Monitoring

Network Monitoring is essential to ensure that computer systems and network devices are running. Use PRTG to monitor LANs, servers, websites, applications and devices, bandwidth, virtual environments, remote systems, IoT, and many more. PRTG is easy to set up & use.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article discusses how to create an extensible mechanism for linked drop downs.
Introduction This article is intended for those who are new to PHP error handling (https://www.experts-exchange.com/articles/11769/And-by-the-way-I-am-New-to-PHP.html).  It addresses one of the most common problems that plague beginning PHP develop…
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question