问题
How can I generate a sequence of numbers like
1 2 4 8 11 12 14 18 ...
(plus 10 every 4 numbers) with the following additional requirements:
- using only one loop
- output should stop when a value in the sequence is greater than a specified input
Examples
$input = 24;
1 2 4 8 11 12 14 18 21 22 24
$input = 20;
1 2 4 8 11 12 14 18
Here's what I tried so far:
<?php
// sample user input
$input = 20;
$number = 1;
$counter = 0;
$array = array();
//conditions
while ($counter < 4) {
$counter++;
array_push($array, $number);
$number += $number;
}
//outputs
for ($x = 0; $x < count($array); $x++) {
echo $array[$x];
echo " ";
}
回答1:
Code: (Demo)
function arrayBuilder($max,$num=1){
$array=[];
for($i=0; ($val=($num<<$i%4)+10*floor($i/4))<=$max; ++$i){
$array[]=$val;
}
return $array;
}
echo implode(',',arrayBuilder(28)),"\n"; // 1,2,4,8,11,12,14,18,21,22,24,28
echo implode(',',arrayBuilder(28,2)),"\n"; // 2,4,8,16,12,14,18,26,22,24,28
echo implode(',',arrayBuilder(20)),"\n"; // 1,2,4,8,11,12,14,18
echo implode(',',arrayBuilder(24)),"\n"; // 1,2,4,8,11,12,14,18,21,22,24
This method is very similar to localheinz's answer, but uses a technique introduced to me by beetlejuice which is faster and php version safe. I only read localheinz's answer just before posting; this is a matter of nearly identical intellectual convergence. I am merely satisfying the brief with the best methods that I can think of.
How/Why does this work without a lookup array or if statements?
- When you call
arrayBuilder()
, you must send a$max
value (representing the highest possible value in the returned array) and optionally, you can nominate$num
(the first number in the returned array) otherwise the default value is1
. - Inside
arrayBuilder()
,$array
is declared as an empty array. This is important if the user's input value(s) do not permit a single iteration in thefor
loop. This line of code is essential for good coding practices to ensure that under no circumstances should a Notice/Warning/Error occur. - A
for
loop is the most complex loop in php (so says the manual), and its three expressions are the perfect way to package the techniques that I use.- The first expression
$i=0;
is something that php developers see all of the time. It is a one-time declaration of$i
equalling0
which only occursbefore
the first iteration. - The second expression is the only tricky/magical aspect of my entire code block. This expression is called
before
every iteration. I'll try to break it down: (parentheses are vital to this expression to avoid unintended results due to operator precedence(
open parenthesis to contain leftside of comparison operator$val=
declare$val
for use inside loop on each iteration($num<<$i%4)
because of precedence this is the same as$num<<($i%4)
meaning: "find the remainder of$i
divided by 4 then use the bitwise "shift left" operator to "multiply$num
by2
for every "remainder". This is a very fast way of achieving the 4-number pattern of[don't double],[double once],[double twice],[double three times]
to create:1,2,4,8
,2,4,8,16
, and so on.bitwise operators
are always more efficient thanarithmetic operators
.
The use of the arithmetic operator modulo ensure that the intended core number pattern repeats every four iterations.+
add (not concatenation in case there is any confusion)10*floor($i/4)
round down$i
divided by4
then multiply by10
so that the first four iterations get a bonus of0
, the next four get10
, the next four get20
, and so on.)
closing parenthesis to contain leftside of comparison operator<=$max
allow iteration until the$max
value is exceeded.
++$i
is pre-incrementing$i
at theend
of every iteration.
- The first expression
回答2:
Complex solution using while
loop:
$input = 33;
$result = [1]; // result array
$k = 0; // coeficient
$n = 1;
while ($n < $input) {
$size = count($result); // current array size
if ($size < 4) { // filling 1st 4 values (i.e. 1, 2, 4, 8)
$n += $n;
$result[] = $n;
}
if ($size % 4 == 0) { // determining each 4-values sequence
$multiplier = 10 * ++$k;
}
if ($size >= 4) {
$n = $multiplier + $result[$size - (4 * $k)];
if ($n >= $input) {
break;
}
$result[] = $n;
}
}
print_r($result);
The output:
Array
(
[0] => 1
[1] => 2
[2] => 4
[3] => 8
[4] => 11
[5] => 12
[6] => 14
[7] => 18
[8] => 21
[9] => 22
[10] => 24
[11] => 28
[12] => 31
[13] => 32
)
回答3:
On closer inspection, each value in the sequence of values you desire can be calculated by adding the corresponding values of two sequences.
Sequence A
0 0 0 0 10 10 10 10 20 20 20 20
Sequence B
1 2 4 8 1 2 4 8 1 2 4 8
Total
1 2 4 8 11 12 14 18 21 22 24 28
Solution
Prerequisite
The index of the sequences start with 0
. Alternatively, they could start with 1
, but then we would have to deduct 1
, so to keep things simple, we start with 0
.
Sequence A
$a = 10 * floor($n / 4);
The function floor()
accepts a numeric value, and will cut off the fraction.
Also see https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
Sequence B
$b = 2 ** ($n % 4);
The operator **
combines a base with the exponent and calculates the result of raising base to the power of exponent.
In PHP versions prior to PHP 5.6 you will have to resort to using pow()
, see http://php.net/manual/en/function.pow.php.
The operator %
combines two values and calculates the remainder of dividing the former by the latter.
Total
$value = $a + $b;
Putting it together
$input = 20;
// sequence a
$a = function ($n) {
return 10 * floor($n / 4);
};
// sequence b
$b = function ($n) {
return 2 ** ($n % 4);
};
// collect values in an array
$values = [];
// use a for loop, stop looping when value is greater than input
for ($n = 0; $input >= $value = $a($n) + $b($n) ; ++$n) {
$values[] = $value;
}
echo implode(' ', $values);
For reference, see:
- http://php.net/manual/en/control-structures.for.php
- http://php.net/manual/en/function.floor.php
- http://php.net/manual/en/language.operators.arithmetic.php
- http://php.net/manual/en/function.implode.php
For an example, see:
- https://3v4l.org/pp9Ci
来源:https://stackoverflow.com/questions/46025060/make-an-output-of-1-2-4-8-11-12-14-18-using-one-for-loop