PHP - Avoid typing much lines [closed] - php

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
So I want to avoid typing 9999 possible outcomes doing something like this:
if($experience==50) {echo 'level 2'; }
if($experience==300) {echo 'level 3'; }
if($experience==700) {echo 'level 4'; }
if($experience==1000) {echo 'level 5'; }
Is there a way to do it? So yes i hope somebody could help me!
EDIT: The max level is 99 so i have also to check
if($level == 1 && $experience >= 50) { echo 'level 2';}

Short answer: You can't.
Long answer: You must create the experience map somewhere. In code or in a settings file/database. The system can't magical know how to map the experience to levels without you telling it how to.
What you can do however is to create a formula on how the levels are linked to experiences then it's just a small calculation and you are done.

Agreed to #Ztripez HellströmYou's answer. You need some kind of math formula, I would do something like:
$level = 1;
// level up after every 50 * $currentLevel xp gained
if ($experience >= 50 && $experience % (50*$level) == 0 && $level <= 99) {
$level++;
}
echo 'Level: ' . $level;
So above would result in the following:
$level = 1;
$experience = 50;
After if condition, $level = 2
$level = 2;
$experience = 95;
After if condition, $level = 2 //not increased
$level = 2;
$experience = 100;
After if condition, 100 % (50*2) == 0 ? true - $level = 3
etc...
Note: make sure to increase XP points by multiples of 5 so you dont go over the threshold. Or increase it by multiples of 50 or multiples of 10.

Typically, EXP-level thresholds are calculated on some kind of formula. Often this formula is exponential in nature, so you might have something like:
$base = 50;
$growth = 1.2;
$maxlevel = 100;
$levelthresholds = array();
for( $i=0; $i<$maxlevel; $i++) $levelthresholds[$i] = $base*$i*pow($growth,$i-1);
// the above is a formula I've used in many places, it works quite well
The base rate would be your EXP for Level 2, then the growth is how much you multiply the base rate by to get the next level. With 1.2, you'd get:
Level 1: 0
Level 2: 50
Level 3: 120
Level 4: 216
Level 5: 345
...
You can adapt your $base and $growth rates as desired. You can also use something like using a lower $base, rounding the result and then multiplying by 100 to get nice, rounded numbers.
The point here is to determine a formula for EXP, as this greatly reduces the amount of code you have to write. Once done you can do this:
for( $level = 0; $level < $maxlevel; $level++) {
if( $levelthresholds[$level] > $experience) break;
}
$level++; // result should be 1-based

Related

Divide an amout equally beteen no of user

I want to distribute an amount among some users equally.If amount is not divisible equally then all other member will get equal amount expect the last member who will get rest of the money.Below is what i have tried
$number = $_POST['number'];
$noOfTime = $_POST['no_of_time'];
$perHead = ceil($number / $noOfTime);
for ($i = 1; $i <= $noOfTime; $i++) {
if ($i == $noOfTime) {
echo $perHead * $noOfTime - $number;
} else {
echo $perHead;
}
}
Here if number is 7 and member are 4 the first 3 member will the 2 and the last will get 1. Like 2,2,2,1.
But this logic seems to be not working for all cases.
Please help.Thanks.
I think it should help you.
$no = 22;
$users = 8;
// count from 0 to $users number
for ($i=0;$i<$users;$i++)
// if the counting reaches the last user AND $no/$users rests other than 0...
if ($i == $users-1 && $no % $users !== 0) {
// do the math rounding fractions down with floor and add the rest!
echo floor($no / $users) + ($no % $users);
} else {
// else, just do the math and round it down.
echo floor($no / $users)." ";
}
OUTPUTS:
2 2 2 2 2 2 2 8
EDIT: I nested an if verification so the logic won't fail even if users are 1 or 2. And since it received more upvotes, I commented the code to make it more clear.

Defining percentage for random number

My rand(0,1) php function returns me the 0 and 1 randomly when I call it.
Can I define something in php, so that it makes 30% numbers will be 0 and 70% numbers will be 1 for the random calls? Does php have any built in function for this?
Sure.
$rand = (float)rand()/(float)getrandmax();
if ($rand < 0.3)
$result = 0;
else
$result = 1;
You can deal with arbitrary results and weights, too.
$weights = array(0 => 0.3, 1 => 0.2, 2 => 0.5);
$rand = (float)rand()/(float)getrandmax();
foreach ($weights as $value => $weight) {
if ($rand < $weight) {
$result = $value;
break;
}
$rand -= $weight;
}
You can do something like this:
$rand = (rand(0,9) > 6 ? 1 : 0)
rand(0,9) will produce a random number between 0 and 9, and whenever that randomly generated number is greater than 6 (which should be nearly 70% time), it will give you 1 otherwise 0...
Obviously, it seems to be the easiest solution to me, but definitely, it wont give you 1 exactly 70% times, but should be quite near to do that, if done correctly.
But, I doubt that any solution based on rand will give you 1 exactly 70% times...
Generate a new random value between 1 and 100. If the value falls below 30, then use 0, and 1 otherwise:
$probability = rand(1, 100);
if ($probability < 30) {
echo 0;
} else {
echo 1;
}
To test this theory, consider the following loop:
$arr = array();
for ($i=0; $i < 10000; $i++) {
$rand = rand(0, 1);
$probability = rand(1, 100);
if ($probability < 30) {
$arr[] = 0;
} else {
$arr[] = 1;
}
}
$c = array_count_values($arr);
echo "0 = " . $c['0'] / 10000 * 100;
echo "1 = " . $c['1'] / 10000 * 100;
Output:
0 = 29.33
1 = 70.67
Create an array with 70% 1 and 30% 0s. Then random sort it. Then start picking numbers from the beginning of the array to the end :)
$num_array = array();
for($i = 0; $i < 3; $i++) $num_array[$i] = 0;
for($i = 0; $i < 7; $i++) $num_array[$i] = 1;
shuffle($num_array);
Pros:
You'll get exactly 30% 0 and 70% 1 for any such array.
Cons: Might take longer computation time than a rand() only solution to create the initial array.
I searched for an answer to my question and this was the topic I found.
But it didn't answered my question, so I had to figure it out myself, and I did :).
I figured out that maybe this will help someone else as well.
It's regarding what you asked, but for more usage.
Basically, I use it as a "power" calculator for a random generated item (let's say a weapon). The item has a "min power" and a "max power" value in the db. And I wanted to have 80% chances to have the "power" value closer to the lower 80% of the max possible power for the item, and 20% for the highest 20% possible max power (that are stored in the db).
So, to do this I did the following:
$min = 1; // this value is normally taken from the db
$max = 30; // this value is normally taken from the db
$total_possibilities = ($max - $min) + 1;
$rand = random_int(1, 100);
if ($rand <= 80) { // 80% chances
$new_max = $max - ($total_possibilities * 0.20); // remove 20% from the max value, so you can get a number only from the lowest 80%
$new_rand = random_int($min, $new_max);
} elseif ($rand <= 100) { // 20% chances
$new_min = $min + ($total_possibilities * 0.80); // add 80% for the min value, so you can get a number only from the highest 20%
$new_rand = random_int($new_min, $max);
}
echo $new_rand; // this will be the final item power
The only problem you can have, is if the initial $min and $max variables are the same (or obviously, if the $max is bigger than the $min). This will throw an error since the random works like this ($min, $max), not the other way around.
This code can be very easily changed to have more percentages for different purposes, instead of 80% and 20% to put 40%, 40% and 20% (or whatever you need). I think the code is pretty much easy to read and understand.
Sorry if this is not helpful, but I hope it is :).
It can't do any harm either way ;).

Get one list of parents including all the children at least once in one to many relationship

I have a one to many relationship between a Report and a Location. My goal is to narrow my list of Reports down to as few Reports as possible containing all of the Locations represented.
If I simplify it to lists of numbers it would look like the following with the key being the report and the array being the list of locations:
{
1:[1,2],
2:[1],
3:[2,3],
4:[1,3,4]
}
The ideal solution would be to select Reports 1 or 3 and 4. Either 1 or 3 could be selected because they both include Location 2 and duplicate Location 1 with Report 4. Report 4 needs selected because it is the only one with Location 4.
Efficiency isn't a major concern. How is the best way to narrow the list down using PHP?
NP-completeness strikes again.
The problem you're trying to solve is called Set Cover, and, sure enough, is NP-Complete.
This means that an "efficient" (read, polynomial-time) algorithm for it is unlikely to exist.
The good news is that there are simple approximation algorithms that give you a decent approximation.
See this for how the "obvious" greedy algorithm (at each point, pick the report with the largest number of uncovered locations) gives you a log (R) approximation, where R is the number of reports (actually, it's even better than that).
If efficiency is not a problem as you have stated, I can propose you O(2^n * k) algorithm, where n is the number of lists and k is the sum of their lengths. Just take all possible combinations using bitmasks and for each of them calculate whether it covers everything or not.
P.S.
Here is an implementation(http://ideone.com/bAGpbL):
$arr = array(
0 => array(1,2),
1 => array(1),
2 => array(2,3),
3 => array(1,3,4),
);
// It is assumed that all indexes are sequential starting from 0
$total_cover = array();
foreach($arr as $sub_arr) {
foreach($sub_arr as $value) {
$total_cover[$value] = true;
}
}
$n = count($arr);
$best_cover = array_keys($arr);
for($i = 0; $i < (1 << $n); $i++) {
$cover = array();
$selected_list = array();
for($j = 0; $j < $n; $j++) {
if(($i >> $j) & 1) {
$selected_list[] = $j;
foreach($arr[$j] as $value) {
$cover[$value] = true;
}
}
}
$good_cover = true;
foreach($total_cover as $key => $value) {
if(!isset($cover[$key])) {
$good_cover = false;
break;
}
}
if($good_cover && count($selected_list) < count($best_cover)) {
$best_cover = $selected_list;
}
}
var_dump($best_cover);

Efficient Leveling System

I'm making a leveling system based on experience you have on the site. I already have all the experience stuff figured out, and how I want to do the leveling, but I need a more efficient way to do it. I know this would probably would be achieved using an array, but I don't really know how to go about doing that. Enough blabbering, though, this is what I'm trying to do...
Level 1 will be anything under 150 experience
Then I'm going to multiply that by 1.5, so
Level 2 will be anything under 225
Level 3 will be anything under 337.5 and so on. This is the inefficient way that I was going to do.
if($xp < 150){
$level = "1";
}elseif($xp < 225){
$level = "2";
}elseif($xp < 337.5){
$level = "3";
}
I could use a variable for the number and multiple by 1.5 ($number*1.5), but like I said before I don't really know how that'd work.
*Some more info..
I have a session file included on every page and I have queries that would check every time there is new experience earned and this code would be used to update the level on the database automatically.
Try
$level = min(max((int)(log($xp / 100, 1.5) + 1), 1), $maxLevel);
That takes the logarithm base 1.5 of xp/100 (i.e. the number of times you'd have to multiply 100 by to get $xp), then adds one (since log($x, 1.5) is less than one if $x is less than 1.5). The min(max(..., minLevel), maxLevel) construct lets you clamp the level to lie between 1 and $maxLevel, also avoiding any issues with negative levels (if $xp is sufficiently less than 150).
Here's how I did my level system. It's a little more advanced featuring skill points..
<?php
$level_up = ($level + 1);
if ($exp >= $max_exp)
{
$sql = "UPDATE users SET level=(level + 1) , max_exp=(exp * 1.05) , skill_points=(skill_points + 3) WHERE id='".$id."' LIMIT 1";
$res = mysql_query($sql);
if ($exp >= $max_exp)
echo '<div class="Leveled">' . 'You sucessfully leveled up to ' . $level_up . '!' . ' As a reward you were given 3 skill points!' . '</div>';
}
else
{
}
?>
I'd agree. either objects or arrays would be best.
Something like:
$array = array(
array(
'xp_required' => 150,
'level' => 1
),
array(
'xp_required' => 225,
'level' => 2
),
array(
'xp_required' => 337.5,
'level' => 3
)
);
$current_xp = 155;
foreach( $array as $reqs ){
if( $current_xp > $reqs['xp_required'] ){
$level = $reqs['level'];
}
}
echo $level';
well at the moment once you grab the users exp you can have that code block as a function to constantly check for variations in exp levels, but use a ranged statement
if($xp < 150){
$level = "1";
}elseif($xp > 150 && $xp < 225 ){
$level = "2";
}elseif($xp > 225 && $xp < 337.5){
$level = "3";
}

Project Euler || Question 10

I'm attempting to solve Project Euler in PHP and running into a problem with my for loop conditions inside the while loop. Could someone point me towards the right direction? Am I on the right track here?
The problem, btw, is to find the sums of all prime numbers below 2,000,000
Other note: The problem I'm encountering is that it seems to be a memory hog and besides implementing the sieve, I'm not sure how else to approach this. So, I'm wondering if I did something wrong in the implementation.
<?php
// The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
// Additional information:
// Sum below 100: 1060
// 1000: 76127
// (for testing)
// Find the sum of all the primes below 2,000,000.
// First, let's set n = 2 mill or the number we wish to find
// the primes under.
$n = 2000000;
// Then, let's set p = 2, the first prime number.
$p = 2;
// Now, let's create a list of all numbers from p to n.
$list = range($p, $n);
// Now the loop for Sieve of Eratosthenes.
// Also, let $i = 0 for a counter.
$i = 0;
while($p*$p < $n)
{
// Strike off all multiples of p less than or equal to n
for($k=0; $k < $n; $k++)
{
if($list[$k] % $p == 0)
{
unset($list[$k]);
}
}
// Re-initialize array
sort ($list);
// Find first number on list after p. Let that equal p.
$i = $i + 1;
$p = $list[$i];
}
echo array_sum($list);
?>
You can make a major optimization to your middle loop.
for($k=0; $k < $n; $k++)
{
if($list[$k] % $p == 0)
{
unset($list[$k]);
}
}
By beginning with 2*p and incrementing by $p instead of by 1. This eliminates the need for divisibility check as well as reducing the total iterations.
for($k=2*$p; $k < $n; $k += $p)
{
if (isset($list[k])) unset($list[$k]); //thanks matchu!
}
The suggestion above to check only odds to begin with (other than 2) is a good idea as well, although since the inner loop never gets off the ground for those cases I don't think its that critical. I also can't help but thinking the unsets are inefficient, tho I'm not 100% sure about that.
Here's my solution, using a 'boolean' array for the primes rather than actually removing the elements. I like using map,filters,reduce and stuff, but i figured id stick close to what you've done and this might be more efficient (although longer) anyway.
$top = 20000000;
$plist = array_fill(2,$top,1);
for ($a = 2 ; $a <= sqrt($top)+1; $a++)
{
if ($plist[$a] == 1)
for ($b = ($a+$a) ; $b <= $top; $b+=$a)
{
$plist[$b] = 0;
}
}
$sum = 0;
foreach ($plist as $k=>$v)
{
$sum += $k*$v;
}
echo $sum;
When I did this for project euler i used python, as I did for most. but someone who used PHP along the same lines as the one I did claimed it ran it 7 seconds (page 2's SekaiAi, for those who can look). I don't really care for his form (putting the body of a for loop into its increment clause!), or the use of globals and the function he has, but the main points are all there. My convenient means of testing PHP runs thru a server on a VMWareFusion local machine so its well slower, can't really comment from experience.
I've got the code to the point where it runs, and passes on small examples (17, for instance). However, it's been 8 or so minutes, and it's still running on my machine. I suspect that this algorithm, though simple, may not be the most effective, since it has to run through a lot of numbers a lot of times. (2 million tests on your first run, 1 million on your next, and they start removing less and less at a time as you go.) It also uses a lot of memory since you're, ya know, storing a list of millions of integers.
Regardless, here's my final copy of your code, with a list of the changes I made and why. I'm not sure that it works for 2,000,000 yet, but we'll see.
EDIT: It hit the right answer! Yay!
Set memory_limit to -1 to allow PHP to take as much memory as it wants for this very special case (very, very bad idea in production scripts!)
In PHP, use % instead of mod
The inner and outer loops can't use the same variable; PHP considers them to have the same scope. Use, maybe, $j for the inner loop.
To avoid having the prime strike itself off in the inner loop, start $j at $i + 1
On the unset, you used $arr instead of $list ;)
You missed a $ on the unset, so PHP interprets $list[j] as $list['j']. Just a typo.
I think that's all I did. I ran it with some progress output, and the highest prime it's reached by now is 599, so I'll let you know how it goes :)
My strategy in Ruby on this problem was just to check if every number under n was prime, looping through 2 and floor(sqrt(n)). It's also probably not an optimal solution, and takes a while to execute, but only about a minute or two. That could be the algorithm, or that could just be Ruby being better at this sort of job than PHP :/
Final code:
<?php
ini_set('memory_limit', -1);
// The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
// Additional information:
// Sum below 100: 1060
// 1000: 76127
// (for testing)
// Find the sum of all the primes below 2,000,000.
// First, let's set n = 2 mill or the number we wish to find
// the primes under.
$n = 2000000;
// Then, let's set p = 2, the first prime number.
$p = 2;
// Now, let's create a list of all numbers from p to n.
$list = range($p, $n);
// Now the loop for Sieve of Eratosthenes.
// Also, let $i = 0 for a counter.
$i = 0;
while($p*$p < $n)
{
// Strike off all multiples of p less than or equal to n
for($j=$i+1; $j < $n; $j++)
{
if($list[$j] % $p == 0)
{
unset($list[$j]);
}
}
// Re-initialize array
sort ($list);
// Find first number on list after p. Let that equal p.
$i = $i + 1;
$p = $list[$i];
echo "$i: $p\n";
}
echo array_sum($list);
?>

Categories