Shuffling Array in the same way, according to number - php

I am running a quiz making website. I wish to show the answers to a question to a user in a shuffled order.
I'm trying to avoid storing the order that answers were presented to the user, if I were to shuffle them randomly.
I want to shuffle the answers predictably, so that I can repeat the shuffle in the same way later (when displaying results).
I've thought that I could shuffle the list of answers by a certain number (either using the number within the sort, or having multiple types of sorts identifiable by an ID number. This way I can simply store the number that they were shuffled by and recall that number to reshuffle them again to the same order.
Here's the skeleton of what I have so far, but I don't have any logic to put the answers back in the $shuffled_array in the shuffled order.
<?php
function SortQuestions($answers, $sort_id)
{
// Blank array for newly shuffled answer order
$shuffled_answers = array();
// Get the number of answers to sort
$answer_count = count($questions);
// Loop through each answer and put them into the array by the $sort_id
foreach ($answers AS $answer_id => $answer)
{
// Logic here for sorting answers, by the $sort_id
// Putting the result in to $shuffled_answers
}
// Return the shuffled answers
return $shuffled_answers;
}
// Define an array of answers and their ID numbers as the key
$answers = array("1" => "A1", "2" => "A2", "3" => "A3", "4" => "A4", "5" => "A5");
// Do the sort by the number 5
SortQuestions($answers, 5);
?>
Is there technique that I can use to shuffle the answers by the number passed into the function?

PHP's shuffle function uses the random seed given with srand, so you can set a specific random seed for this.
Also, the shuffle method change's the array keys, but this is probably not the best outcome for you, so you may use a different shuffle function:
function shuffle_assoc(&$array, $random_seed) {
srand($random_seed);
$keys = array_keys($array);
shuffle($keys);
foreach($keys as $key) {
$new[$key] = $array[$key];
}
$array = $new;
return true;
}
This function will keep the original keys, but with a different order.

You could rotate the array by a factor.
$factor = 5;
$numbers = array(1,2,3,4);
for ( $i = 0; $i < $factor; $i++ ) {
array_push($numbers, array_shift($numbers));
}
print_r($numbers);
The factor can be randomized, and a function can switch the array back in place, by rotation the other way around.

This can be one of the possible ways.
$result = SortQuestions($answers, 30);
print_r($result);
function SortQuestions($answers, $num)
{
$answers = range(1, $num);
shuffle($answers);
return $answers;
}

Related

Get n number of random values from an array and prevent consecutively repeated values

I want to populate a result array containing values randomly drawn from an input array, but the result array must not have two identical consecutive values.
Additional rules:
The input array of values will contain only unique values and will have at least two values to ensure that it is possible to populate the required result array.
The number of random values may be more or less than the size of the input array.
The result array must not require that all values from the input are used if the number of random values is greater than the input array's size. In other words, the randomly selected values must not be biased for even distribution.
Sample input:
$array = ['one', 'two', 'three', 'four'];
$n = 10;
A non-exhaustive list of possible valid results:
["three","one","three","one","two","one","four","one","three","four"]
["four","three","two","one","two","four","one","three","two","one"]
["two","four","three","one","two","one","four","two","three","one"]
This question was inspired by this deleted question which struggled to ask the question with clear rules and expectations.
To guarantee that the two consecutive values are not the same, keep track of the previous value (or its key) and remove it as a possible random value for the current iteration. Push the random value into the result array, then update the "previous" variable.
array_diff_key() can be used to exclude a specific key before calling array_rand() to return the random key.
Code: (Demo) (Reduced alternative) (The ugly version)
$lastIndex = -1;
$result = [];
for ($x = 0; $x < $n; ++$x) {
$key = array_rand(array_diff_key($array, [$lastIndex => null]));
$result[] = $array[$key];
$lastIndex = $key;
}
echo PHP_EOL . json_encode($result);
Alternatively, you can use unset() to exclude the previous random value, but it is important to not modify the original array or else there may not be enough values to fill the result array. Modifying a copy of the input array will do.
Code: (Demo)
$lastIndex = -1;
$result = [];
for ($x = 0; $x < $n; ++$x) {
$copy = $array;
unset($copy[$lastIndex]);
$key = array_rand($copy);
$result[] = $copy[$key];
$lastIndex = $key;
}
echo PHP_EOL . json_encode($result);

How can I output 2 random values from an array?

I'm trying to make a simple PHP script for school. I need to output 2 random students from the array $leerlingen (Leerlingen = students).
It work's fine when I use echo $leerlingen within the foreach loop, but when I use the return statement it stops executing, because when return is used, it ends the function.
Code:
$leerlingen = array("tobias", "hasna", "aukje", "fred", "sep", "koen", "wahed", "anna", "jackie", "rashida", "winston", "sammy", "manon", "ben", "karim", "bart", "lisa", "lieke");
shuffle($leerlingen);
function maakGroepjes($leerlingen) {
$begin = 1;
foreach ($leerlingen as $leerling) {
if ($begin <= 2) {
echo $leerling;
$begin++;
}
}
}
echo maakGroepjes($leerlingen);
Can anyone tell me how to solve this problem?
You can return only one value inside a function, in this case is an array. I assume that the array have at least two values.
<?php
$leerlingen = array(
"tobias", "hasna", "aukje", "fred", "sep", "koen", "wahed", "anna", "jackie", "rashida", "winston", "sammy", "manon", "ben", "karim", "bart", "lisa", "lieke"
);
shuffle($leerlingen);
function maakGroepjes($leerlingen) {
//your result array
$result = array();
//Picking 2 random entries out of an array to $keys
$keys = array_rand($leerlingen, 2);
//Returning the array with two values
return array($leerlingen[$keys[0]], $leerlingen[$keys[1]]);
}
//assign the values to the vars
list($one, $two) = maakGroepjes($leerlingen);
//printing
echo $one . "<br>\n";
echo $two . "<br>\n";
?>
array_rand and other functions (rand) that rely on libc have a bad standard distribution. I'd always recommend using mt_rand() if you need it to be equally distributed, otherwise some entries will be heavily favored.
This is a good easy replacement for numerical arrays:
function array_mt_rand($array) {
return $array[ mt_rand( 0, count($array)-1 ) ];
}
$one = array_mt_rand($array);
$two = array_mt_rand($array);
You may need some extra checks if you have a small array and always want two distinct values though.
You could always try something like this.
function maakGroepjes($leerlingen) {
do {
$first_student = array_rand($leerlingen);
$second_student = array_rand($leerlingen);
} while ($leerlingen[$first_student] == $leerlingen[$second_student]);
return [$leerlingen[$first_studen], $leerlingen[$second_student]];
}
Which returns this.
Array
(
[0] => manon
[1] => winston
)
Also, the difference between a function printing/echo'ing information and returning information is pretty big.
You can't assign a variable to the echo statement inside of a function, whereas you could assign a variable to the return statement.
I would approach this a little differently.
function maakGroepjes($leerlingen) {
shuffle($leerlingen); // randomize the list of students
return array_chunk($leerlingen, 2); // break in into groups of two and return it
}
Then with
$groepjes = maakGroepjes($leerlingen);
you can generate all of the groups at once. No worries about repetition. This way if you need multiple groups, you can loop over the list of groups. If you really only need one group of two, then that will be
$groepjes[0];
which you can ouptut however you like. A very simple example:
foreach ($groepjes[0] as $student) echo "$student<br>";
Try this:
if ($begin <= 2) {
echo $leerling;
$begin++;
} else {
return
}
What this does is: every time through the loop, it looks at $begin. If it's less than or equal to two, it echoes that student and increments $begin. Otherwise, if it's greater than two, it returns, ending the function (and thus, the loop).
A perhaps better way to do it would be to just look at the ordinal directly:
foreach ($leerlingen as $ord => $leerling) {
echo $leerling;
if ($ord == 1) return;
}
Note the syntax in the loop definition. The "old =>" part sets the ordinal value as a variable as you loop through, letting you see which entry in the array you are currently on. So, this just loops through printing students. When it has printed the second student (remember, arrays are counted 0, 1, 2...) it returns.
Also, you probably don't want to echo maakGroepjes(). That function is what is echoing the student names. You probably don't want to echo the function result unless the function compiles the names into a string and returns the string or something.

php maximum values to show in a list in random order? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Output array elements randomly with PHP
Lets say we have this code:
<? if (isset($specialoffers)) { ?>
<? foreach ($specialoffers as $value) { ?>
<div>product #</div>
<?};?>
<?};?>
For example this list may be different everytime, it may have 1 product, and it may have 58 products. I want do display only 10 products in the list and in a random order.
How to do that?
I would like not to touch the SQL query!
Take a look at the array_rand function which takes one or more random elements from an array.
So, you could do something along the lines of:
foreach (array_rand($specialoffers,10) as $key)
do_something_interesting_with $specialoffers[$key];
$specialoffers = array_splice( shuffle($specialoffers), 0, 9 );
Something like this might work.
You can use shuffle() to randomize the array.
http://php.net/manual/en/function.shuffle.php
You can do that with Reservoir Sampling which is even needed if the number of elements would exhaust your memory (and are presented through an iterator):
$randoms = new RandomIterator($specialoffers, 10);
foreach ($randoms as $value) {
...
}
You can find the source-code of the RandomIterator class as Gist.
Please try example for getting random value from assoc arrays;
function array_random_assoc( $arr, $num = 1) {
$keys = array_keys($arr);
shuffle($keys);
$r = array();
for ($i = 0; $i < $num; $i++) {
$r[$keys[$i]] = $arr[$keys[$i]];
}
return $r;
}
$specialoffers = (array_random_assoc($data_array, 10));

Search Multiple Arrays for

I have officially hit a wall and I cannot figure out the solution to this issue. Any help would be much appreciated! I have tried array_intersect() but it just keeps running against the first array in the function, that wont work.
I have an infinite amounts of arrays (I'll show 4 for demonstration purposes), for example:
// 1.
array(1,2,3,4,5);
// 2.
array(1,3,5);
// 3.
array(1,3,4,5);
// 4.
array(1,3,5,6,7,8,9);
I need to figure out how to search all the arrays and find only the numbers that exist in all 4 arrays. In this example I need to only pull out the values from the arrays - 1, 3 & 5.
PS: In all reality, it would be best if the function could search against a multi dimensional array and extract only the numbers that match in all the arrays within the array.
Thanks so much for your help!
Fun question! This worked:
function arrayCommonFind($multiArray) {
$result = $multiArray[0];
$count = count($multiArray);
for($i=1; $i<$count; $i++) {
foreach($result as $key => $val) {
if (!in_array($val, $multiArray[$i])) {
unset($result[$key]);
}
}
}
return $result;
}
Note that you can just use $multiArray[0] (or any sub-array) as a baseline and check all the others against that since any values that will be in the final result must necessarily be in all individual subarrays.
How about this?
Find the numbers that exist in both array 1 and 2. Then compare those results with array 3 to find the common numbers again. Keep going as long as you want.
Is this what you are getting at?
If it's in a multidimensional array you could
$multiDimensional = array(/* Your arrays*/);
$found = array_pop($multiDimensional);
foreach($multiDimensional as $subArray)
{
foreach($found as $key=>$element)
{
if(!in_array($element, $subArray)
{
unset($found[$key]);
}
}
}
Per your comment on my other question here is a better solution:
<?php
// 1. merge the arrays
$merged_arrays = array_merge( $arr1, $arr2, $arr3, $arr4, ...);
// 2. count the values
$merged_count = array_count_values( $merged_arrays );
// 3. sort the result for elements that only matched once
for( $merged_count as $key => $value ){
if ($value == 1) {
// 4. unset the values that didn't intersect
unset($merged_count($key));
}
}
// 5. print the resulting array
print_r( $merged_count );
Performing iterated in_array() calls followed by unset() is excessive handling and it overlooks the magic of array_intersect() which really should be the hero of any solid solution for this case.
Here is a lean iterative function:
Code: (Demo)
function array_intersect_multi($arrays){ // iterative method
while(sizeof($arrays)>1){
$arrays[1]=array_intersect($arrays[0],$arrays[1]); // find common values from first and second subarray, store as (overwrite) second subarray
array_shift($arrays); // discard first subarray (reindex $arrays)
}
return implode(', ',$arrays[0]);
}
echo array_intersect_multi([[1,2,3,4,5],[1,3,5],[1,3,4,5],[1,3,5,6,7,8,9]]);
// output: 1, 3, 5
This assumes you will package the individual arrays into an indexed array of arrays.
I also considered a recursive function, but recursion is slower and uses more memory.
function array_intersect_multi($arrays){ // recursive method
if(sizeof($arrays)>1){
$arrays[1]=array_intersect($arrays[0],$arrays[1]); // find common values from first and second subarray, store as (overwrite) second subarray
array_shift($arrays); // discard first subarray (reindex $arrays)
return array_intersect_multi($arrays); // recurse
}
return implode(', ',$arrays[0]);
}
Furthermore, if you are happy to flatten your arrays into one with array_merge() and declare the number of individual arrays being processed, you can use this:
(fastest method)
Code: (Demo)
function flattened_array_intersect($array,$total_arrays){
return implode(', ',array_keys(array_intersect(array_count_values($array),[$total_arrays])));
}
echo flattened_array_intersect(array_merge([1,2,3,4,5],[1,3,5],[1,3,4,5],[1,3,5,6,7,8,9]),4);
or replace array_intersect() with array_filter() (slightly slower and more verbose):
function flattened_array_intersect($array,$total_arrays){
return implode(', ',array_keys(array_filter(array_count_values($array),function($v)use($total_arrays){return $v==$total_arrays;})));
}
echo flattened_array_intersect(array_merge([1,2,3,4,5],[1,3,5],[1,3,4,5],[1,3,5,6,7,8,9]),4);

php get two different random array elements [duplicate]

This question already has answers here:
How do I select 10 random things from a list in PHP?
(5 answers)
Closed 8 months ago.
From an array
$my_array = array('a','b','c','d','e');
I want to get two DIFFERENT random elements.
With the following code:
for ($i=0; $i<2; $i++) {
$random = array_rand($my_array); # one random array element number
$get_it = $my_array[$random]; # get the letter from the array
echo $get_it;
}
it is possible to get two times the same letter. I need to prevent this. I want to get always two different array elements. Can somebody tell me how to do that?
Thanks
array_rand() can take two parameters, the array and the number of (different) elements you want to pick.
mixed array_rand ( array $input [, int $num_req = 1 ] )
$my_array = array('a','b','c','d','e');
foreach( array_rand($my_array, 2) as $key ) {
echo $my_array[$key];
}
What about this?
$random = $my_array; // make a copy of the array
shuffle($random); // randomize the order
echo array_pop($random); // take the last element and remove it
echo array_pop($random); // s.a.
You could always remove the element that you selected the first time round, then you wouldn't pick it again. If you don't want to modify the array create a copy.
for ($i=0; $i<2; $i++) {
$random = array_rand($my_array); # one random array element number
$get_it = $my_array[$random]; # get the letter from the array
echo $get_it;
unset($my_array[$random]);
}
You can shuffle and then pick a slice of two. Use another variable if you want to keep the original array intact.
$your_array=[1,2,3,4,5,6,7];
shuffle($your_array); // randomize the order
$your_array = array_slice($your_array, 0, 2); //pick 2
foreach (array_intersect_key($arr, array_flip(array_rand($arr, 2))) as $k => $v) {
echo "$k:$v\n";
}
//or
list($a, $b) = array_values(array_intersect_key($arr, array_flip(array_rand($arr, 2))));
here's a simple function I use for pulling multiple random elements from an array.
function get_random_elements( $array, $limit=0 ){
shuffle($array);
if ( $limit > 0 ) {
$array = array_splice($array, 0, $limit);
}
return $array;
}
Here's how I did it. Hopefully this helps anyone confused.
$originalArray = array( 'first', 'second', 'third', 'fourth' );
$newArray= $originalArray;
shuffle( $newArray);
for ($i=0; $i<2; $i++) {
echo $newArray[$i];
}
Get the first random, then use a do..while loop to get the second:
$random1 = array_rand($my_array);
do {
$random2 = array_rand($my_array);
} while($random1 == $random2);
This will keep looping until random2 is not the same as random1

Categories