Related
I have a problem about loop in line of true table. I need get all lines where only there are 3 positions with value equal 1, Look sample:
For a table with 4 elements exists 16 possibilities but I need just 4 of their. So I need to get the 4 positions without pass in all others positions:
[00002] 0-0-0-1
[00003] 0-0-1-0
[00004] 0-0-1-1
[00005] 0-1-0-0
[00006] 0-1-0-1
[00007] 0-1-1-0
[00008] 0-1-1-1 I want this
[00009] 1-0-0-0
[00010] 1-0-0-1
[00011] 1-0-1-0
[00012] 1-0-1-1 I want this
[00013] 1-1-0-0
[00014] 1-1-0-1 I want this
[00015] 1-1-1-0 I want this
[00016] 1-1-1-1
Look for 4 elements it's easy make a loop but you imagine a table with millions elements. Following is a code for ilustre my first thought:
$t1 = time();
echo "\n\n";
echo "################ Started in :" . date('d/m/Y H:i:s', $t1);
echo "\n\n";
$votesCount = 4;
$possibilities = pow(2, $votesCount);
for ($i = 0; $i < $possibilities; $i++) {
$binare = str_pad(decbin($i), $votesCount, 0, STR_PAD_LEFT);
$arrayBinare = str_split($binare);
$posVote = str_pad($i + 1, 5, 0, STR_PAD_LEFT);
$c = "[" . $posVote . '] ' . implode('-', $arrayBinare);
if (array_sum($arrayBinare) == 3) {
echo "<b>$c</b> I want this<br>";
continue;
}
echo "$c <br>";
}
$t2 = time();
echo "\n";
echo "################ Finished in:" . date('d/m/Y H:i:s', $t2);
echo "\n";
echo "################ Duraction: " . ($t2 - $t1) . ' seconds';
echo "\n";
A recursive solution looks like this:
function Enumerate(len, rem)
if rem = 0 then return [0]^len
if len < rem then return {}
if len = rem then return {[1]^rem}
return ({[0]} & Enumerate(len - 1, rem))
union
({[1]} & Enumerate(len - 1, rem - 1))
The notation [1]^rem means the vector of length rem containing only 1; the operator & returns all vectors obtained by concatenating vectors from the RHS to vectors in the LHS. This short-circuits when it detects that solutions can't be obtained or when there is only one possible solution, but you could remove this to print all permutations and just check them. Example execution:
Enumerate(4, 3) = {[0,1,1,1],[1,0,1,1],[1,1,0,1],[1,1,1,0]}
4 < 3? no
4 = 3? no
[0] & Enumerate(3, 3) = [0] & [1,1,1] = [0,1,1,1]
3 = 0? no
3 < 3? no
3 = 3? yes, return [1,1,1]
[1] & Enumerate(3, 2) = [1] & ([0,1,1],[1,0,1],[1,1,0]| = {[1,0,1,1],[1,1,0,1],[1,1,1,0]}
2 = 0? no
3 < 2? no
3 = 3? no
[0] & Enumerate(2, 2) = [0] & [1,1] = [0,1,1]
2 = 0? no
2 < 2? no
2 = 2? yes, return [1,1]
[1] & Enumerate(2, 1) = [1] & {[0,1],[1,0]) = {[1,0,1],[1,1,0]}
1 = 0? no
2 < 1? no
2 = 1? no
[0] & Enumerate(1, 1) = [0] & [1] = [0,1]
1 = 0? no
1 < 1? no
1 = 1? yes, return [1]
[1] & Enumerate(1, 0) = [1] & [0] = [1,0]
0 = 0? yes, return [0]
If you want this to be iterative rather than recursive, add a stack data structure and manage the stack explicitly in a while loop while the stack's not empty.
If you want the position of each solution in the ordered space of all solutions - simply interpret the vector as the sequence of digits of a len-digit binary number and add one. So, [0,1,1,1] becomes (0111)b = (7)d + 1 = (8)d = 8.
With millions of rows, you definitely want to move the selection logic on the database side, or you'll have to make insane amounts of calculations on the PHP side, leading to potential performance & memory issues.
Assuming you have 4 vote columns storing 1 or 0:
SELECT *
FROM votes
WHERE (vote1 + vote2 + vote3 + vote4) = 3
And if you are storing booleans, you can cast it as an integer : WHERE CAST(vote1 AS SIGNED INTEGER) + ...
I am working on a scratch card in PHP and i need to build the backbone behind it.
My plan is as following:
Generate 15 random numbers ( there are 8 containers and 15 images to choose from. ).
Each number correspondences with a image ( 1 is image 1, 2 is image 2 etc. ).
Show a random image (1/15) on container 1, show random image (1/15) on container 2 etc. There are 8 containers to be filled.
What i currently can't figure out is to check if there are duplicate numbers, and if so it is fine to have 2 duplicates but not 3 since that would mean a win.
What i have now is this:
$images = array();
for ($i = 0; $i < 8; $i++) {
$random[$i] = rand(1,15);
}
This will fill $random with 15 numbers i can use. Now i want to check if within those 15 there are duplicates. But the trick is that duplicates are no problem ( and even preferred on some degree ) but once there are 3 of the same numbers i want one of those to change again in a random number ( and re-check for duplicates).
So what should be fine ( 2x 8 is fine, 2x 1 is fine ):
Container 1: 14
Container 2: 8
Container 3: 8
Container 4: 4
Container 5: 1
Container 6: 9
Container 7: 1
Container 8: 12
What should be incorrect ( 3x 14 is not fine ):
Container 1: 14
Container 2: 8
Container 3: 4
Container 4: 14
Container 5: 14
Container 6: 9
Container 7: 1
Container 8: 12
You guys have any advice on what the right way is here? I am trying to stay away from a lot of "if's".
Two sets of 15 ranges and shuffle them.
Then slice out 8 items from the array.
$random = array_merge(range(1,15), range(1,15));
shuffle($random);
$random = array_slice($random, 1,8);
Print_r($random);
You could create an array with each selectable element twice.
Then, 8 times, pick a random index from the array and remove it from the array.
$random = [ ];
for ($i = 1; $i <= 15; $i++) {
$random[] = $i;
$random[] = $i; // Twice
}
for ($i = 0; $i < 8; $i++) {
$pick = rand(0, count($random));
// Use $random[$pick]
// Remove array key at index $pick
}
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 " ";
}
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.
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
)
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
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 7 years ago.
Improve this question
For example I got this integer 110
Let Nx be an integer
Is it possible to generate N1 to N8 (N1 to N8 is an integer) using PHP that confine the following rules:
N1 + N2 + N3 + N4 + N5 + N6 + N7 + N8 = 110 ?
Got no idea what function (e.g. rand(), mt_rand(), using loops etc.) can PHP do that.
the shortest solution for your problem that i can think of would be:
$m = 110; // desired sum
$v = 8; // desired elements
$x = []; // result
for($i = 1; $i < $v; $i++)
$x[$i] = rand(1, $m - array_sum($x) - (($v - $i) * $v));
$x[0] = $m-array_sum($x);
// print results
print_r($x);
printf("Elements sum to: %d\n",array_sum($x));
which results in
Array
(
[1] => 16
[2] => 13
[3] => 15
[4] => 23
[5] => 5
[6] => 7
[7] => 1
[0] => 30
)
Elements sum to: 110
so hurray, just what you wanted, although it could be optimized for nicer spread. but let me explain line for line.
first i just initialized three variables. $m is the desired sum you want to have for all of your elements. $v is the number of elements (N's) you want to generate. and $x holds an array with all N's that where calculated, so the result we are interested in:
$m = 110; // desired sum
$v = 8; // desired elements
$x = []; // result
now we start a loop through the elements we want to calculate. notice we start with 1, because we will be using 0 for calculating last value later on:
for($i = 1; $i < $v; $i++)
and here now comes the "brainwork" we are setting the current element $x[$i] to a random value. in PHP we can pass min and max values to the random function, so i chose 1 as min to avoid 0's and the max i chose i shall explain a bit more detailed at the end, but obviously we want to avoid having 110 as first value and the rest zeros and this is how we do so:
$x[$i] = rand(1, $m - array_sum($x) - (($v - $i) * $v));
in the last step now we need to make sure that we will end up with the desired sum, so instead of going random, i simply set the element to the desired maximum sum minus the sum of all existing elements:
$x[0]=$m-array_sum($x);
the last rows of the code just print out the results, but as promised, let me explain the "complicated" max part of the random function that i chose.
$m - array_sum($x) - (($v - $i) * $v)
the one PHP function that comes really handy here is the array_sum function, which adds up all elements of an array. if we would just call rand(1,110) on every element, then we would could easily end up with a sum higher than 110, so we subtract the sum of all existing elements from the max value to avoid this on first hand: $m - array_sum($x). so if last three generated elements add up to 43 then next element can be no higher than 67 (110 - 43). we still could end up with a result like (12,7,24,5,62,0,0,0) and we don't wan't zeros, so we will have to subtract the number of elements left $v - $i which will avoid the zeros.
again we still could end up with a result like (12,7,24,5,59,1,1,1) and that does not look nice, so last optimization i did was to multiply this by an arbitrary number to leave more space for higher values - in this case i just used the $v (the number of elements) as arbitrary number, but you can play around with this value i.e. (($v - $i) * 12)) until you get desired result.
Thanks a lot! Almost the same as what I wanted.
$m = 110; // desired sum
$v = 8; // desired elements
$x = []; // result
for($i = 1; $i < $v; $i++)
$x[$i] = mt_rand(1, $m - array_sum($x) - (($v - $i) * $v));
$x[] = $m-array_sum($x);
// print results
print_r($x);
printf("Elements sum to: %d\n",array_sum($x));
I've changed rand() into mt_rand() which might rand things faster
And $x[0] = $m-array_sum($x); => $x[] = $m-array_sum($x);
So that the Array can start from 1 and ends at 8
Array
(
[1] => 16
[2] => 13
[3] => 15
[4] => 23
[5] => 5
[6] => 7
[7] => 1
[8] => 30
)
Elements sum to: 110
I've got these two functions:
function drawNumber($drawnNumbers){
$unique = true;
while ($unique == true){
$number = mt_rand(10, 69);
if (!in_array($number, $drawnNumbers)){
return $number;
$unique = false;
}
}
}
fillCard(); ?>
It's a bingo game. The card gets filled with random Numbers. But I can't get it like this:
column column column column column column
row 10 13 16 14 16 19
row 24 26 28 29 23 21
row 36 33 39 30 31 35
row 46 48 42 45 43 47
row 59 56 51 52 58 50
row 60 65 68 62 61 67
So I would like to have the first row with numbers from 10 to 19
the second row from 20 to 29 and so on.
I tried like this
<?php drawnNumber(): $number = mt_rand(0,9);
fillArray(): $number = $row . $number; ?>
But that doesn't work, because there are double numbers in the card.
So before that I tried it in a different way,with in_array:
<?php
function fillCard(){
$card = array();
/* fill card with random numbers */
for($i = 0, $min = 10, $max = 19; $i < 6; $i++, $min+=10, $max += 10)
{
for($j = 0; $j < 6; $j++)
{
$number = mt_rand($min,$max) ;
if(!in_array($number, $card){
$card['row' . $i]['column' . $j]['number'] = $number;
$card['row' . $i]['column' . $j]['found'] = 0;
}
}
}
var_dump($card);
return $card;
} ?>
But there are still double random numbers in the card.
I tried a few other thinks in the last two weeks, but I just can't get it to work together.
If one thing succeeds the other thing fails.
I can get the random numbers but not unique random numbers in the card.
I hope someone can help me.
(for extra information: it's a bingo game. So drawnNumber() are the "balls", which get drawn
and stored in the array $drawnNumbers, they also are unique random numbers. fillCard() is the
function that fills the bingo card and checks if $drawNumber is in $card)
I would appreciate some help, if someone can tell me how to get it to work. Maybe in
an algorithm way or else some code?
Thank you in advance.
In general, you draw from some kind of box, right? So do the same, have an array with all available numbers and once you get a random number out of it, remove it, so the next time you search for a number, you will only pick from the remaining ones. Small example:
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
we pick a random number between 0 and 3 inclusive (0 and the length - 1 of a that is). Let's say we picked index 2, then a will look like:
a[0] = 1
a[1] = 2
a[2] = 4
Now if you draw a number between 0 and 2 (note that you take the length - 1 of a!), you won't re-pick the already chosen number in any way thus giving you unique numbers ALL the time.
P.S. this is a simplified version, but now if you can apply that to yourself and, for your example, create several arrays you will pick from.
The simplest way would be to have an additional flat array to keep track, and loop mt_rand
Here's an example of the meat of things:
$drawn = array();
// Loop around until you have a new number in the desired range
do {
$number = mt_rand($min,$max);
} while(in_array($number, $drawn));
// And save it to the $drawn array
$drawn[] = $rand;
To clarify, the above snippet (without the initialization of $drawn) is meant to replace the line
$number = mt_rand($min,$max) ;
in your code.
define('NB_ROWS', 6);
define('NB_COLS', 6);
$rows = range(10, NB_ROWS * 10, 10);
$bingo = array();
foreach($rows as $rowIndex)
{
$availNumbers = range(0, 9);
$line = array();
for($cellIndex = 0; $cellIndex < NB_COLS; $cellIndex++)
{
// pick a random value among remaining ones for current line
$numIndex = rand(0, count($availNumbers)-1);
list($value) = array_splice($availNumbers, $numIndex, 1);
$line[] = $value + $rowIndex;
}
$bingo[] = $line;
}
print_r($bingo);