Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
Building a simple algorithm where, based on various inputs (x, y, z), a phrase will be sent back to the user. What's the fastest way to store and search for these phrases?
Currently, I'm using various if statements. But I'm wondering if I should instead use some sort of dictionary, or hash maps.
I've posted a code sample here.
Thank you!
for ( $i=0 ; $i < count($age) ; $i++ ) {
if ($age[$i] >= 95) {
$something = "You need a plant";
} elseif ($age[$i] >= 85 && $age[$i] < 95) {
$something = "You need a small rug and a plant";
} elseif ($age[$i] >= 75 && $age[$i] < 85) {
$something = "You need a rug";
} elseif ($age[$i] >= 60 && $age[$i] < 75) {
$something = "You need a coffee table";
} elseif ($age[$i] >= 55 && $age[$i] < 60) {
$something = "You need a small table";
} elseif ($age[$i] >= 51 && $age[$i] < 55) {
$something = "You need a table";
} elseif ($age[$i] >= 42 && $age[$i] < 51) {
$something = "You need a knife";
} elseif ($age[$i] >= 35 && $age[$i] < 42) {
$something = "You need a knife and a jar";
} elseif ($age[$i] >= 22 && $age[$i] < 35) {
$something = "You need a knife, bowl, jar and a small book";
} elseif ($age[$i] >= 5 && $age[$i] < 22) {
$something = "You need a knife, bowl, jar and a book";
} else {
$something = "Go back to sleep";
}
if ($somethingElse[$i] >= 30) {
$also = " and we'll see you tomorrow!";
} else {
$also = ".";
}
$phrase[$i] = ''.$something.''.$also.'';
}
For the first part of the phrase, you could use an array with the age limits as keys and the texts as values. For the second part, which is more of a yes/no choice, you can use a ternary operator.
foreach is also nicer for looping over the $age array:
$phrases = [
95 => "You need a plant",
85 => "You need a small rug and a plant",
75 => "You need a rug",
65 => "You need a coffee table",
55 => "You need a small table",
51 => "You need a table",
42 => "You need a knife",
35 => "You need a knife and a jar",
22 => "You need a knife, bowl, jar and a small book",
5 => "You need a knife, bowl, jar and a book",
];
foreach ($age as $i => $a) {
foreach ($phrases as $limit => $something) {
if ($a >= $limit) break;
}
$also = $somethingElse[$i] >= 30 ? " and we'll see you tomorrow!" : ".";
$phrase[] = "$something$also";
}
Note that unless you already had an array $phrase, you don't need to specify the index $i, as [] will just make it append at the end. So in the end you only need $i for $somethingElse. The reason why it is still needed in the foreach syntax.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I was in a job interview and was asked to solve FizzBuzz with PHP.
Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.
I never heard of FizzBuzz before but here is how I solved it because I didn't know modulo or how to use it.:
for ($i = 1; $i <= 100; $i++){
if($i / 3 == round($i / 3) && $i / 5 == round($i / 5)){
echo $i . " is FizzBuzz<br />";
}
else if($i / 3 == round($i / 3)){
echo $i . " is Fizz<br />";
}
else if($i / 5 == round($i / 5)){
echo $i . " is Buzz<br />";
}
else {
echo $i."<br />";
}
}
I googled and didn't find any solution with round and that got me thinking that maybe there is something wrong with it, this is one of the solutions I found that is close to mine:
for ($i = 1; $i <= 100; $i++){
if($i % 3 == 0 && $i % 5 ==0){
echo "FizzBuzz<br />";
}
else if($i % 3 == 0){
echo "Fizz<br />";
}
else if($i % 5 == 0){
echo "Buzz<br />";
}
else {
echo $i."<br />";
}
}
My code is working fine but is there anything wrong with it that I don't see?
Actually they are testing how you will solve such simple task. It should be increadibly optimized, the code shouldbe clean and easy readable.
Your version of code is not good.
The version you've found in the internet is better, but it's not ideal from the point of the optimization.
Try to think how to get the goal with less actions.
Some tips:
do not use functions (such as range) for this task - it will only slow down the script execution time
use operator "for" for this task, do not use any other (while, do-while, foreach), because operator "for" the best fits in this case (you know how many iterations you need).
do not use round function, use modulus operator "%", because any function works slower than any operator
in the result you need to get code, in which the number of operations will be the least as possible (the number of "if" statements, the number of operators like "==" or "%"
Use 15 instead of % 3 && % 5 - less calculations - faster execution time.
My example of code:
for ($i = 1; $i <= 100; $i++) {
if ($i % 15 == 0) {
echo 'FizzBuzz<br>';
} elseif ($i % 3 == 0) {
echo 'Fizz<br>';
} elseif ($i % 5 == 0) {
echo 'Buzz<br>';
} else {
echo $i . '<br>';
}
}
Code style and lack of optimization gives impression of a newbie to the interviewer. My tips are:
Follow PSR-2
Never use else (restructure, use early returns/continues)
Always try to reduce the number of ifs (code complexity)
Use "identical" operators for comparison when dealing with integers and nulls (and any other type)
Do not use <br/> when HTML is never mentioned
Try to keep maintainability:
Extract match calculations in variables/functions, so they can be easily changed
Do not overcomplicate.
Try to optimize mathematically:
Use %15 instead of %3 and %5 check
You can also skip the above at all (check for %15), as you already have calculated that. Boolean operations are much faster.
Try not to calculate something twice.
IMHO, to follow all good practices your code should look like this:
for ($i = 1; $i <= 100; $i++) {
$isFizz = (0 === $i % 3);
$isBuzz = (0 === $i % 5);
if (!$isFizz && !$isBuzz) {
echo $i . PHP_EOL;
continue;
}
if ($isFizz) {
echo 'Fizz';
}
if ($isBuzz) {
echo 'Buzz';
}
echo PHP_EOL;
}
Test
There is yet another tricky solution
for ($i = 1; $i <= 100; $i++) {
switch ($i % 15) {
case 3:
case 6:
case 9:
echo 'Fizz';
break;
case 5:
case 10:
echo 'Buzz';
break;
case 0:
echo 'FizzBuzz';
break;
default:
echo $i;
break;
}
echo PHP_EOL;
}
if you read carefully it says "instead".
this is another short and clean solution of the FizzBuzz problem :
foreach (range(1, 100) as $number) {
if(0 !== $number % 3 && 0 !== $number % 5) {
echo $number.'<br>';
continue;
}
if(0 === $number % 3) {
echo 'Fizz';
}
if(0 === $number % 5) {
echo 'Buzz';
}
echo '<br>';
}
the output is :
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
Example: When I have only two select options
input_one = 'one';
input_two = 'two';
when I select only input_one
if((isset($_POST['input_one']) && (!isset($_POST['input_two']) {
//query
}
When I select only input_two
elseif((!isset($_POST['input_one']) && (isset($_POST['input_two'])) {
//query
}
When I don't select any of those
elseif((!isset($_POST['input_one']) && (!isset($_POST['input_two'])) {
//query
}
When I select both of them
elsif((isset($_POST['input_one']) && (!isset($_POST['input_two'])) {
//query
}
but when I have 6 input options then I need to write a number of conditions. How do I reduce those, if there is any alternate method?
$query = "some query";
foreach($_POST as $key => $post){
switch($key){
case 1:
$query .= " AND some condition";
break;
case 2:
$query .= " AND some condition";
break;
}
}
mysqli_query($sql,$query);
I will give my answer based on the hypothesis, that the inputs are dependent on each other. Or let's call it interdependent.
If you are looking at your code, you'll notice that your conditions can be abstracted by basic binary calculations.
So let's assume we speak about two states:
isset = 1,
!isset = 0
And each input can have one of the two states.
If you have 1 input, then you can have 2exp(1) = 2 possibilities.
0
1
If you have 2 inputs, then you can have 2exp(2) = 4 possibilities. Nota bene: each column represents the states that an input can take in dependence with the other one.
00
01
10
11
If you have 3 inputs, then you can have 2exp(3) = 8 possibilities.
000
001
010
011
100
101
110
111
...
If you have 6 inputs, then you can have 2exp(6) = 64 states in total.
000000
000001
...
111110
111111
The general formula is:
N = 2exp(x)
Where:
N = total number of possibilities;
x: Number of observable elements, each can achieve 2 states (0 or 1).
So, as you see, it's hard to maintain such a structure.
And take into consideration that you'll probably need to make validations on empty() values too. At least.
I recommend you, that you really change something in your code, so that your inputs are not anymore so dependable on each other.
So, my answer would be "no", there is no way to do it, better than you already do it. The if-elseif-else is the proper one. But that doesn't help you a lot if you have more than, let's say 3 interdependent inputs.
EDIT 1:
One relative maintainable solution would be to divide the interdependent inputs in groups of maximum 3. And only for these 3 to send a request to the server.
EDIT 2:
Or to ensure that in one request moment, only one input has been set or have a value.
Or to ensure that each of the inputs have at least a default value (like '0') on the request moment.
an example for four conditions:
<?php
$c = 0;
//basic conditions...............
if( isset($_POST['input_1'] ) $c += 1; //set 1st bit
if( isset($_POST['input_2'] ) $c += 2; //set 2nd bit
if( isset($_POST['input_3'] ) $c += 4; //set 3rd bit
if( isset($_POST['input_4'] ) $c += 8; //set 4th bit
//additional conditions...............
if($c < 8){ //0..7
if($c < 4){ //0..3
if($c < 2){ //0..1
if($c == 0){ //nothing set
}else{ // 1 //input_1
}
}else{ //2..3
if($c == 2){ //input_2
}else{ //3 //input_1 input_2
}
}
}else{ //4..7
if($c < 6){//4..5
if($c == 4){ //input_3
}else{ // 5 //input_1 input_3
}
}else{//6..7
if($c == 6) { //input_2 input_3
}else{ // 7 //input_1 input_2 input_3
}
}
}
}else{ //8..15
if($c < 12){ //8..11
if($c < 10){ //8..9
if($c == 8){ //input_4
}else{ // 9 //input_1 input_4
}
}else{ //10..11
if($c == 10){ //input_2 input_4
}else{ //11 //input_1 input_2 input_4
}
}
}else{ //12..15
if($c < 14){//12..13
if($c == 12){ //input_3 input_4
}else{ // 13 //input_1 input_3 input_4
}
}else{//14..15
if($c == 14) { //input_2 input_3 input_4
}else{ // 15 //input_1 input_2 input_3 input_4
}
}
}
}
?>
In this case we have to check 8 condition to get all the issets states.
So it could not be the optimal solution when we have only 4 parameters what produces only 2^4=16 possibilities.
But if we take 6 arguments:
Then we have to check 6 basic onditions and 6 additionals.
So it gives 12 conditions to check when there is 2^6=64 possibilites.
Lets take 7 args:
Then we have 7 + 7 = 14 condition to check to get one of 2^7=128 possibilities.
And now we start to see advantages for bigger amount of arguments.
I have managed to create an algorithm to check the rank of a poker hand. It works 100% correctly, but it's very slow. I've been analysing the code, and the check straight function is one of the slowest parts of it.
So my question is, is there a better way of calculating whether a hand make a straight?
Here is some details:
7 cards, 2 from holder, 5 from board. A can be high or low.
Each card is assigned a value:
2 = 2
3 = 3
..
9 = 9
T = 10
J = 11
Q = 12
K = 13
A = 14
The script has an array of all 7 cards:
$cards = array(12,5,6,7,4,11,3);
So now I need to be able to sort this into an array where it:
discards duplicates
orders the card from lowest to highest
only returns 5 consecutive cards I.e. (3,4,5,6,7)
It needs to be fast; loops and iterations are very costly. This is what I currently use and when it tries to analyse say 15000 hands, it takes its toll on the script.
For the above, I used:
discard duplicates (use array_unique)
order cards from lowest to highest (use sort())
only return 5 consecutive cards (use a for loop to check the values of cards)
Does anyone have any examples of how I could improve on this? Maybe even in another language that I could perhaps look at and see how it's done?
Instead of working with array deduping and sorting, consider using a bitmask instead, and setting bits to 1 where the card value is set. A bitmask works like a Set datastructure and comes with additional advantages when it comes to detecting contiguous elements.
for ($i = 0; $i < count($cards); $i++) {
$card = $cards[$i];
// For each card value, set the bit
if ($card == 14) {
// If card is an ace, also set bit 1 for wheel
$cardBitmask |= 0x2;
}
$cardBitmask |= (1 << $card);
}
// To compare, you simply write a for loop checking for 5 consecutive bits
for($i = 10; $i > 0; $i--)
{
if ($cardBitmask & (0x1F << $i) == (0x1F << $i)) {
// Straight $i high was found!
}
}
Consider the Java implementation at this link. I've included it here:
public static boolean isStraight( Card[] h )
{
int i, testRank;
if ( h.length != 5 )
return(false);
sortByRank(h); // Sort the poker hand by the rank of each card
/* ===========================
Check if hand has an Ace
=========================== */
if ( h[4].rank() == 14 )
{
/* =================================
Check straight using an Ace
================================= */
boolean a = h[0].rank() == 2 && h[1].rank() == 3 &&
h[2].rank() == 4 && h[3].rank() == 5 ;
boolean b = h[0].rank() == 10 && h[1].rank() == 11 &&
h[2].rank() == 12 && h[3].rank() == 13 ;
return ( a || b );
}
else
{
/* ===========================================
General case: check for increasing values
=========================================== */
testRank = h[0].rank() + 1;
for ( i = 1; i < 5; i++ )
{
if ( h[i].rank() != testRank )
return(false); // Straight failed...
testRank++; // Next card in hand
}
return(true); // Straight found !
}
}
A quick Google search for "check for poker straight (desired_lang)" will give you other implementations.
You could just sort the cards and loop over them in an array - saving always the last card and compare them with the current one.
$cards = array(12,5,6,7,4,11,3);
sort($cards);
$last = 0;
$count = 0;
$wheel = false;
foreach ($cards as $card) {
if ($card == $last) {
continue;
} else if ($card == ++$last) {
$count++;
} else {
if ($last == 6) $wheel = true;
$count = 1;
$last = $card;
}
if ($count == 5 || ($card == 14 && $wheel)) {
echo "straight $last";
$straight = range($last - 4, $last);
break;
}
}
You may go like this, you don't need to sort or anything (assuming that 2 is 2 and 14 is ace):
$cards = [12,5,6,7,4,11,3];
function _inc(&$i) {
if ($i == 14)
$i = 2;
else
$i++;
return $i;
}
$straight = false;
for($i = 2; $i <= 14; $i++) {
$ind = $i;
if (!in_array($ind, $cards)) continue;
$s = [$ind, _inc($ind), _inc($ind), _inc($ind), _inc($ind)];
$straight = count(array_intersect($s, $cards)) == count($s);
if ($straight) break;
}
print $straight;
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I'd like to know if my conditions are written with the right way (for the first statement). (in therm of optimization, readability)
if(($nb_post_by_user >= 3 && $nb_post_by_user < 5) || ( $nb_post_by_user >= 3 && ($nb_comm_by_user < 15 || $percent_voted < 25) )){
// Call function A();
}
else if( ($nb_post_by_user >= 5 && $nb_post_by_user < 10) && ($nb_comm_by_user >= 15 && $nb_comm_by_user < 30) && ($percent_voted >= 25 && $percent_voted < 70) ){
// Call function B();
}
Or does the first statement could be written that way? (second statement will be then nested).
if($nb_post_by_user >= 3){
if($nb_comm_by_user >= 15 || $percent_voted >= 25){
// Call function B
}
else{
// Call function A
}
}
if(($nb_post_by_user >= 3 && $nb_post_by_user < 5) || ( $nb_post_by_user >= 3 && ($nb_comm_by_user < 15 || $percent_voted < 25) )){
// Call function A();
} else if( ($nb_post_by_user >= 5 && $nb_post_by_user < 10) && ($nb_comm_by_user >= 15 && $nb_comm_by_user < 30) && ($percent_voted >= 25 && $percent_voted < 70) ){
// Call function B();
}
Edited one :
if($nb_post_by_user >= 3) {
if($nb_post_by_user < 5 || $nb_comm_by_user < 15 || $percent_voted < 25) {
// Call function A();
} else if($nb_post_by_user < 10 && $nb_comm_by_user >= 15 && $nb_comm_by_user < 30 && $percent_voted >= 25 && $percent_voted < 70) {
// Call function B();
}
}
In my opinion both the statements are not doing the same piece of work. The best logical reason i came up with is this :
The first piece of code will get executed if one of the condition written in the if statements are true. But in the second piece of code. The code will always gets executed if the first if condition holds true because of the presence of else statement instead of if else. So both are doing a different work.
hope it helps :)
I have the following php code which works but it is too long and cumbersome for reading...
// get the row
if ($taktArticle[0]['t1position3'] > 3 AND $taktArticle[0]['t1position3'] < 7 ) {
$row = "row1";
}
if ($taktArticle[0]['t1position3'] > 7 AND $taktArticle[0]['t1position3'] < 12 ) {
$row = "row2";
}
if ($taktArticle[0]['t1position3'] > 12 AND $taktArticle[0]['t1position3'] < 17 ) {
$row = "row3";
}
if ($taktArticle[0]['t1position3'] > 17 AND $taktArticle[0]['t1position3'] < 22 ) {
$row = "row4";
}
if ($taktArticle[0]['t1position3'] > 22 AND $taktArticle[0]['t1position3'] < 27 ) {
$row = "row5";
}
// get the columns
if ($taktArticle[0]['t1position3'] == 3
or $taktArticle[0]['t1position3'] == 8
or $taktArticle[0]['t1position3'] == 13
or $taktArticle[0]['t1position3'] == 18
or $taktArticle[0]['t1position3'] == 23) {
$col = "col1";
}
if ($taktArticle[0]['t1position3'] == 4
or $taktArticle[0]['t1position3'] == 9
or $taktArticle[0]['t1position3'] == 14
or $taktArticle[0]['t1position3'] == 19
or $taktArticle[0]['t1position3'] == 24) {
$col = "col2";
}
if ($taktArticle[0]['t1position3'] == 5
or $taktArticle[0]['t1position3'] == 10
or $taktArticle[0]['t1position3'] == 15
or $taktArticle[0]['t1position3'] == 20
or $taktArticle[0]['t1position3'] == 25) {
$col = "col3";
}
if ($taktArticle[0]['t1position3'] == 6
or $taktArticle[0]['t1position3'] == 11
or $taktArticle[0]['t1position3'] == 16
or $taktArticle[0]['t1position3'] == 21
or $taktArticle[0]['t1position3'] == 26) {
$col = "col4";
}
if ($taktArticle[0]['t1position3'] == 7
or $taktArticle[0]['t1position3'] == 12
or $taktArticle[0]['t1position3'] == 17
or $taktArticle[0]['t1position3'] == 22
or $taktArticle[0]['t1position3'] == 27) {
$col = "col5";
}
Now... I have to repeat this from ($taktArticle[0]['t1position3'] until ($taktArticle[0]['t1position11']
As you understand, code will become huge...Anyone have an idea how this code can be shortened?
Regards, John
you can create functions to clean up the code. There are obvious patterns in your code, looking for those patterns and generalizing those patterns is a key requirement to cleaning up your code. I'm sure a PHP guru could find a more concise way to accomplish this, but a basic example is something like:
function get_row($position) {
$row_ranges = array(
array(3, 7),
array(7, 12),
// etc
);
foreach ($row_ranges as $row_index => $range) {
if ($range[0] < $position && $position < $range[1]) {
return sprtintf('row%s', $row_index + 1)
}
}
}
All the row ranges are kept in a centralized location inside of your function, and there are no more repeated conditionals
function get_column($value) {
// looks like you are starting at 3 and have increments of 5
// 3, 8, 13, 18
// you could loop through and calculate these, or hardcode them in
// use `in_array` to clean up the multiple or statements
if (in_array($value, array(3, 8, 13, 18))) {
}
}
Good answer on the first part by Christoph already.
As for the columns, instead of multiple comparisons inside the IFs, use in_array.
Now... I have to repeat this from ($taktArticle[0]['t1position3'] until ($taktArticle[0]['t1position11']
Your fault for choosing such a sub-optimal data structure.
Why is this data not organized as $taktArticle[0]['t1position'][3] to $taktArticle[0]['t1position'][11], so that you could easily loop over the positions …?
(And if there’s analogues to t1position, so you have t2position, t3position etc. as well – then those should be organized in arrays too.)
My idea would be to make an array an look it up:
$rowtbl = array(4 => 1, 1, 1, 8 => 2, 2, 2, 2);
$row = 'row'.$rowtbl[$taktArticle[0]['t1position3']];
of course the array could get large but you can build something from array_merge and range.
Moreover it seems to me that you could do something like:
$row = ceil(($taktArticle[0]['t1position3']-3)/5);
$col = ($taktArticle[0]['t1position3']-3)%5;
You'd have to check the exact parameters for 3 and 5 (3 would be the starting points and 5 would be the number of cols per row.
In the columns section, rather than joining several OR operators you could check whether your $taktArticle[0]['t1position3'] value is present in an array.
Using PHP in_array (http://php.net/manual/en/function.in-array.php) for example:
if(in_array($taktArticle[0]['t1position3'], [3, 8, 13, 18, 23])) {
$col = "col1";
}
Although this is cleaner, you're still hard-coding all the values in this mapping, so the maintenance overhead will grow as you add new cases.
You can use
if(in_array($taktArticle[0]['t1position3'],array(7,12,17,22,27)))
In place of this type of statement
if ($taktArticle[0]['t1position3'] == 7
OR $taktArticle[0]['t1position3'] == 12
OR $taktArticle[0]['t1position3'] == 17
OR $taktArticle[0]['t1position3'] == 22
OR $taktArticle[0]['t1position3'] == 27)
Or you can do like this
if((((int)$taktArticle[0]['t1position3'])-7)%5==0||((int)$taktArticle[0]['t1position3'])-7)==0)
for this statement
if ($taktArticle[0]['t1position3'] == 7
OR $taktArticle[0]['t1position3'] == 12
OR $taktArticle[0]['t1position3'] == 17
OR $taktArticle[0]['t1position3'] == 22
OR $taktArticle[0]['t1position3'] == 27)
another way is use boolean operators in switch to get row and recursive function to get col
//to get Row:
$m = $taktArticle[0]['t1position3'];
switch($m){
case ($m>3 && $m< 7): $row = 'row1'; break;
case ($m>7 && $m< 12): $row = 'row2'; break;
case ($m>12 && $m< 17): $row = 'row3'; break;
case ($m>17 && $m< 22): $row = 'row4'; break;
case ($m>22 && $m< 27): $row = 'row5'; break;
}
//To get Col
function rootNum($num){
return $num-5>0?rootNum($num-5):$num;
}
$n = rootNum($taktArticle[0]['t1position3']);
$col = 'col' . ($n -2);
There are patterns in there so you can use division instead of arrays:
$temp = $taktArticle[0]['t1position3']-2
if($temp%5 != 0){
$row = "row".ceil(($taktArticle[0]['t1position3']-2)/5);
}
$col = "col".(($taktArticle[0]['t1position3']-2)%5);