I'm trying to generate a unique randomized array with exceptions,
i got this far:
function rand_except($min, $max,$no_numbers, $except) {
//loop until you get a unique randomized array without except number
$end=false;
while (!$end){
$numbers = rand_array($min, $max,$no_numbers);//get unique randomized array
if(!in_array($except,$numbers)){
$end=true;
break;
}
}
return $numbers;
}
but now i want the function to loop until the except parameter isn't in the array
I suspect it would be easier to solve this problem by updating the rand_array function (or writing a modified version of it) to generate the array without the $except value to start with.
If that is not an option, here is one possible solution that doesn't involve calling the rand_array function over and over again:
$numbers = rand_array($min, $max-1, $no_numbers);
for ($i = 0; $i < count($numbers); $i++) {
if ($numbers[$i] >= $except) {
$numbers[$i] += 1;
}
}
You call the rand_array function but specify one less than the actual maximum number you want in the array. Then you loop over the results, and any value that is greater than or equal to the $except value, you increment by 1.
This is assuming that the $except value is in the range $max to $max. If not, you can just return rand_array($min, $max, $no_numbers); as is.
Related
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);
I have an array that carries a definite number of dimensions so I'm not really looking at something recursive (Unless maybe for maintainability sake in the future). It's a numeric array gotten from the database with each row holding another array. Each of those level 2 arrays contain strings like
var1, var2 , var3
And so on. Note the irregular appearance of commas in the string. So I intend to break the comma delimited string in the third level then log them in the final array but I get an error saying I am supplying an null array. So I want to know why it says the array is null and how I can make it recognise that as a valid array. My code goes below:
function fetch_each($arr) {
$temp = array();
for ($i = 0; $i < count($arr); $i++) {
for ($j = 0; $j < count($arr[$i]); $j++) {
array_reduce(preg_split("/[\s,]+/", $arr[$i][$j]), function($a, $b) {
return array_push($temp, $a, $b);
});
}
}
return $temp;
}
PS: Please don't mark as duplicate. I don't want to copy someone else's code but want to understand why this does not work. Thanks.
You have this problem because $temp is not visible in the function block.
To solve that, you must use the keyword use (variable_name) next to the function definition as in this example :
array_reduce(preg_split("/[\s,]+/", $arr[$i][$j]), function($a, $b) use (&$temp) {
return array_push($temp, $a, $b);
});
Just a remark, $a will contain the result of array_push
Returns:int the new number of elements in the array.
So you can remove it from the array_push() instruction to keep a clean array with only splitted strings
SO,
The problem
It's well known about pseudo-random numbers. 'Pseudo' actually means, that, despite they are random (i.e. unpredictable) in general, they still will be same in sequence, in which same generator init value was used. For example, in PHP there's mt_srand() function to do that. Example:
mt_srand(1);
var_dump(mt_rand(), mt_rand(), mt_rand());
-no matter, how many time we'll launch our script: generated three numbers will always be same in sequence.
Now, my issue is how to do the same - but for shuffling array. I.e. I want to create a function, which will accept input array to shuffle and seed. Within same seed value shuffling must have consecutive same order. I.e. let we call that function shuffleWithSeed() - and then following should work for every script launch:
$input = ['foo', 'bar', 'baz'];
$test = shuffleWithSeed($input, 1000);//1000 is just some constant value
var_dump($test); //let it be ['bar', 'foo', 'baz']
$test = shuffleWithSeed($test, 1000);
var_dump($test); //let it be ['baz', 'foo', 'bar']
$test = shuffleWithSeed($test, 1000);
var_dump($test); //let it be ['baz', 'bar', 'foo']
//...
-i.e. no matter how many times we'll do shuffle for our array - I want for the next script launch ordering sequence will be always the same within one seed value.
My approach
I have in mind this algorithm:
Initialize random numbers generator with passed seed
Generate N random numbers, where N is the number of $input members
Sort numbers from step 2
Make corresponding numbers be dependent from $input keys.
I've implemented this in:
function shuffleWithSeed(array $input, $seed=null)
{
if(!isset($seed))
{
shuffle($input);
return $input;
}
if(!is_int($seed))
{
throw new InvalidArgumentException('Invalid seed value');
}
mt_srand($seed);
$random = [];
foreach($input as $key=>$value)
{
$random[$key] = mt_rand();
}
asort($random);
$random = array_combine(array_keys($random), array_values($input));
ksort($random);
return $random;
}
-now, also found Fisher-Yates algorithm - but not sure if it can work with pseudorandom numbers (i.e. with seed)
The question
As you can see, I'm doing two sorts in my function - first by values and second by keys.
Can this be done with one sort? Or without sort at all? Input array could be large, so I want to avoid this.
However, may be my algorithm is not well? If yes, what other options could be suggested?
Here's a copy and paste of a function I have implemented a while ago for exactly this purpose:
/**
* Shuffles an array in a repeatable manner, if the same $seed is provided.
*
* #param array &$items The array to be shuffled.
* #param integer $seed The result of the shuffle will be the same for the same input ($items and $seed). If not given, uses the current time as seed.
* #return void
*/
protected function seeded_shuffle(array &$items, $seed = false) {
$items = array_values($items);
mt_srand($seed ? $seed : time());
for ($i = count($items) - 1; $i > 0; $i--) {
$j = mt_rand(0, $i);
list($items[$i], $items[$j]) = array($items[$j], $items[$i]);
}
}
It implements a simple Fisher-Yates shuffle with a seeded random number generator.
I have an array that contains both strings and numeric values, While using this code I keep getting the highest string value, how to get only numeric max values from the array?
$maxprice = max($textpieces);
I have also tried this but this just returns the number of keys in the array
$maxprice = max(array_search($textpieces));
thanks for your help! I have looked but the max function here does not give any information http://php.net/manual/en/function.max.php
I have also looked at this solution Find highest max() of mixed array but is it possible to get it without building an custom function?
$maxprice = max(array_filter($textpieces, 'is_numeric'));
It at first filters only entries that contains digits only (integers) and then extracts max() from it
The following solution:
$max = array_reduce($textpieces, function($max, $i) {
if (is_numeric($i) && $i > $max) {
return $i;
}
return $max;
});
var_dump($max);
could be a bit more performance (from both CPU and memory perspectives) but is more complicated as well.
A non-empty zero-indexed array A consisting of N integers is given. The first covering prefix of array A is the smallest integer P such that $0 \leq P < N$ and such that every value that occurs in array A also occurs in sequence $A[0], A[1], \ldots, A[P]$.
For example, the first covering prefix of array A such that
A[0]=2 A[1]=2 A[2]=1 A[3]=0 A[4]=1
is 3, because sequence A[0], A[1], A[2], A[3] equal to 2, 2, 1, 0 contains all values that occur in array A.
Write a function
int ps(int[] A);
that given a zero-indexed non-empty array A consisting of N integers returns the first covering prefix of A. Assume that $N <= 1,000,000$. Assume that each element in the array is an integer in range [0..N-1].
For example, given array A such that A[0]=2 A[1]=2 A[2]=1 A[3]=0 A[4]=1
the function should return 3, as explained in the example above.
This is a very short solution. Pretty but won't scale well.
function ps($A) {
$cp = 0; // covering prefix
$unique = array_unique($A); // will preserve indexes
end($unique); // go to end of the array
$cp = key($unique); // get the key
return $cp;
}
Here's a simple way :
function covering_prefix ( $A ) {
$in=array();
$li=0;
$c=count($A);
for($i=0 ;$i<$c ; $i++){
if (!isset($in[$A[$i]])){
$in[$A[$i]]='1';
$li=$i;
}
}
return $li;
}
Here is a solution using ruby
def first_covering_prefix(a)
all_values = a.uniq
i = 0
a.each do |e|
all_values.delete(e)
if all_values.empty?
return i
end
i = i + 1
end
end
it's a 83% answer, because of use of in_array, a better solution already proposed by ronan
function solution($A) {
// write your code in PHP5
$in=array();
$li=0;
for ($i=0; $i < count($A); $i++) {
# code...
if (!in_array($A[$i], $in)){
$in[]=$A[$i];
$li=$i;
}
}
return $li;
}