I have a random number from 0 to 100 (x) and different ranges. How to check in which range the number is?
Example:
Given
Case1 (0 - 40),
Case2 (40 - 60),
Case3 (60 - 75),
Case4 (75 - 85),
Case5 (85 - 100)
and x = 50, the result should be Case2.
Here's one example on how to check which range a given number belongs to. Define an associative ranges array, where cases are keys and bottom limits are key values. Then loop the array through and compare the given number to the limits. If the number equals or is bigger than the limit, return the limit's case.
$range = in_which_range( rand(0,100) );
function in_which_range( int $number ) {
foreach ( ranges() as $case => $bottom_limit ) {
if ( $number >= $bottom_limit ) {
return $case;
}
}
}
function ranges() {
return array(
'case5' => 85,
'case4' => 75,
'case3' => 60,
'case2' => 40,
'case1' => 0,
);
}
So, you have 5 ranges:
Range #0: 0-40
Range #1: 41-60 (note that this range starts from 41, not from 40; otherwise you will get intersecting ranges that's probably not what you originally wanted)
Range #2: 61-75
Range #3: 76-85
Range #4: 86-100
You can store your ranges in a single array, just put there all range borders sorted.
$ranges = [0, 40, 60, 75, 85, 100];
Note that you have 5 ranges, but 6 elements in array - that's important. Now you can easily check for each range:
$findRange = function(int $x, int ...$ranges): ?int
{
for ($i = 0; $i < count($ranges) - 1; $i++) {
$start = 0 == $i ? $ranges[$i] : $ranges[$i] + 1;
$end = $ranges[$i + 1];
if ($x >= $start && $x <= $end) {
return $i;
}
}
return null;
};
foreach ([0, 41, 86, 101] as $x) {
$range = $findRange($x, ...$ranges);
$message = null === $range
? "Value {$x} is not in the range"
: "Value {$x} is in range #{$range}";
echo $message, PHP_EOL;
}
This will output:
Value 0 is in range #0
Value 41 is in range #1
Value 86 is in range #4
Value 101 is not in the range
Here is the working demo.
Related
Let's say I have this array of numbers:
$arr = [100, 60, 30, 22, 1]
and it's based off a 1 - 100 range. And I want it based of a 1 - 5 range.
I have found this answer here: How to scale down a range of numbers with a known min and max value which details:
Array.prototype.scaleBetween = function(scaledMin, scaledMax) {
var max = Math.max.apply(Math, this);
var min = Math.min.apply(Math, this);
return this.map(num => (scaledMax-scaledMin)*(num-min)/(max-min)+scaledMin);
}
But I am not sure how I would translate that to PHP/Laravel.
Not sure if this are the correct results, but it does what you described:
$arr = [100, 60, 30, 22, 1];
function scale(array $array, float $min, float $max): array
{
$lowest = min($array);
$highest = max($array);
return array_map(function ($elem) use ($lowest, $highest, $min, $max) {
return ($max - $min) * ($elem - $lowest) / ($highest - $lowest) + $min;
}, $array);
}
echo json_encode(scale($arr, 1, 10));
// [10,6.363636363636363,3.6363636363636362,2.909090909090909,1]
Could you tell why Codility tell me the next error, please?
Running solution... Compilation successful.
Example test: [-1, 3, -4, 5, 1, -6, 2, 1] Output (stderr): Invalid
result type, int expected. RUNTIME ERROR (tested program terminated
unexpectedly)
Detected some errors.
My solution was wrote on PHP.
function solution($A) {
$N = count($A);
$Ps = array();
foreach ( $A as $KeyP => $P ) {
$sum = 0;
if ( $KeyP == 0 ) {
for ( $x = 1; $x < $N; $x++ ) {
$sum += $A[$x];
}
if ( $sum == $P ) {
$Ps[] = $KeyP;
}
}
else {
if ( ($KeyP+1) == $N ) {
for ( $z = 0; $z < $KeyP; $z++) :
$sum += $A[$z];
endfor;
if ( ( $sum >= 0 ) AND ( $sum < $N ) ) {
$Ps[] = $KeyP;
}
}
else {
$sum1 = 0;
$sum2 = 0;
for ( $z = 0; $z < $KeyP; $z++ ) :
$sum1 += $A[$z];
endfor;
for ( $y = ( $KeyP+1 ); $y <= ($N-1); $y++ ) :
$sum2 += $A[$y];
endfor;
if ( $sum1 == $sum2 ) {
if ( $sum1 < $N ) {
$Ps[] = $KeyP;
}
}
}
}
}
return ( count($Ps) <= 0 ) ? -1: $Ps;
}
The output of my function given the next array has been:
array(-1, 3, -4, 5, 1, -6, 2, 1);
Ouput
Array ( [0] => 1 [1] => 3 [2] => 7 )
It's just like they request me in the task but Codility show me all those errors.
The demo task is below:
This is a demo task.
A zero-indexed array A consisting of N integers is given. An
equilibrium index of this array is any integer P such that 0 ≤ P < N
and the sum of elements of lower indices is equal to the sum of
elements of higher indices, i.e. A[0] + A[1] + ... + A[P−1] = A[P+1]
+ ... + A[N−2] + A[N−1]. Sum of zero elements is assumed to be equal to 0. This can happen if P = 0 or if P = N−1.
For example, consider the following array A consisting of N = 8
elements:
A[0] => -1
A[1] => 3
A[2] => -4
A[3] => 5
A[4] => 1
A[5] => -6
A[6] => 2
A[7] => 1
P = 1 is an equilibrium index of this array, because:
A[0] = −1 = A[2] + A[3] + A[4] + A[5] + A[6] + A[7] P = 3 is an
equilibrium index of this array, because:
A[0] + A[1] + A[2] = −2 = A[4] + A[5] + A[6] + A[7] P = 7 is also an
equilibrium index, because:
A[0] + A[1] + A[2] + A[3] + A[4] + A[5] + A[6] = 0 and there are no
elements with indices greater than 7.
P = 8 is not an equilibrium index, because it does not fulfill the
condition 0 ≤ P < N.
Write a function:
function solution($A);
that, given a zero-indexed array A consisting of N integers, returns
any of its equilibrium indices. The function should return −1 if no
equilibrium index exists.
For example, given array A shown above, the function may return 1, 3
or 7, as explained above.
Assume that:
N is an integer within the range [0..100,000]; each element of array A
is an integer within the range [−2,147,483,648..2,147,483,647].
Complexity:
expected worst-case time complexity is O(N); expected worst-case space
complexity is O(N), beyond input storage (not counting the storage
required for input arguments). Elements of input arrays can be
modified.
Thank you.
for the Codility error please check this post :
https://stackoverflow.com/a/19804284/4369087
Try this one it's more readable, in this solution I'm making the code more readable by introducing two functions.
sumRight(), sumLeft() in which I use built in php functions.
array_sum() : Calculate the sum of values in an array :
it returns the sum of values as an integer or float; 0 if the array is empty.
array_slice(): Extract a slice of the array: array_slice() returns
the sequence of elements from the array as specified by the
offset and length parameters.
So every time we loop over the array we calculate to sum of the right and left slice of the array from the given position $i :
<?php
function solution(array $a)
{
$result = [];
$count = count($a);
for($i = 0; $i < $count; $i++) {
if(sumLeft($a, $i-1) === sumRight($a, $i+1)) {
$result[] = $i;
}
}
return count($result) ? $result : -1;
}
function sumRight(array $a, float $position): float
{
return array_sum(array_slice($a, $position));;
}
function sumLeft(array $a, float $position): float
{
return array_sum(array_slice($a, 0, $position + 1));
}
echo "<pre>";
print_r(solution([-1, 3, -4, 5, 1, -6, 2, 1]));
output:
Array
(
[0] => 1
[1] => 3
[2] => 7
)
The Tape Equilibrium questions is worded super confusingly.
P is the values of the array $A
P values must be greater than 0 and less than N
N is the total number of indexes in the array $A
$A is a non-empty array consisting of N integers
The part "0 < P < N" doesn't make sense. If N is the integers in non-empty array A then N should be the length of the array and data such as [100,200] should not work because P=100 & P=200, both of which are greater than 2.
My opinion is that the explanation is poorly worded. However, the example is clear, so the solution is relatively straight forward if you can get past the opening text.
A non-empty array A consisting of N integers is given. Array A represents numbers on a tape.
Any integer P, such that 0 < P < N, splits this tape into two
non-empty parts: A[0], A[1], ..., A[P − 1] and A[P], A[P + 1], ...,
A[N − 1].
The difference between the two parts is the value of: |(A[0] + A[1] +
... + A[P − 1]) − (A[P] + A[P + 1] + ... + A[N − 1])|
In other words, it is the absolute difference between the sum of the
first part and the sum of the second part.
The part that stumped me initially was that I was returning NULL for erroneous array input and receiving a Codility error that NULL was returned when it was expecting an INT. I thought, how can I return an INT when there is no solution for bad data? Turns out, they want you to return a zero.
Here is my 100/100 PHP solution:
function solution($A) {
if ( empty($A) ) {
return 0;
}
$count = count($A);
if ($count == 1) {
return $A[0];
}
for ($i=0, $max_position = $count - 1; $i<$max_position; $i++) {
if (!is_int($A[$i])) {
return;
}
if ($i == 0) {
$left = $A[0];
$right = array_sum($A) - $left;
$min = abs($left - $right);
}
else {
$left += $A[$i];
$right -= $A[$i];
$min = min([$min, abs($left - $right)]);
}
}
return $min;
}
Three things to take note of.
IF $A is empty then return an integer (0) because NULL results in a
codility error
If $A only has one index then return an integer (0) because you cannot split a single index into two equal parts like they want and
NULL results in a codility error
Apparently, invalid non-integer values are not tested - you can see where I checked for !is_int($A[$i]) and then a "return;" that
should have resulted in a NULL (and an error about the NULL not
being an int), but I did not.
Assume that: N is an integer within the range [0..100,000]; each element of array A is an integer within the range [−2,147,483,648..2,147,483,647].
This means I don't see any reason to check if an element isInt() as suggested by #Floyd.
For best efficiency, always break/return as early as possible.
There is no reason to iterate on the first or last index in the array because splitting the array on the first element will result in nothing on the "left side" and splitting on the last element will result in nothing on the "right side". For this reason, start iterating from 1 and only iterate while $i is less than array count - 1.
I struggled to follow the asker's coding attempt, but it seems likely that iterating/evaluating the bookend elements was to blame.
Code -- not actually tested on Codility: (Demo)
$test = [-1, 3, -4, 5, 1, -6, 2, 1];
function solution(array $array): int {
for ($i = 1, $count = count($array) - 1; $i < $count; ++$i) {
if (array_sum(array_slice($array, 0, $i)) === array_sum(array_slice($array, $i + 1))) {
return $i;
}
}
return -1;
}
echo solution($test);
I have to fill an array with random numbers to satisfy a few conditions:
The number of elements in the result array must match the designated number.
The sum of the numbers in the result array must equal the designated number.
Random numbers must be selected between designated lower and upper bounds.
For example:
Sum of the array: 130
Total array elements: 3
Random integers' lower bound: 23
Random integers' upper bound: 70
Possible result:
array(23, 70, 37)
What to do now? How to split/divide my number?
I started with this (pseudo code):
i=0;
while(sum(number) > 0 and i < arraykeys){
x = randomize(from, to)
number = number - x
myarray[i] = x
i++
}
This should work for you:
Code explanation
Workability
The first thing we need to check is, if it is possible to build the goal out of numbers from the scope:
if(checkWorkability($result, $goal, $amountOfElementsLeft, $scope))
Means it just uses the highest values possible and looks if it is bigger than the goal.
While loop
In the while loop we need to check if we still have elements left which we can use:
while($amountOfElementsLeft > 0)
Scope adjustment
Every iteration we need to check if we need to adjust the scope, so that at the end we will be able to build the goal.
This means if the current sum of numbers + the highest possible number is bigger than the goal, we need to make the max value of the scope smaller.
Also on the opposite side we need to make the min value of the scope bigger, when we can't reach our goal anymore.
Code
<?php
$goal = 130;
$amountOfElementsLeft = 3;
$scope = [23, 70];
$result= [];
function adjustScope(array $result, $goal, $amountOfElementsLeft, $scope) {
$newScope = $scope;
if($amountOfElementsLeft == 1) {
$leftOver = $goal - array_sum($result);
return [$leftOver, $leftOver];
}
if((($goal - (array_sum($result) + $scope[1])) / ($amountOfElementsLeft - 1)) < $scope[0])
$newScope[1] = (int) ($goal - array_sum($result)) / ($scope[0] * ($amountOfElementsLeft - 1));
elseif(($adjustTop = $goal - array_sum($result)) < $scope[1])
$newScope[1] = $adjustTop;
if(($adjustBottom = $goal - (array_sum($result) + $scope[0] + (($amountOfElementsLeft - 1) * $scope[1]))) < $goal && $adjustBottom > 0)
$newScope[0] = $scope[0] + $adjustBottom;
return $newScope;
}
function checkWorkability(array $result, $goal, $amountOfElementsLeft, $scope) {
if(array_sum($result) + $amountOfElementsLeft * $scope[1] >= $goal)
return TRUE;
return FALSE;
}
if(checkWorkability($result, $goal, $amountOfElementsLeft, $scope)) {
while($amountOfElementsLeft > 0) {
$scope = adjustScope($result, $goal, $amountOfElementsLeft, $scope);
$result[] = rand($scope[0], $scope[1]);
$amountOfElementsLeft--;
}
}
print_r($result);
echo array_sum($result);
?>
possible outputs:
Array
(
[0] => 58
[1] => 30
[2] => 42
) -> 130
Array
(
[0] => 35
[1] => 54
[2] => 41
) -> 130
Array
(
[0] => 52
[1] => 51
[2] => 27
) -> 130
I've written a custom function for portability and to meaningfully implement some guard conditions which throw exceptions when incoming parameters make the desired result impossible.
Loop one less than $count times -- this is because the final element in the returned array is determined by the difference between the desired total and the sum of the randomly acquired values.
Adjust the lower and upper bounds of the $scope array (if required) to ensure a successfully populated return array.
Get a random integer, push it into the return array, then subtract it from the $total.
When the looped processes are finished, push the remaining $total value as the final element in the return array.
Code: (Demo)
function getRandWithStipulations(int $total, int $count, array $scope): array
{
if ($scope[0] > $scope[1]) {
throw new Exception('Argument 3 (\$scope) is expected to contain a minimum integer then a maximum integer.');
}
if ($scope[0] * $count > $total) {
throw new Exception('Arguments 2 (\$count) and 3 (\$scope) can only exceed argument 1 (\$total).');
}
if ($scope[1] * $count < $total) {
throw new Exception('Arguments 2 (\$count) and 3 (\$scope) cannot reach argument 1 (\$total).');
}
$result = [];
for ($x = 1; $x < $count; ++$x) { // count - 1 iterations
$scope[0] = max($scope[0], $total - ($scope[1] * ($count - $x)));
$scope[1] = min($scope[1], $total - ($scope[0] * ($count - $x)));
$rand = rand(...$scope);
$result[] = $rand;
$total -= $rand;
}
$result[] = $total;
return $result;
}
try {
var_export(
getRandWithStipulations(
130,
3,
[23, 70]
)
);
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage();
}
A few random results:
[60, 34, 36]
[23, 59, 48]
[67, 36, 27]
[47, 23, 60]
I want to generate in PHP an array of random numbers, but each number should not be the same as any of the X (for example 2 ) numbers bofore it and not even close to any of them by a define range (for example 5).
So for example:
I need numbers between 1 and 100
i've set my "range" to 5
the first two generated number are 20 and 50.
the third number will be a random number between 1 and 100, excluding all the numbers between 15 and 25, and between 45 and 55.
I can't figure out a function to achieve it. Ideally I want to call something like this:
getRandomNumbers( $min, $max, $previous, $range);
where $previous is the number of previous elements to take in consideration when generating the next one and $range is the "proximity" to those number where I don't want the next number to be.
I hope I explained in a decent way my request. :) Please, add a comment if you have any question about it.
I just came up with this:
function getRandomNumbers($min, $max, $previous, $range) {
static $generated = array();
$chunk = array_slice($generated, -$previous);
// Added this infinite loop check to save you some headache.
if (((($max - $min + 1) / (($range * 2) + 1)) + 1) <= $previous) {
die("Values set have the potential of running into an infinite loop. Min: $min, Max: $max, Previous: $previous, Range: $range");
}
while(true) {
$number = rand($min, $max);
$found = true;
foreach ($chunk as $value) {
if (in_array($number, range($value-$range, $value+$range))) {
$found = false;
}
}
if ($found) {
$generated[] = $number;
return $number;
}
}
}
Test it using this:
for ($i = 1; $i < 25; $i++) {
echo getRandomNumbers(1, 100, 5, 5) . "<br />";
}
PHPFiddle Link: http://phpfiddle.org/main/code/51ke-4qzs
Edit: Added a check to prevent a possible infinite loop. For example: if you set the following values:
$min = 1;
$max = 100;
$previous = 5;
$range = 12;
echo getRandomNumbers($min, $max, $previous, $range);
Then let's say, in a really unfortunate situation it would generate 13, 38, 63 and 88. So the 5th number cannot be anything between 1 and 25, 26 and 50, 51 and 75, 76 and 100. So it would result in an infinite loop. I've updated the PHPFiddle link as well.
getRandomNumbers( $previous, $range ) {
//I'm assuming that previous will be an array of your previous X that you don't want to be close to
$num = getRandomNumber() //However you are doing this now
foreach( $previous as $key => $value ) {
if ( ( $value - $range ) > $num && ( $value + $range ) < $num ) {
return getRandomNumbers($previous, $range);
}
}
//You need to also replace a value in previous
return num;
}
I have this:
<?php $n = rand(1,1600); echo $n ?>
I want to exclude from random numbers let's say 234, 1578 ,763 , 1274 and other numbers. How would I do that?
<?php
while( in_array( ($n = mt_rand(1,1600)), array(234, 1578 ,763 , 1274) ) );
Try like this
do {
$n = rand(1,1600);
} while(in_array($n, array(234, 1578 ,763 , 1274 ));
echo $n;
Check if the number is one that you don't want, if it is get a new random number.
function getRandomNumber() {
do {
$n = mt_rand(1,1600);
} while(in_array($n, array(234,1578, 763, 1274)));
return $n;
}
Always use cryptographically strong algorithms for generating random numbers:
/**
* #param int $from From number
* #param int $to To number
* #param array $excluded Additionally exclude numbers
* #return int
*/
function randomNumber($from, $to, array $excluded = [])
{
$func = function_exists('random_int') ? 'random_int' : 'mt_rand';
do {
$number = $func($from, $to);
} while (in_array($number, $excluded, true));
return $number;
}
var_dump(randomNumber(1, 100));
var_dump(randomNumber(1, 10, [5, 6, 7, 8]));
var_dump(randomNumber(1, 100, range(10, 90)));
I'd also recommend using the paragonie/random_compat library for compatibility in case of using multiple PHP versions.
Or avoid making loops with random (possibly infinite) running time:
/**
* Returns a random integer between $min and $max (inclusive) and
* excludes integers in $exarr, returns false if no such number
* exists.
*
* $exarr is assumed to be sorted in increasing order and each
* element should be unique.
*/
function random_exclude($min, $max, $exarr = array()) {
if ($max - count($exarr) < $min) {
return false;
}
// $pos is the position that the random number will take
// of all allowed positions
$pos = rand(0, $max - $min - count($exarr));
// $num being the random number
$num = $min;
// while $pos > 0, step to the next position
// and decrease if the next position is available
for ($i = 0; $i < count($exarr); $i += 1) {
// if $num is on an excluded position, skip it
if ($num == $exarr[$i]) {
$num += 1;
continue;
}
$dif = $exarr[$i] - $num;
// if the position is after the next excluded number,
// go to the next excluded number
if ($pos >= $dif) {
$num += $dif;
// -1 because we're now at an excluded position
$pos -= $dif - 1;
} else {
// otherwise, return the free position
return $num + $pos;
}
}
// return the number plus the open positions we still had to go
return $num + $pos;
}
This function chooses a random position and walks the exclusion array to find the free position. It's running time depends on the amount of numbers to exclude. If you want to exclude certain ranges, you may want to adapt the algorithm to take this into account.
As the volume of "blacklisted" integers approaches the volume of the full range of integers, it becomes increasingly compelling to take the advice of #regenschein.
A non-iterative approach might look like this:
$range = range(1, 1600);
$blacklist = [234, 1578, 763, 1274]; // 4 blacklisted versus 1600 full range is NOT compelling
$valids = array_diff($range, $blacklist);
echo array_values($valids)[rand(0, count($valids) - 1)];
// or
echo $valids[array_rand($valids)];
// the two approaches use different randomizers
Or if you'd be just as happy shuffling, you could do:
$blacklist = [234, 1578, 763, 1274];
$range = range(1, 1600);
$valids = array_diff($range, $blacklist);
shuffle($valids);
echo $valids[0];
*Note array_diff() is particularly great if you want to pass multiple blacklist arrays -- just comma-separate them.
For example:
var_export($valids = array_diff(range(1, 100), range(5, 50), range(61, 99), [55]));
Output:
array (
0 => 1,
1 => 2,
2 => 3,
3 => 4,
50 => 51,
51 => 52,
52 => 53,
53 => 54,
55 => 56,
56 => 57,
57 => 58,
58 => 59,
59 => 60,
99 => 100,
)
Another solution for this could be as follows:
function random_number($min, $max, $exclude)
{
$number = rand($min, $max);
if(in_array($number, $exclude))
{
random_number($min, $max, $exclude);
} else {
return $number;
}
}
$number = random_number(1,10, [2,5,6]);
I know this is a bit old,but I think what the op tries to do is shuffle the integers. If so the following method is better
$array = array(1,2,3,4,5,6,7);
shuffle($array);
This code will randomize the order of the array's exact elements without repetition and return the result inside the array itself.
You could create an array with valid numbers.
Then, your random number generation should return the index into that array.
If you don't have too many numbers to exclude, it is easier and faster to just retry if you find an unwanted number:
$n = 0;
while (in_array($n, array(0, 234, 1578 ,763 , 1274))) {
$n = rand(1,1600);
}
echo $n;