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.
Related
i have this function:
function pairwiseDifference($arry)
{
$n = count($arry) - 1; // you can add the -1 here
$diff = 0;
$result = array();
for ($i = 0; $i < $n; $i++)
{
// absolute difference between
// consecutive numbers
$diff = abs($arry[$i] - $arry[$i + 1]);
echo $diff." ";
array_push($result, $diff); // use array_push() to add new items to an array
}
return $result; // return the result array
}
$arry = array(20,10,10,50);
echo "<br> Percent of commisions are: ";
// this echos 10,0,40 and assigns whatever your function returns
// to the variable $diffArray
$diffArray = pairwiseDifference($arry);
The problem are that im not expecting this output
becouse first number of array (20) is my commission
and the other numbers are my parents commission (10,10,50).
So basically i need to output like this: (0,0,30)
becouse i take 20 of commission,
first parent not take nothing becouse are less of my commission (10)
second parent not take nothing becouse are less of my commission (10)
and only last parent take 30 becouse are greater than my commission (50 - 20 my commission).
Thanks in advance
Since your first element of the array is your commission and the others are the the commissions of parents, and since it seems that you don't want to include your commission in the result array, you can do something like this:
function pairwiseDifference($arry)
{
$n = count($arry);
$diff = 0;
$result = array();
$myComm = $arry[0]; // your commision
for ($i = 1; $i < $n; $i++)
{
$diff = 0; // set $diff to 0 as default
if($myComm < $arry[$i]) // if your commision < parent commision
$diff = $arry[$i] - $myComm;
echo $diff." ";
array_push($result, $diff);
}
return $result;
}
$arry = array(20,10,10,50);
echo "<br> Percent of commisions are: ";
$diffArray = pairwiseDifference($arry);
echo $diffArray[0]; // prints 0
echo $diffArray[1]; // prints 0
echo $diffArray[2]; // prinst 30
To tweak the logic according to your code, there would be only 3 modifications.
Create a $max variable and assign it the value of $arry[0].
Make difference as 0 if current max is greater than current one, else take the difference.
Calculate the new max again using max() function.
Snippet:
<?php
function pairwiseDifference($arry)
{
$n = count($arry) - 1; // you can add the -1 here
$diff = 0;
$result = array();
$max = $arry[0]; // new addition
for ($i = 1; $i <= $n; $i++) // new modification <= n instead of < n
{
// absolute difference between
// consecutive numbers
$diff = $max < $arry[$i] ? $arry[$i] - $max : 0; // new modification
$max = max($max, $arry[$i]); // new modification
echo $diff." ";
array_push($result, $diff); // use array_push() to add new items to an array
}
return $result; // return the result array
}
$arry = array(20,10,10,50);
echo "<br> Percent of commisions are: ";
// this echos 10,0,40 and assigns whatever your function returns
// to the variable $diffArray
$diffArray = pairwiseDifference($arry);
in an N numbers of array a number is a X-number if it is divisible by atleast one other number in the array. Program to find how many such numbers exists in an given array
example 1 : in array [1,2,3] , number of x-numbers is 2 ( 2 and 3 as they are divisible by number 1 )
example 2 : in array [2,3,5,8] , number of x-numbers is 1 ( 8 is divisible by 2 )
example 3 : in array [2,3,6,12] , number of x-numbers is 2 ( 6 is divisible by 2 and 3 , 12 is divisible by 2 and 3 and 6 )
I am using the below code, but i want to optimize it in a way if the array size increase the performance should not hamper :
$arr = array(2,3,6,12);
$count = 0;
function check_special_num($tnum, $tarr){
sort($tarr);
for($i=0;$i<sizeof($tarr);$i++){
if( $tnum % $tarr[$i] == 0 ){
return true;
}
}
return false;
}
for($i=0;$i<sizeof($arr);$i++){
$temp = $arr;
$num = $arr[$i];
array_splice($temp, $i, 1);
if( check_special_num( $num, $temp ) ){
$count += 1;
}
}
echo $count;
Coding language is PHP
It might be faster this way:
$arr = array(2,3,6,12);
function x_numbers_count($arr) {
$count = 0;
sort($arr);
foreach ($arr as $i => $number) {
for ($j = 0; $j < $i; $j++) {
if ($number % $arr[$j] == 0) {
$count++;
break;
}
}
}
return $count;
}
echo x_numbers_count($arr);
The main differences, which can be expected to improve performance:
the array is sorted only once, before beginning to work (this could also improve your own method, without changing anything else)
the search loop stops as soon as it is to reach the current investigated number
less variables are used
I've searched through a number of similar questions, but unfortunately I haven't been able to find an answer to this problem. I hope someone can point me in the right direction.
I need to come up with a PHP function which will produce a random number within a set range and mean. The range, in my case, will always be 1 to 100. The mean could be anything within the range.
For example...
r = f(x)
where...
r = the resulting random number
x = the mean
...running this function in a loop should produce random values where the average of the resulting values should be very close to x. (The more times we loop the closer we get to x)
Running the function in a loop, assuming x = 10, should produce a curve similar to this:
+
+ +
+ +
+ +
+ +
Where the curve starts at 1, peeks at 10, and ends at 100.
Unfortunately, I'm not well versed in statistics. Perhaps someone can help me word this problem correctly to find a solution?
interesting question. I'll sum it up:
We need a funcion f(x)
f returns an integer
if we run f a million times the average of the integer is x(or very close at least)
I am sure there are several approaches, but this uses the binomial distribution: http://en.wikipedia.org/wiki/Binomial_distribution
Here is the code:
function f($x){
$min = 0;
$max = 100;
$curve = 1.1;
$mean = $x;
$precision = 5; //higher is more precise but slower
$dist = array();
$lastval = $precision;
$belowsize = $mean-$min;
$abovesize = $max-$mean;
$belowfactor = pow(pow($curve,50),1/$belowsize);
$left = 0;
for($i = $min; $i< $mean; $i++){
$dist[$i] = round($lastval*$belowfactor);
$lastval = $lastval*$belowfactor;
$left += $dist[$i];
}
$dist[$mean] = round($lastval*$belowfactor);
$abovefactor = pow($left,1/$abovesize);
for($i = $mean+1; $i <= $max; $i++){
$dist[$i] = round($left-$left/$abovefactor);
$left = $left/$abovefactor;
}
$map = array();
foreach ($dist as $int => $quantity) {
for ($x = 0; $x < $quantity; $x++) {
$map[] = $int;
}
}
shuffle($map);
return current($map);
}
You can test it out like this(worked for me):
$results = array();
for($i = 0;$i<100;$i++){
$results[] = f(20);
}
$average = array_sum($results) / count($results);
echo $average;
It gives a distribution curve that looks like this:
I'm not sure if I got what you mean, even if I didn't this is still a pretty neat snippet:
<?php
function array_avg($array) { // Returns the average (mean) of the numbers in an array
return array_sum($array)/count($array);
}
function randomFromMean($x, $min = 1, $max = 100, $leniency = 3) {
/*
$x The number that you want to get close to
$min The minimum number in the range
$max Self-explanatory
$leniency How far off of $x can the result be
*/
$res = [mt_rand($min,$max)];
while (true) {
$res_avg = array_avg($res);
if ($res_avg >= ($x - $leniency) && $res_avg <= ($x + $leniency)) {
return $res;
break;
}
else if ($res_avg > $x && $res_avg < $max) {
array_push($res,mt_rand($min, $x));
}
else if ($res_avg > $min && $res_avg < $x) {
array_push($res, mt_rand($x,$max));
}
}
}
$res = randomFromMean(22); // This function returns an array of random numbers that have a mean close to the first param.
?>
If you then var_dump($res), You get something like this:
array (size=4)
0 => int 18
1 => int 54
2 => int 22
3 => int 4
EDIT: Using a low value for $leniency (like 1 or 2) will result in huge arrays, since testing, I recommend a leniency of around 3.
Lets say, I have an array with 1000 values (integers). And I need from this array to have an array with f.e. 400 values (the number can be changed, f.e. 150, etc.).
So I need to return each 2.5th array value, i.e. 1st, 3rd, 6th, 8th, 11th, etc.
Is this somehow possible?
I dont need a code, I just need some way, how to do it.
EDITED:
My array is array of elevations (from another array with GPS coordinates). And I want to draw a 2D model of elevation. Lets say, my map will always have 400px width. 1px = 1 point of elevation. Thats why I need each 2.5th elevation value...
Like this:
You don't want the 2.5th one. You effectively want to divide the set into blocks of five and get the first and third from each set.
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
You want the first and third columns.
This is PHP, so we've got 0-based arrays.
We can divide it into groups of 5 using the modulo operator %. We can then see if the return value is 0 (i.e. it's in the first column) or 2 (i.e. it's in the third column).
I'm going to presume your array has numeric keys starting from 0.
// PHP 5.6
$filtered = array_filter($array, function($value, $key) {
$mod = $key % 5;
return ($mod === 0) || ($mod === 2);
}, ARRAY_FILTER_USE_BOTH);
// pre-PHP 5.6
$filtered = array();
foreach ($array as $key => $value) {
$mod = $key % 5;
if (($mod === 0) || ($mod === 2)) {
$filtered[$key] = $value;
}
}
var_dump($filtered);
How about using the modulo % operator?
Say you want to make 1000 into 200 values, loop through all the items in the array and keep a counter, if the counter % 5 == 0 then put that value into a new array, or if != 0 then remove from array. We use modulo 5 because 1000 / 200 = 5.
Below is the way to start with. It does not ensure that first and last elements are included in the output and, probably, has some other glitches. But since you requested the idea, here you go—array_reduce:
$a=[1,2,3,4,5,6,7,8,9,10,11,12]
$step = 2.5;
$i = 0;
$r = array_reduce($a, function($memo, $curr) use(&$i, $step) {
if($i === round($step * count($memo))) {
$memo[] = $curr;
}
$i++;
return $memo;
}, []);
print_r($r);
/*
Array
(
[0] => 1
[1] => 4
[2] => 6
[3] => 9
[4] => 11
)
*/
Hope it helps.
This should be pretty close to what you seem to be looking for. It will collect whichever values are closest (rounding down) to the float values.
$list = range(1,1000);
$targetSize = 300;
$new = array();
$step = count($list) / $targetSize;
$curStep = 0;
for( $i = 0; $i < count($list); $i++ ) {
$curStep++;
if( $curStep > $step ) {
$new[] = $list[ floor($i) ];
$curStep -= $step;
}
}
So this is it:
$arr = range(1, 1387); // f.e.
$cnt = 296; // f.e.
$new = array();
$max = count($arr);
$step = $max / $cnt;
for ($i = 0; $i < $max; $i += $step) {
$new[] = round($arr[(int)$i]);
}
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;
}