Make an output of 1 2 4 8 11 12 14 18… using one for loop

风格不统一 提交于 2019-12-25 08:49:48

问题


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 is 1.
  • 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 the for 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 equalling 0 which only occurs before 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 by 2 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 than arithmetic 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 by 4 then multiply by 10 so that the first four iterations get a bonus of 0, the next four get 10, the next four get 20, 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 the end of every iteration.



回答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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!