Solved

# Php Array Shuffle - shuffling totally all results

Posted on 2013-01-23
536 Views
Hi E's, I have this code:
``````<?
\$arraycerta = array("0-0", "75-0", "150-0", "225-0", "300-0");
shuffle(\$arraycerta);
print_r(\$arraycerta);
?>
``````
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
Question by:Pedro Chagas
• 4
• 4
• 2
• +2

LVL 10

Assisted Solution

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);
``````
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

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

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;
}
``````

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

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

~JC
0

LVL 34

Expert Comment

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;
}
?>
``````
0

LVL 3

Author Comment

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);
........................
``````
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

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);
?>
``````
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"

~JC
0

LVL 9

Expert Comment

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

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

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";
``````

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;
}
``````
0

LVL 34

Expert Comment

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

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);
?>
``````

~JC
0

## Featured Post

Question has a verified solution.

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

### Suggested Solutions

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.