I have a array with the lvl=>xp correspondance and I would make a function that return the lvl for a specific xp. like $lvl = getLvlOf(15084); //return 5
$lvl_correspondance = array(
1=>100,
2=>520,
3=>2650,
4=>6588,
5=>12061,
6=>23542,
...
n=>xxxxxx
);
I search the easyest and ressourceless way to do it.
Sorry for my poor english :(
Assuming the level values in the array are kept sorted, e.g. (it's 100,200,300, 400, etc... and not 200,500,100,300,400), then a simple scan will do the trick:
$xp = 15084;
$last_key = null;
foreach($lvl_correspondenance as $key => $val) {
if ($val < $xp) {
$last_key = $key;
} else {
break;
}
}
That'll iterate through the array, and jump out as soon as the XP level in the array is larger than the XP level you're looking for, leaving the key of that "last" level in $last_key
function getLvlOf($lvl, $int){
foreach($lvl as $level=>$exp){
if($exp > $int){
return $level-1;
}
}
}
it's O(n) so no magic there...
It looks like your array can be computed live -
XP = exp( 3 * ln(LVL) + 4 ) * 2
You can do the same in reverse, in O(1):
LVL = exp(ln(XP/2) - 4 / 3)
I rounded the equation, so there may be a +/- 1 issue
Good Luck!
Not good if you have very high level values, but:
$lvl = $lvl_correspondance[array_search(
max(array_intersect(
array_values($lvl_correspondance),
range(0,$points)
)
),
$lvl_correspondance
)];
You can use array_flip if the xp levels are distinct. Then you can simply access the level number using the xp as index:
$levels = array_flip($lvl_correspondance);
$lvl = $levels[15084];
EDIT: But maybe a function would be better to fetch even the xp levels in between:
function getLvlOf($xp) {
// get the levels in descending order
$levels = array_reverse($GLOBALS['lvl_correspondance'], true);
foreach ($levels as $key => $value) {
if ($xp >= $value)
return $key;
}
// no level reached yet
return 0;
}
Related
I've been banging my head against this problem for a while. I feel like it should be simple, but I'm having a hard time coming up with a solution.
I'm looking to pre-populate a database, and I need to create SQL statements with some foreign key values. It would be tedious to hand-code them, so naturally I decided to do it in code.
What I want are series of arrays that have values as such:
[1]
[2]
[3]
[1,1]
[1,2]
[1,3]
[2,1]
[2,2]
...
[1,1,1]
[1,1,2]
[1,1,3]
...
[3,1,1]
...
[3,3,3]
I want to specify the number of values in the array, and the numerical value at which it causes the preceeding value to roll over.
In the example I gave above, it would be like generate(3,3), since the maximum number of elements is 3, and the highest value is 3.
How could I write some code that would give me this series of arrays?
This is a recursive function that will generate each of the combinations of the ranges up to the maximum value, with elements in each array from 1 to the number specified:
function generate($elements, $maxvalue) {
if ($elements == 0) return array();
$result = array();
foreach (range(1, $maxvalue) as $el) {
$result[] = array($el);
}
foreach (range(1, $maxvalue) as $el) {
foreach (generate($elements - 1, $maxvalue) as $arr) {
$result[] = array($el, ...$arr);
}
}
return $result;
}
$combs = generate(3, 3);
Output is too long to show here but can be seen in this demo on 3v4l.org
Note for PHP < 7.4, replace
$result[] = array($el, ...$arr);
with
$result[] = array_merge(array($el), $arr);
Here's a version using generators (which may be slightly easier on memory than pure arrays):
function generate(int $elementsCount, int $maxValue, array $current = []): \Generator
{
for ($value = 1; $value <= $maxValue; $value++) {
yield [...$current, $value];
}
if ($elementsCount > 1) {
for ($value = 1; $value <= $maxValue; $value++) {
yield from generate($elementsCount - 1, $maxValue, [...$current, $value]);
}
}
}
Exemple usage + debug/print:
$combinations = generate(3, 3);
print_r(iterator_to_array($combinations, false));
Demo
I'm trying to combine numbers in an array by adding them so that the max value can only by 30.
For example, this is my array:
array(10,30,10,10,15);
After combining the numbers in the array to items with a max value 30, the result should be:
array(30,30,15);
How to achieve this?
I'm trying to combine numbers in an array by adding them so that the
max value can only by 30
So, when you combine numbers, you can achieve the lowest possible set of values in your array and also make sure that max value remains 30 by:
First, sort them.
Second, keeping adding elements to sum till you are about to get a sum > 30.
Third, once an element can no longer be added to a sum, add the current sum in your array and make the current element as the new sum.
Code:
<?php
$arr = array(10,30,10,10,15);
sort($arr);
$res = [];
$curr_sum = 0;
foreach($arr as $each_value){
if($curr_sum + $each_value <= 30) $curr_sum += $each_value;
else{
$res[] = $curr_sum;
$curr_sum = $each_value;
}
}
$res[] = $curr_sum;
print_r($res);
Demo: https://3v4l.org/BYhuE
Update: If order of the numbers matters, seeing your current output, you could just use rsort() to show them in descending order.
rsort($res);
$total = array_sum(array(10,30,10,10,15)); //assign sum totals from orignal array
$maxValue = 30; //assign max value allowed in array
$numberOfWholeOccurancesOfMaxValue = floor($total/$maxValue);
$remainder = $total%$maxValue;
//build array
$i=0;
while ( $i < $numberOfWholeOccurancesOfMaxValue ){
$array[] = $maxValue;
$i++;
}
$array[] = $remainder;
print_r($array);
You can loop only once to get this,
$temp = array(10,30,10,10,15);
natsort($temp); // sorting to reduce hustle and complication
$result = [];
$i = 0;
$maxValue = 30;
foreach($temp as $v){
// checking sum is greater or value is greater or $v is greater than equal to
if(!empty($result[$i]) && (($result[$i]+$v) > $maxValue)){
$i++;
}
$result[$i] = (!empty($result[$i]) ? ($result[$i]+$v) : $v);
}
print_r($result);
Working demo.
I believe finding most space-optimized/compact result requires a nested loop. My advice resembles the firstFitDecreasing() function in this answer of mine except in this case the nested loops are accessing the same array. I've added a couple of simple conditions to prevent needless iterations.
rsort($array);
foreach ($array as $k1 => &$v1) {
if ($v1 >= $limit) {
continue;
}
foreach ($array as $k2 => $v2) {
if ($k1 !== $k2 && $v1 + $v2 <= $limit) {
$v1 += $v2;
unset($array[$k2]);
if ($v1 === $limit) {
continue 2;
}
}
}
}
rsort($array);
var_export($array);
By putting larger numbers before smaller numbers before processing AND by attempting to add multiple subsequent values to earlier values, having fewer total elements in the result is possible.
See my comparative demonstration.
I believe #Clint's answer is misinterpreting the task and is damaging the data by summing all values then distributing the max amounts in the result array.
With more challenging input data like $array = [10,30,5,10,5,13,14,15,10,5]; and $limit = 30;, my solution provides a more dense result versus #nice_dev's and #rahul's answers.
Is there a way to find out how many value-elements are in one array in all nesting levels?
I want to find out how many are in $_REQUEST to give out a warning if the limit dictated by the max_input_vars directive is nearly reached?
I want to count only the real values, so array-elements should not be counted if they are another array (see
http://pastebin.com/QAKxxqJf)
use: count($array_name, COUNT_RECURSIVE);
as count method takes a second argument which is the mode. int COUNT_NORMAL or COUNT_RECURSIVE
Use array_walk_recursive to count all elements that are not of type array or object:
$count = 0;
array_walk_recursive($a, function($v) use(&$count) {
if(!is_object($v)) ++$count; //or if(is_string($v))
});
This is my solution for PHP <= 5.2
/**
* Counts only the real values, so array-elements
* are not counted if they are another array.
*
* #param array $array
* #return int
* #author Muslim Idris
*/
function count_recursive($array) {
$count = 0;
foreach ($array as $v) {
if (is_array($v)) $count += count_recursive($v);
else $count++;
}
return $count;
}
I cannot use count($_REQUEST, COUNT_RECURSIVE), so I used:
function count_recursive($array) {
$count = 0;
foreach ($array as $id => $_array) {
if (is_array ($_array)) $count += count_recursive ($_array);
else $count += 1;
}
return $count;
}
$num=count_recursive($_REQUEST);
$max=ini_get('max_input_vars');
$debug=true;
if($debug and $max - $num < 50) {
echo "Warning:Number of requests ($num) is near the value of max_input_vars:$max";
}
(although there is still a strange thing is on my testing server, but thats another question: why is the number of elements in $_REQUEST smaller than the limit i set by max_input_vars? and maybe a bug in PHP)
check following link for all option of using count function
http://php.net/manual/en/function.count.php
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.
If I have a PHP array:
$array
With values:
45,41,40,39,37,31
And I have a variable:
$number = 38;
How can I return the value?:
39
Because that is the closest value to 38 (counting up) in the array?
Regards,
taylor
<?php
function closest($array, $number) {
sort($array);
foreach ($array as $a) {
if ($a >= $number) return $a;
}
return end($array); // or return NULL;
}
?>
Here is a high-level process to get the desired results and work for any array data:
Filter the array keeping on values greater than or equal to the target and then select the lowest remaining value. This is the "best" value (which may be "nothing" if all the values were less) -- this is O(n)
Alternatively, sort the data first and see below -- this is O(n lg n) (hopefully)
Now, assuming that the array is sorted ASCENDING, this approach would work:
Loop through the array and find the first element which is larger than or equal to the target -- this is O(n)
And if the array is DESCENDING (as in the post), do as above, but either:
Iterate backwards -- this is O(n)
Sort it ASCENDING first (see fardjad's answer) -- this is O(n lg n) (hopefully)
Iterate forwards but keep a look-behind value (to remember "next highest" if the exact was skipped) -- this is O(n)
Happy coding.
EDIT typo on array_search
Yo... Seems easy enough. Here's a function
<?php
$array = array(45,41,40,39,37,31);
function closest($array, $number){
#does the array already contain the number?
if($i = array_search( $number, $array)) return $i;
#add the number to the array
$array[] = $number;
#sort and refind the number
sort($array);
$i = array_search($number, $array);
#check if there is a number above it
if($i && isset($array[$i+1])) return $array[$i+1];
//alternatively you could return the number itself here, or below it depending on your requirements
return null;
}
to Run echo closest($array, 38);
Here's a smaller function that will also return the closest value. Helpful if you don't want to sort the array (to preserve keys).
function closest($array, $number) {
//does an exact match exist?
if ($i=array_search($number, $array)) return $i;
//find closest
foreach ($array as $match) {
$diff = abs($number-$match); //get absolute value of difference
if (!isset($closeness) || (isset($closeness) && $closeness>$diff)) {
$closeness = $diff;
$closest = $match;
}
}
return $closest;
}
Do a linear scan of each number and update two variables and you'll be done.
Python code (performance is O(N), I don't think it's possible to beat O(N)):
def closestNum(numArray, findNum):
diff = infinity # replace with actual infinity value
closestNum = infinity # can be set to any value
for num in numArray:
if((num - findNum) > 0 and (num - findNum) < diff):
diff = num - findNum
closestNum = num
return closestNum
Please add null checks as appropriate.
If you really want the value that's "closest" in distance, even if it's a lesser value, try this, which #Jason gets most of the credit for.
Imagine a scenario when you want the closest number to 38.9 in the following:
$array = array(37.5, 38.5, 39.5);
Most of the solutions here would give you 39.5, when 38.5 is much closer.
This solution would only take the next highest value if what you're looking is in the exact middle between two numbers in the array:
function nearest_value($value, $array) {
if (array_search($value, $array)) {
return $value;
} else {
$array[] = $value;
sort($array);
$key = array_search($value, $array);
if ($key == 0) { return $array[$key+1]; }
if ($key == sizeof($array)-1) { return $array[$key-1]; }
$dist_to_ceil = $array[$key+1]-$value;
$dist_to_floor = $value-$array[$key-1];
if ($dist_to_ceil <= $dist_to_floor) {
return $array[$key+1];
} else {
return $array[$key-1];
}
}
}
What it lacks in elegance, it makes up for in accuracy. Again, much thanks to #Jason.
Try this simple PHP function:
<?php
function nearest($number, $numbers) {
$output = FALSE;
$number = intval($number);
if (is_array($numbers) && count($numbers) >= 1) {
$NDat = array();
foreach ($numbers as $n)
$NDat[abs($number - $n)] = $n;
ksort($NDat);
$NDat = array_values($NDat);
$output = $NDat[0];
}
return $output;
}
echo nearest(90, array(0, 50, 89, 150, 200, 250));
?>
I made a shorter function for that:
function nearestNumber($num, $array) {
if(!in_array($num, $array)) $array[] = $num;
sort($array);
$idx = array_search($num, $array);
if(($array[$idx] -$array[$idx-1]) >= ($array[$idx+1] -$array[$idx])) return $array[$idx+1];
else return $array[$idx-1];
}
Works great in my case: $array = array(128,160,192,224,256,320); $num = 203 :)
It's taking the nearest number and if there's the same distance between two numbers (like 208 for my example), the next highest number is used.
+1 to Jason.
My implementation below, but not as brisk
$array = array(1,2,4,5,7,8,9);
function closest($array, $number) {
$array = array_flip($array);
if(array_key_exists($number, $array)) return $number;
$array[$number] = true;
sort($array);
$rendered = array_slice($array, $number, 2, true);
$rendered = array_keys($rendered);
if(array_key_exists(1, $rendered)) return $rendered[1];
return false;
}
print_r(closest($array, 3));
You could use array_reduce for this, which makes it more functional programming style:
function closest($needle, $haystack) {
return array_reduce($haystack, function($a, $b) use ($needle) {
return abs($needle-$a) < abs($needle-$b) ? $a : $b;
});
}
For the rest, this follows the same principle as the other O(n) solutions.
Here is my solution.
$array=array(10,56,78,17,30);
$num=65;
$diff=$num;
$min=$num;
foreach($array as $a){
if( abs($a-$num)< $diff ){
$diff=abs($a-$num);
$min=$a;
}
}
echo $min;