Solved

Php Array Shuffle - shuffling totally all results

Posted on 2013-01-23
12
521 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
 
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
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
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

Easy Project Management (No User Manual Required)

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

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…
Developers of all skill levels should learn to use current best practices when developing websites. However many developers, new and old, fall into the trap of using deprecated features because this is what so many tutorials and books tell them to u…
The viewer will learn how to dynamically set the form action using jQuery.
The viewer will learn how to count occurrences of each item in an array.

747 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now