?
Solved

Php Array Shuffle - shuffling totally all results

Posted on 2013-01-23
12
Medium Priority
?
575 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 4
  • 2
  • +2
12 Comments
 
LVL 10

Assisted Solution

by:acbxyz
acbxyz earned 880 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 35

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
WordPress Tutorial 4: Recommended Plugins

Now that you have WordPress installed, understand the interface, and know how to install new parts, let’s take a look at our recommended plugins.

 
LVL 3

Author Comment

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

~JC
0
 
LVL 35

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 880 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 35

Accepted Solution

by:
gr8gonzo earned 1120 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 35

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

WordPress Tutorial 2: Terminology

An important part of learning any new piece of software is understanding the terminology it uses. Thankfully WordPress uses fairly simple names for everything that make it easy to start using the software.

Question has a verified solution.

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

Foreword (July, 2015) Since I first wrote this article, years ago, a great many more people have begun using the internet.  They are coming online from every part of the globe, learning, reading, shopping and spending money at an ever-increasing ra…
This article discusses four methods for overlaying images in a container on a web page
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.
Suggested Courses

765 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