Shuffles Random Numbers with no repetition in Javascript/PHP - php

I've searched through some of the answers here but it doesn't seem the thing that I needed or I just don't know how to apply it though.
I haven't started any codes and I'm only thinking on how to do it and I have no idea how to do it. I need your help guys.
Let's assume that I have an array which consists of these values below
[1,2,3,4,5,6,7,8,9]
And I need to shuffle it without repeating the position of each numbers of the last result. so it would probably like
[5,3,9,6,2,8,1,4,7]
if I shuffle it again it would be like
[4,7,2,1,8,3,6,9,5]
And so on.
Well I don't know if there' any relevance to it but, would rather not to use rand() though. Any solution for this stuff?

Try this,
$count = 15;
$values = range(1, $count);
shuffle($values);
$values = array_slice($values, 0, 15);
OR
$numbers = array();
do {
$possible = rand(1,15);
if (!isset($numbers[$possible])) {
$numbers[$possible] = true;
}
} while (count($numbers) < 15);
print_r(array_keys($numbers));
may this help you.

What you want to do is to add elements from your array to another array, randomly, but to ensure that elements are not in the same indexed position. Try this:
$array = [1,2,3,4,5,6,7,8,9];
$new = array();
for($i = 0; $i < $array.length; $i++){
$rand = $i;
do {
$rand = Math.floor( Math.random() * ( $array.length + 1 ) );
} while ($rand == $i || array_key_exists($rand, $new))
// Check that new position is not equal to current index
// and that it doesnt contain another element
$new[$rand] = $array[i];
}
Not the most efficient, but guaranteed to put elements in non same indices.

You can use the variant of the Fisher-Yates-Shuffle which does biased randomly choose the swapped element and is known as Sattolo's algorithm:
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * i); // no +1 here!
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
That way, every element is guaranteed to be swapped and not appear in the same position as before.

This makes the values of the array not repeat any previous position for n shuffles (i'm using half of the array's size as n, after that i restart the forbidden indexes). At the end a modified this version to make it not repeat the current position.
For this you will have to save a history of all the index where each value of the orignal array has been. To do this i've added a little more complexity to your numbers
var numberArray = [{value:1, unavailable_indexes:[0]},
{value:2, unavailable_indexes:[1]},
{value:3, unavailable_indexes:[2]},
{value:4, unavailable_indexes:[3]},
{value:5, unavailable_indexes:[4]},
{value:6, unavailable_indexes:[5]},
{value:7, unavailable_indexes:[6]},
{value:8, unavailable_indexes:[7]},
{value:9, unavailable_indexes:[8]}
];
this way you have the number in value and an array of all the positions where it has been. Next we need to run all the array and switch numbers around.
var arrayLen = numberArray.length-1;
$.each(numberArray, function(index, value){
var newIndex;
//restart the array when half of the index have been covered or it will take awhile to get a random index that wasn't used
if(value.unavailable_indexes.length >= numberArray.length/2)
value.unavailable_indexes = [index];//restart the unavailable indexes with the current index as unavailable
do{
newIndex = Math.floor(Math.random()*arrayLen);
//verify if you can swap the 2 values, if any of them have been on the destination index get another random index
}while($.inArray(value.unavailable_indexes, newIndex) || $.inArray(numberArray[newIndex].unavailable_indexes, index));
numberArray[index] = numberArray[newIndex];
numberArray[newIndex] = value;
})
after all the array has been moved around you need to save the positions where they landed
$.each(numberArray, function(index, value){
value.unavailable_indexes.push(index);
}
EDIT: if you just want to prevent it from just repeating the previous position then make unavailable_indexes hold the last position it was in and replace the do{...}while() with:
do{
newIndex = Math.floor(Math.random()*arrayLen);
}while(newIndex != value.unavailable_indexes)
and the last method would look like:
$.each(numberArray, function(index, value){
value.unavailable_indexes = index;
}

I just came up with the following code, to combat a problem I was having where sometimes my randomly shuffled array would end up in it's original order (was it too random, or not random enough?).
How it works, it loops over a while loop until the $isDifferent variable becomes true, which can only happen if the arrays do not match. This perhaps may work similarly to the Fisher-Yates method, although when I tried that, I still ended up with matching arrays on occasion.
This solution is written in PHP but could easily be converted to JavaScript.
guaranteedShuffle($array){
$isDifferent = false;
while(!$isDifferent){
$arrayCopy = $array;
shuffle($arrayCopy);
if($array !== $arrayCopy){
$isDifferent = true;
}
}
return $arrayCopy;
}
Usage:
$array = ['1','2'];
$shuffled = guaranteedShuffle($array);

You can shuffle using Fisher Yates Shuffle
function fisherYates ( myArray ) {
var i = myArray.length, j, tempi, tempj;
if ( i == 0 ) return false;
while ( --i ) {
j = Math.floor( Math.random() * ( i + 1 ) );
tempi = myArray[i];
tempj = myArray[j];
myArray[i] = tempj;
myArray[j] = tempi;
}
}
See this reference

Related

How do I move a group of items in a PHP array below the next group of items?

In an Array with normal numerical keys ( $file_array_for_editing ), I want to move a set of consecutive X number of values below the next set of X values.
Example: If a group of X values is 10, then in an Array of 50, I have 5 sets of 10 values. I want to move the 10 values in set 3 - below the 10 values in set 4.
So that : Set1(10 values) , Set2(10 values) , Set3(10 values) , Set4(10 values) , Set5(10 values)
Becomes: Set1(10 values) , Set2(10 values) , Set4(10 values) , Set3(10 values) Set5(10 values)
Since I don't know a better way, this is how I have done it now - by what I believe is switching the values:
$file_array_for_editing = [];
for($i=1; $i <= 50; $i++) {
$file_array_for_editing[] = $i;
}
$number_in_one_set = 10 ;
$set_number_to_move = 3 ;
$f = $file_array_for_editing ;
$s = ($set_number_to_move - 1) * $number_in_one_set ; // (3 - 1) * 10 = 20
list(
$f[$s+0],$f[$s+1],$f[$s+2],$f[$s+3],$f[$s+4],$f[$s+5],$f[$s+6],$f[$s+7],$f[$s+8],$f[$s+9],$f[$s+10],$f[$s+11],$f[$s+12],$f[$s+13],$f[$s+14],$f[$s+15],$f[$s+16],$f[$s+17],$f[$s+18],$f[$s+19]
) =
[$f[$s+10],$f[$s+11],$f[$s+12],$f[$s+13],$f[$s+14],$f[$s+15],$f[$s+16],$f[$s+17],$f[$s+18],$f[$s+19],$f[$s+0],$f[$s+1],$f[$s+2],$f[$s+3],$f[$s+4],$f[$s+5],$f[$s+6],$f[$s+7],$f[$s+8],$f[$s+9]];
$file_array_for_editing = $f ;
At the end, the numerical keys stay properly in order, from 0 to 49.
I need help finding a way to do this where I can vary the $number_in_one_set
If possible, I would appreciate help finding a solution where it is clearly apparent what is being done, so that after a year, when I look at the code again, it will be easy to understand. (as in, even now, I just vaguely understand why the list() above works)
Possibly following might help you on your way. You can change the 'sets' you'd like to switch using array $switch.
<?php
$arr = [];
for($i=1; $i <= 50; $i++) {
$arr[] = $i;
}
$set = 10; // each set has 10 items
$switch = [3, 4]; // switch element 3 vs. 4
$newArr = array_chunk($arr, $set); // chop array in chunks of size $set
// swap
$tmp = $newArr[$switch[0]]; // $temp=$newArr[3]
$newArr[$switch[0]] = $newArr[$switch[1]]; // $newArr[3]=newArr[4]
$newArr[$switch[1]] = $tmp; // $newArr[4]=$newArr[3]
$result = [];
array_walk_recursive($newArr, function($el) use (&$result) { $result[] = $el; }); // flatten array
EDIT
array_walk_recursive() applies the callback function (function($el)) to each element of nested array $newArr.
Each element of array $newArr, referenced by $el will be pushed into array $result, a flat indexed array.
Also, because the anonymous function (closure) writes to flat array $result which is defined in the parent scope, we need to employ the use language construct.
source manual: Closures may also inherit variables from the parent scope. Any such variables must be passed to the use language construct.
working demo
#jibsteroos - I really like the method you provided (which #CBroe also suggested) - reason being that it allows any two sets anywhere within the original array to be swapped.
After some thought, I find I prefer using the method below, even though it is a little long winded - reason being my use case is only to exchange a set with the one below it.
<?php
$arr = []; // Setting up a sample array
for($i=1; $i <= 50; $i++) {
$arr[] = $i;
}
$set_size = 10 ; // 10 items per set
$set = 3 ; // set to move down
$set_to_move = ($set - 1) * $set_size; // start position of this set in the array - 20
print_r($arr) ;
echo '<hr>';
$slice1 = array_slice($arr,0,$set_to_move);
print_r($slice1) ; // from the start of the array - to the start of the set to move
echo '<hr>';
$slice2 = array_slice($arr,$set_to_move,$set_size);
print_r($slice2) ; // the set to move
echo '<hr>';
$slice3 = array_slice($arr,$set_to_move + $set_size,$set_size);
print_r($slice3) ; // the set after the set to move
echo '<hr>';
$slice4 = array_slice($arr,$set_to_move + (2 * $set_size));
print_r($slice4) ; // the rest of the array
echo '<hr>';
$arr_edited = array_merge($slice1,$slice3,$slice2,$slice4,);
print_r($arr_edited) ; // All 4 arrays combined in the new order
echo '<hr>';

Array with only 10 most recent values

I have an array with multiple elements. I want to keep only the 10 most recent values. So I am reversing the array in a loop, checking if the element is within the first 10 range and if not, I unset the element from the array.
Only problem is that the unset does not work. I am using the key to unset the element, but somehow this does not work. The array keeps on growing. Any ideas?
$currentitem = rand(0,100);
$lastproducts = unserialize($_COOKIE['lastproducts']);
$count = 0;
foreach(array_reverse($lastproducts) as $key => $lastproduct) {
if ($count <= 10) {
echo "item[$key]: $lastproduct <BR>";
}
else {
echo "Too many elements. Unsetting item[$key] with value $lastproduct <BR>";
unset($lastproducts[$key]);
}
$count = $count + 1;
}
array_push($lastproducts, $currentitem);
setcookie('lastproducts', serialize($lastproducts), time()+3600);
I'd use array_slice ( http://php.net/array_slice ) perhaps like:
$lastproducts = unserialize($_COOKIE['lastproducts']);
// add on the end ...
$lastproducts[] = $newproduct;
// start at -10 from the end, give me 10 at most
$lastproducts = array_slice($lastproducts, -10);
// ....
You can use array_splice($input, $offset) function for this purpose.
$last_items_count = 10;
if(count($lastproducts) >= $last_items_count) {
$lastproducts = array_splice($lastproducts, count($lastproducts) - $last_items_count);
}
var_dump($lastproducts);
I hope this code helps.
For more information, here is the documentation:
http://php.net/manual/en/function.array-splice.php
I think a better way to select last 10 is:
$selection = array();
foreach(array_reverse($lastproducts) as $key => $lastproduct) {
$selection[$key] = $lastproduct;
if (count($selection)>=10) break;
}
Finally, $selection will have last 10 (or less) products.
Works great using array_splice and array_slice, thanks! :)
$lastproducts = unserialize($_COOKIE['lastproducts']);
// remove this product from array
$lastproducts = array_diff($lastproducts, array($productid));
// insert product on first position in array
array_splice($lastproducts, 0, 0, $productid);
// keep only first 15 products of array
$lastproducts = array_slice($lastproducts, 0, 15);

multi-dimension array duplicate - performance

What is the best way to check if an multi-dimension array contains 2 or more equal value and only return the first one .
ex : i have an array of people that contain
[0][firstName] = 'John';
[0][lastName] = 'Doe';
[N][firstName] = 'Marco';
[N][lastName] = 'Polo';
[120][firstName] = 'John';
[120][lastName] = 'Doe';
Should detect that the index 120 is a duplicate and remove it .
I'm looking for the best performance , i don't want to loop on the array and check every time if i have the value or not .
Is there something faster ?
It is the element distinctness problem which is basically O(NlogN) via sorting and iterating (after sorting, duplicates will be adjacent to each other - so easy to detect them)
However, it can be done also in O(N) on average and with O(N) additional space by storing all elements to a hash table while iterating, and breaking if an element already exists.
You might also want to store the original index of each element if you will later need it (and don't use the index as key).
pseudo code:
map <- empty hash map
for each element e with idx i in list (in ascending order of i):
if (map.contains(e)):
e is a dupe, the first element is in index map.get(e)
else:
map.add(e,i)
You can try
// Generate Possible name with duplicate
$names = array("John","Doe","Polo","Marco","Smith");
$array = array();
for($i = 0; $i < 20; $i ++) {
$key = mt_rand(0, 1000);
$array[$key]["firstName"] = $names[array_rand($names)];
$array[$key]["lastName"] = $names[array_rand($names)];
}
// Start Sorting process
ksort($array);
// Start Storage
$data = $hash = array();
// Loop and porpulate new array
foreach ( $array as $k => $v ) {
$h = sha1($v['firstName'] . $v["lastName"]);
isset($hash[$h]) or $data[$k] = $v and $hash[$h] = 1;
}
var_dump($data);

PHP Array shuffle, keeping unique

this is my first php script and problem, I've searched hours with no conclusion other than looping a function" too many laterations". but it doesn't solve my problem I've never studied programming or what ever so I'm hoping that there is an educated person to fill me in on this:
I have an array that contains 120 elements; consists of duplicates eg:
myArray = [0]= item_1, [1] = item _1, [2] = item_2, [3] = item_3 ect..
Briefly I'm trying to make a flash php pokermachine but I need these items in the array to be shuffled BUT I do not want the duplicates to be next to each other after the shuffle but I need the duplicates to be still in the array
I can't do a loop function to check this because it will change the shuffle too many times which will effect the odds of the game: below is what I currently have:
/ * Removed the link here that is no longer available */
you may notice at times it will double up with 2 items in the same reel
Basically I created the virtual reel dynamically with php.ini file
these values are repeatedly pushed into an array($virtualreel) so the value may appear 10 times in the reel and another value will appear 5 times variating the odds. Then after I take a random slice() from the $virtualreel to display 3 vars from this reel and repeat the loop 4 more times for the other reels, also I only can shuffle once as I want the slice() to be from the same reels array order
I only shuffle every new spin not running loop functions to shuffle if I double up on a slice(array,3 items).
hope I've explained what I'm after well enough to give you guys an idea.
You can use this function:
<?php
function shuffleArray($myArray) {
$value_count = array_count_values($myArray);
foreach($value_count as $key=>$value) {
if ($value > count($myArray)/2) {
return false;
}
}
$last_value = $myArray[count($myArray) - 1];
unset($myArray[count($myArray) - 1]);
$shuffle = array();
$last = false;
while (count($myArray) > 0) {
$keys = array_keys($myArray);
$i = round(rand(0, count($keys) - 1));
while ($last === $myArray[$keys[$i]]) {
$i = round(rand(0, count($keys) - 1));
}
$shuffle[] = $myArray[$keys[$i]];
$last = $myArray[$keys[$i]];
unset($myArray[$keys[$i]]);
}
if ($last_value === $last) {
$i = 0;
foreach($shuffle as $key=>$value) {
if ($value !== $last_value) {
$i = $key;
break;
}
}
array_splice($shuffle, $i + 1, 0, $last_value);
} else {
$shuffle[] = $last_value;
}
return $shuffle;
}
print_r(shuffleArray(array(1,5,5,3,7,7)));
Why not just:
Edit :
$shuffled = array();
while(count($to_shuffle) > 0):
$i = rand(0, count($to_shuffle)-1);
$shuffled[] = $to_shuffle[$i];
array_splice($to_shuffle, $i, 1,null);
endwhile;
I think this is what you were expecting, if you don't mind not preserving the association between keys and values.

PHP in_array() horrible performance. Fatest way to search array for value

I have the following simple code to test against collision on a primary key I am creating:
$machine_ids = array();
for($i = 0; $i < 100000; $i++) {
//Generate machine id returns a 15 character alphanumeric string
$mid = Functions::generate_machine_id();
if(in_array($mid, $machine_ids)) {
die("Collision!");
} else {
$machine_ids[] = $mid;
}
}
die("Success!");
Any idea why this is taking many minutes to run? Anyway to speed it up?
for($i = 0; $i < 100000; $i++)
{
//Generate machine id returns a 15 character alphanumeric string
$mid = Functions::generate_machine_id();
if (isset($machine_ids[$mid]))
{
die("Collision!");
}
$machine_ids[$mid] = true;
}
For this, use $mid as keys, and dummy value as value. Specifically, instead of
if(in_array($mid, $machine_ids)) {
die("Collision!");
} else {
$machine_ids[] = $mid;
}
use
if(isset($machine_ids[$mid])) {
die("Collision!");
} else {
$machine_ids[$mid] = 1;
}
At the end you can extract the array you originally wanted with array_keys($machine_ids).
This should be much faster. If it is still slow, then your Functions::generate_machine_id() is slow.
EDITED to add isset as per comments.
Checking for array membership is a O(n) operation, since you have to compare the value to every element in the array. After you add a whole bunch of stuff to the array, naturally it gets slower.
If you need to do a whole bunch of membership tests, as is the case here, you should use a different data structure that supports O(1) membership tests, such as a hash.
Refactor your code so that it uses a associated array to hold the machine IDs and use isset to check
if( isset($machine_id[$mid]) ) die("Collision");
$machine_ids[$mid] = $mid;
Using isset should be faster
If you need best performance for your case, you need store your data as array key and
use isset or array_key_exists(since php >= 7.4 array_key_exists is now as fast as isset) instead in_array.
Attention. It is true that isset on a hash map is faster than searching through an array for a value (in_array), but keep in mind
that converting an array of values, ["foo", "bar", "baz"], to a hash
map, ["foo" => true, "bar" => true, "baz" => true], incurs a memory
cost (as well as potentially constructing the hash map, depending on
how and when you do it). As with all things, you'll have to weigh the
pros & cons for each case to determine if a hash map or array (list)
of values works best for your needs. This isn't specific to PHP but
more of a general problem space of computer science.
And some performance tests from https://gist.github.com/alcaeus/536156663fac96744eba77b3e133e50a
<?php declare(strict_types = 1);
function testPerformance($name, Closure $closure, $runs = 1000000)
{
$start = microtime(true);
for (; $runs > 0; $runs--)
{
$closure();
}
$end = microtime(true);
printf("Function call %s took %.5f seconds\n", $name, $end - $start);
}
$items = [1111111];
for ($i = 0; $i < 100000; $i++) {
$items[] = rand(0, 1000000);
}
$items = array_unique($items);
shuffle($items);
$assocItems = array_combine($items, array_fill(0, count($items), true));
$in_array = function () use ($items) {
in_array(1111111, $items);
};
$isset = function () use ($assocItems) {
isset($items[1111111]);
};
$array_key_exists = function () use ($assocItems) {
array_key_exists(1111111, $assocItems);
};
testPerformance('in_array', $in_array, 100000);
testPerformance('isset', $isset, 100000);
testPerformance('array_key_exists', $array_key_exists, 100000);
Output:
Function call in_array took 5.01030 seconds
Function call isset took 0.00627 seconds
Function call array_key_exists took 0.00620 seconds

Categories