Displaying min() value in array - php

I want to display 10 highest figure and 10 lowest figure in a multi-dimensional array.
I have figured out way to display the maximum figure using max(), but when I use min(), the least less value get looped again and again for 10 times e.g 2.
How can I reuse my code to display minimum value in my array?
$totalCustomer = count($customerArray);
$postion = 0;
foreach ($customerArray as $custom) {
$postion = $postion + 1;
if($totalCustomer - $postion < 9){
$top[] = $custom['spend'];
$maxprice = max($top);
echo "Max spend price is ". $maxprice. "<br>";
}
}

#hek2mgl answer is a good one. But you can take advantage of PHP array's indexes to avoid sorting and gaining performance.
$prices = [];
foreach ( $customerArray as $custom )
{
// This approach uses your price as an ordering index, and supposes two decimal points
$index = intval( $custom['spend'] * 100 );
$prices[$index] = $custom['spend'];
}
// Showing lowest 10 prices
$top = array_slice( $prices, 0, 10 );
// Showing top 10 prices
$low = array_slice( array_reverse( $prices ), 0, 10 );

I would use usort:
/* Sort the array the value of 'spend' */
usort($customerArray, function($a, $b) {
if($a['spend'] == $b['spend']) {
return 0;
} else if ($a['spend'] > $b['spend']) {
return -1;
} else {
return 1;
}
});
/* The top 10 elements are now on top of the array */
$top = array_slice($customerArray, 0, 10);
/* If we reverse the array using array_reverse() the
smallest items are on top */
$low = array_slice(array_reverse($customerArray), 0, 10);

Related

Optimizing Find nearest sum of numbers in array to a given number

Say I have an array [10000,5000,1000,1000] and I would like to find the closest sum of numbers to a given number. Sorry for the bad explanation but here's an example:
Say I have an array [10000,5000,1000,1000] I want to find the closest numbers to, say 6000.
Then the method should return 5000 and 1000
another example : we want the closest to 14000 , so then he should return 10000 and 5000
I've tried with code below, here is working one but if the $desiredSum and $numbers array is big. it's running so slow until php execution timeout
$numbers = array(
10000,5000,1000,1000
);
$desiredSum = 6000;
$minDist = null;
$minDist_I = null;
// Iterate on every possible combination
$maxI = pow(2,sizeof($numbers));
for($i=0;$i<$maxI;$i++) {
if(!(($i+1) % 1000)) echo ".";
// Figure out which numbers to select in this
$sum = 0;
for($j=0;$j<sizeof($numbers);$j++) {
if($i & (1 << $j)) {
$sum += $numbers[$j];
}
}
$diff = abs($sum - $desiredSum);
if($minDist_I === null || $diff < $minDist) {
$minDist_I = $i;
$minDist = $diff;
}
if($diff == 0) break;
}
$chosen = array();
for($j=0;$j<sizeof($numbers);$j++) {
if($minDist_I & (1 << $j)) $chosen[] = $numbers[$j];
}
echo "\nThese numbers sum to " . array_sum($chosen) . " (closest to $desiredSum): ";
echo implode(", ", $chosen);
echo "\n";
Anyone can help me out ?
<?php
function coinChange($numbers,$desiredSum){
sort($numbers);
$set = [];
$set[0] = [];
for($i = $numbers[0];$i <= $desiredSum;++$i){
foreach($numbers as $index => $current_number){
if($i >= $current_number && isset($set[$i - $current_number])){
if(isset($set[$i - $current_number][$index])) continue;
$set[$i] = $set[$i - $current_number];
$set[$i][$index] = true;
break;
}
}
}
if(count($set) === 0){
return [0,[]];
}
if(isset($set[$desiredSum])){
return [
$desiredSum,
formatResult($numbers,array_keys($set[$desiredSum]))
];
}else{
$keys = array_keys($set);
$nearestSum = end($keys);
$sum = 0;
$rev_numbers = array_reverse($numbers);
$result = [];
foreach($rev_numbers as $number){
$sum += $number;
$result[] = $number;
if($sum > $nearestSum && abs($nearestSum - $desiredSum) > abs($sum - $desiredSum)){
$nearestSum = $sum;
break;
}else if($sum > $nearestSum && abs($nearestSum - $desiredSum) < abs($sum - $desiredSum)){
$result = formatResult($numbers,array_keys($set[$nearestSum]));
break;
}
}
return [
$nearestSum,
$result
];
}
}
function formatResult($numbers,$keys){
$result = [];
foreach($keys as $key) $result[] = $numbers[$key];
return $result;
}
print_r(coinChange([10000,5000,1000,1000],14000));
print_r(coinChange([10000,5000,1000,1000],13000));
print_r(coinChange([100000,100000,100000,100000,100000,100000,50000,50000,50000,50000,10000,10000,500,500,500,1000,1000],250000));
print_r(coinChange([100000,100000,100000,100000,100000,100000,50000,50000,50000,50000,10000,10000,500,500,500,1000,1000],179999));
Demo: https://3v4l.org/hBGeW
Algorithm:
This is similar to coin change problem.
We first sort the numbers.
Now, we iterate from minimum number in the array to the desired sum.
Inside it, we iterate through all elements in the array.
Now, we can make $i(which is a sum) only if we have made sum $i - $current_number. If we have the previous one, then we add $current_number to our collection for sum $i.
Two Scenarios:
If we can make the exact sum, then we return the result as is.
If we can't, then are 2 possibilities:
We would already have nearest sum possible in our $set which would be the last entry. We keep them in a variable.
Now, the nearest sum could also be higher than the desired sum. So, we get the larger sum and check if it's nearer than nearest smallest sum and then compare both and return the result.
Result format:
Let's take the below sample output:
Array
(
[0] => 15000
[1] => Array
(
[0] => 10000
[1] => 5000
)
)
It simply means that the first index is the nearest sum possible and array at 2nd index is all elements it took from $numbers to make that sum.

Randomize a Number over multiple points in descending order

I need to distribute a number for multiple points in degraded order.
For example,
N = 100 // this 100 is fixed
no_of_points = 10 // no of points will vary
Need to break 100 into 10 points in descending order RANDOMLY.
e.g:
80, 55, 32, 18, 10, 10, 10, 8, 2, 0
Here,
degradation will be faster towards end
Must contain a zero to end
I'm trying to do something like:
private static function generateRandomPercentage($no_of_points)
{
$distributions = [];
// need to start from 80
// but it may vary also
$max = mt_rand(80, 81);
$ratio = $max / $no_of_points;
for( $i = 1; $i <= $no_of_points; $i++ ) {
$delta = ($ratio * $i);
$distributions[] = round(($max - $delta) / 100, 2);
}
print_r($distributions);
}
But nothing working.
Please help me.
Can be done with php standard functions:
$N = 100 // this 100 is fixed
$no_of_points = 10 // no of points will vary
$distributions= rsort(array_slice(shuffle(range(1,$N)), 0, $no_of_points);
print_r($distributions));
range = create array with values 1...n
shuffle = shuffle array
array_slice = return part of an array
rsort = sort array values hight to low values

Generate an array in PHP of random number not close to the X previous element

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

PHP rand() exclude certain numbers

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;

How to do a special shuffle function in php

I need a function that randomizes an array similar to what shuffle does, with the difference that each element has different chances.
For example, consider the following array:
$animals = array('elephant', 'dog', 'cat', 'mouse');
elephant has an higher chance of getting on the first index than dog. Dog has an higher chance than cat and so on. For example, in this particular example elephant could have a chance of 40% in getting in the 1st position, 30% of getting on the 2nd position, 20% on getting on 3rd and 10% getting on last.
So, after the shuffling, the first elements in the original array will be more likely (but not for sure) to be in the first positions and the last ones in the last positions.
Normal shuffle may be implemented just as
dropping items randomly at some range
picking them up from left to right
We can adjust dropping step, drop every element not into whole range, but at some sliding window. Let N would be amount of elements in array, window width would be w and we'll move it at each step by off. Then off*(N-1) + w would be total width of the range.
Here's a function, which distorts elements' positions, but not completely at random.
function weak_shuffle($a, $strength) {
$len = count($a);
if ($len <= 1) return $a;
$out = array();
$M = mt_getrandmax();
$w = round($M / ($strength + 1)); // width of the sliding window
$off = ($M - $w) / ($len - 1); // offset of that window for each step.
for ($i = 0; $i < $len; $i++) {
do {
$idx = intval($off * $i + mt_rand(0, $w));
} while(array_key_exists($idx, $out));
$out[$idx] = $a[$i];
}
ksort($out);
return array_values($out);
}
$strength = 0 ~normal shuffle.
$strength = 0.25 ~your desired result (40.5%, 25.5%, 22%, 12% for elephant)
$strength = 1 first item will never be after last one.
$strength >= 3 array is actually never shuffled
Playground for testing:
$animals = array( 'elephant', 'dog', 'cat', 'mouse' );
$pos = array(0,0,0,0);
for ($iter = 0; $iter < 100000; $iter++) {
$shuffled = weak_shuffle($animals, 0.25);
$idx = array_search('elephant', $shuffled);
$pos[$idx]++;
}
print_r($pos);
Try to use this algorithm:
$animals = [ 'elephant', 'dog', 'cat', 'mouse' ]; // you can add more animals here
$shuffled = [];
$count = count($animals);
foreach($animals as $chance => $animal) {
$priority = ceil(($count - $chance) * 100 / $count);
$shuffled = array_merge($shuffled, array_fill(0, $priority, $animal));
}
shuffle($shuffled);
$animals = array_unique($shuffled);
You have an array, let's say of n elements. The probability that the i'th element will go to the j'th position is P(i, j). If I understood well, the following formula holds:
(P(i1, j1) >= P(i2, j2)) <=> (|i1 - j1| <= |j1 - i1|)
Thus, you have a Galois connection between the distance in your array and the shuffle probability. You can use this Galois connection to implement your exact formula if you have one. If you don't have a formula, you can invent one, which will meet the criteria specified above. Good luck.

Categories