Solved

Php Array Shuffle - shuffling totally all results

Posted on 2013-01-23
12
553 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 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 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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

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

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

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

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

Suggested Solutions

Deprecated and Headed for the Dustbin By now, you have probably heard that some PHP features, while convenient, can also cause PHP security problems.  This article discusses one of those, called register_globals.  It is a thing you do not want.  …
I imagine that there are some, like me, who require a way of getting currency exchange rates for implementation in web project from time to time, so I thought I would share a solution that I have developed for this purpose. It turns out that Yaho…
The viewer will learn how to count occurrences of each item in an array.
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …

733 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