Auto generated gallery with PHP - php

I have a minimum of 2 images and a maximum of 8 images per page. Depending on the number of images I have it's going to generate a gallery differently.
Here is a diagram of how it should work
8 - 4x4
7 - 4x3
6 - 3x3
5 - 3x2
4 - 4
3 - 3
2 - 2
The first column has the number of total images. The following numbers are images in a row.
Example
If I have 7 images, it should return an array like:
array(4,3)
The final result is that I will loop out 4 images on the first row and then 3 larger images on the second row.
If statements
I could do this with if statements but I guess there could be a way to calculate this?
Solution for everyone
If this is going to be useful for more people then it would need a function like this:
function getGalleryRows($count, $row_limit) {
}
where $count is the number of images and $row_limit is the maximum number of images there can be in a row.

I don't know if I get you right but is this what you want?
function getGalleryRows($count, $row_limit) {
$arr = array();
while ($count > 0){
array_push($arr, (($count > $row_limit) ? $row_limit : $count));
$count -= $row_limit;
}
return $arr;
}
Examples
echo json_encode(getGalleryRows(7,2)); // returns [2,2,2,1]
echo json_encode(getGalleryRows(7,4)); // returns [4,3]

Related

Backtracking algorithm to generate a sudoku grid does not complete

I wrote a sudoku generator that creates numbers cell by cell and checks immediately after a cell has been created if it is valid (horizontally, vertically and in a 3x3 block).
Now my problem is that the algorithm always gets stuck at some point, as it won't find a valid number for the current cell. Sometimes closer to the end, sometimes already after writing 30 cells.
This is my function to check the cell, which should change the number depending on its validity:
private function checkCell($index)
{
while ($this->isValid($index) === false) {
$this->cell[$index]->setValue(rand(1, 9));
$this->counter++;
echo 'counter: ' . $this->counter;
echo PHP_EOL;
if ($this->counter > 1000) {
$this->display();
die();
}
}
}
isValid() checks if the cell is valid horizontally, vertically and in a block (this is currently not working, it just returns true).
The counter is for debugging purposes so I can see when it gets stuck.
Here is the function generating my cells:
private function fillCell($index)
{
$rand = rand(1, 9);
$this->cell[$index]->setValue($rand);
$this->checkCell($index);
}
What should be changed so the algorithm doesn't get stuck all the time?
The issue might be that the algorithm is a little too random. You end up creating a grid that is invalid and can not be completed further.
I would propose starting from a known valid grid and shuffle the cells randomly. If a cell can't be moved, we can simply skip it.
A fair warning to the reader, the following will contain pseudo code rather than working code.
A perfectly valid starting grid:
1 2 3 | 4 5 6 | 7 8 9
7 8 9 | 1 2 3 | 4 5 6
4 5 6 | 7 8 9 | 1 2 3
------|-------|------
9 1 2 | 3 4 5 | 6 7 8
6 7 8 | 9 1 2 | 3 4 5
3 4 5 | 6 7 8 | 9 1 2
------|-------|------
8 9 1 | 2 3 4 | 5 6 7
5 6 7 | 8 9 1 | 2 3 4
2 3 4 | 5 6 7 | 8 9 1
We can store this in a single dimension array, as you already appear to do.
We follow a simple logic:
We create a single dimension array containing the cells
$cells = array(
1,2,3,4,5,6,7,8,9,7,8,9,1,2,3,4,5,6,4,5,6,7,8,9,1,2,3,
9,1,2,3,4,5,6,7,8,6,7,8,9,1,2,3,4,5,3,4,5,6,7,8,9,1,2,
8,9,1,2,3,4,5,6,7,5,6,7,8,9,1,2,3,4,2,3,4,5,6,7,8,9,1,
);
We create another array containing numbers from 0 to 80 in a random order, which are the indexes of $cells
$indexes = range(0, 80);
shuffle($indexes);
We iterate over $indexes and use the value to select a random $cell in $cells
foreach($indexes as $index) {
$cell = $cells[$index];
}
For each $cell, we iterate over $cells. In each iteration, we create a temporary grid where we switch the value of the current cell with the value of the target cell. If the temporary grid is valid, we save the target index in an array of candidates
// pseudo code because that's a lot of work
$candidates = getCandidates($cell, $cells);
We randomly choose one of the candidates and switch the cells. If no candidate is available, we simply ignore this step
candidatesCount = count(candidates);
if(candidatesCount > 0) {
$candidate = $candidates[range(0, candidatesCount -1)];
// switch values
$cells[$index] = $cells[$candidate];
$cells[candidate] = $cell;
}
Repeat until $cells is processed
There are likely more efficient ways to proceed, but this logic can not get stuck.
Note that there is a low probability that the shuffle will undo itself and produce the original grid. But it's still a valid grid.
You never want to make a backtracking algorithm that uses random numbers. It can end up running infinitely.
What you want to do is:
Find the first empty cell
Try all possible values from 1 to 9 in this cell. When all values are tried, go back.
For every value you try in the cell (at step 2), recursively call the backtracking algorithm. (go back to step 1)
If the function is called and there are no empty cells, evaluate the board. If everything is ok, you found the solution! If it's not ok, go back.
The evaluation means, check that you have all numbers from 1 to 9 exactly once on every line, every column, and every 3x3 square.
Example of how it might look like:
function back($pos) {
if ($pos >= 9*9) {
if (evaluate()) {
// we found a solution
// do soemthing with it
} else {
return;
}
}
$x = pos / 9;
$y = pos % 9;
if ($m[x][y] != 0) {
// we already have a value assigned for this position
back($pos+1);
return;
}
for ($v = 1; $v <= 9; $v++) {
$m[x][y] = $v;
back($pos+1);
}
$m[x][y] = 0; // clean up tested value before going back
}
back(1)
The above algorithm can be optimized by evaluating lines/columns at every step, instead of just once at the end. If the algorithm tries to place number x, but x is already found on the line/column, then we can just move on to try x+1 since we know x will create an invalid solution.

PHP radom array values from another array

///PROBLEM SOLVED. SEE MY ANSWER BELLOW.///
I have an array that randomly generates TRUE values throughout it. I
specified the number of TRUE values i want and it works like a charm.
I was toying around with it`s values and generating certain actions
based on wether or not the value is TRUE. Problem is i need to do this
again, this time using the TRUE values to form an array from which i
need a certain number of TRUE values.
I.E. I wanted 3 TRUE values out of 5. The code gave me the 3 TRUE
values on random iterations:
[1]=>1; [1]=>;
[2]=>1; [2]=>1;
[3]=>; [3]=>1;
[4]=>; [4]=>;
[5]=>1; [5]=>1;
This is all fine and dandy. Now i need TRUE values on 2 out of the 3
previous values. Consider i am taking 5 bites out of an apple. On 3
occasions i choke on it, from which in 2 cases i lose 2,3 teeth.
I want to print the outcome.
"Took a bite"
"Took a bite and choked"
"Took a bite"
"Took a bite , choked , lost 1 tooth and got 9 left" (i have a total of 10 teeth)
"Took a bite , choked , lost 2 teeth and got 7 left"
This is what i need to see printed after the 3/5 and 2/3 random
calculations occured. If i took a bite on the [2],[4] and [5]
iterations, i must lose tooth 2 times randomly on those exact
iterations.
Sorry for this example.
///PROBLEM SOLVED. SEE MY ANSWER BELLOW.///
2) Lastly, how can i store what happened after the code ran? Like "After you ate that apple, you choked 3 times lost 3 teeth and still got 7 left".
The way i did it does not work and returns "me loosing 1 tooth and having 9 left despite the fact that my calculated value is 7", not stacking the values of my ordeal.
I know these questions are silly, but i searched everywhere for information, read manuals and stuff and cannot put the pieces together...
The method used in the " random 3/5" case:
$spots = array();
while (count($spots) < number) {
$rand = rand(1,36);
if (!isset($spots[$rand])) {
$spots[$rand] = TRUE;
}
}
$spots = $spots + array_fill(1, number, FALSE);
ksort($spots); /// credits to Nick J.
Then did a foreach($spots as $k => $v)
Then did my code statements, starting with if ($v == 1), do code...
And gave me a random 3/5 list like the one i posted first.
Thank you in advance,
Vlad
Ok, i edited my answer as i have found the solution to problem number 1.
The catch was to scan the array of elements that are FALSE, unset them, work with that further on as it will randomly select elements from the TRUE elements of the original array.
Hope this comes in handy. I will post the code here:
$spots = array();
while (count($spots) < number) {
$rand = rand(1,constant);
if (!isset($spots[$rand])) {
$spots[$rand] = TRUE;
}
}
$spots = $spots + array_fill(1, constant, FALSE);
ksort($spots);
print_r($spots) ;
foreach($spots as $v)
{
if($v != 1)
{
unset($spots[array_search($v,$spots)]);
}
}
print_r($spots);
If example array:
$spots = array("1","2","3","4","5");
And i want 3 random numbers to be picked (TRUE = 1 , FALSE = nothing)
then the output is:
[1]=>1;
[2]=>1;
[3]=>;
[4]=>;
[5]=>1;
If i want 2 random numbers out of the 3 just got, then the final output is:
[1]=>1;
[2]=>1;
[5]=>1;

Controlling likelyhood of randomly generated numbers

If I wanted a random number between one and three I could do $n = mt_rand(1,3).
There is a 33% chance that $n = 1, a 33% chance it's 2, and a 33% chance that it's 3.
What if I want to make it more difficult to get a 3 than a 1?
Say I want a 50% chance that a 1 is drawn, a 30% chance that a 2 is drawn and a 20% chance that a 3 is drawn?
I need a scalable solution as the possible range will vary between 1-3 and 1-100, but in general I'd like the lower numbers to be drawn more often than the higher ones.
How can I accomplish this?
There is a simple explanation of how you can use standard uniform random variable to produce random variable with a distribution similar to the one you want:
https://math.stackexchange.com/a/241543
This is maths.
In your example the just chose a random number between 0 and 99.
Values returned between 0 to 49 - call it 1
Values returned between 50 - 69 - Call it 2
Values returned between 70 - 99 - Call it 3
Simple if statement will do this or populate an array for the distribution required
Assuming a 1 - 10 scale, you can use a simple if statement and have the numbers represent percentages. And just have each if statement set $n to a specific. Only downfall, it isn't universal.
$dummy = mt_rand(1,10);
// represents 50%
if ($dummy <= 5) {
$n = 1;
}
// represents 40%
if ($dummy >= 6 && $dummy <= 9) {
$n = 2;
} else {
// represents 10%
$n = 3;
}

Lowest cost based on case sizes

I'm working on a shipping module for wine, and was wondering if anyone could give me a hand - basically:
The wine can be shipped in cases of 8, 12 or 15 bottles, each with its own price. The module needs to take the total number of bottles in the order, and work out which combination of cases gives the lowest price. Eg in an order of 31 bottles, the lowest price works out to 1 case of 15 and two cases of 8, (rather than 2 cases of 15 and 1 of 8, or 2 of 12 and one of 8). Currently, I have the following, which almost works, but misses a few possible combinations
foreach ($rates as $case_size => $case_price)
{
$price = floor($total_bottles / $case_size) * $case_price;
$rem = $total_bottles % $case_size;
if($rem > 12)
{
//needs to use another case of 15
$price = $price + $rates[15];
}
elseif($rem > 8)
{
//needs an extra case of 12
$price = $price + $rates[12];
}
elseif($rem > 0)
{
//needs an extra case of 8
$price = $price + $rates[8];
}
$quotes[] = $price;
}
return min($quotes);
From your post your saying that the most price-effective system wouldn't just the one that has uses the lowest cost per bottle of the container, but also needs to be the most efficient at filling the containers. However your algorithm is only looking at would use the fewest large boxes possible. You need an algorithm that will completely fill each case possible.
I would do something like this: Use a recursive program to find the combination that would most completely fill each case.
function fit_case($number, $case_size) {
$rem = $number % $case_size;
$next_size=magic_voodo0();
if($rem==0) { //if perfectly fills it you're done
return ($number/$case_size)*$rates[$case_size];
} else if(($rem % $next_size)/$next_size>.5) {
//if over 50% fills the next case add the next smaller case
return floor($number/$case_size)*$rates[$case_size]+fit_case($rem, $next_size);
} else { //otherwise back off 1 of the biggest cases, and fill the rest
return (floor($number/$case_size)-1)*$rates[$case_size]+fit_case($rem, $next_size);
Hope this helps.
Different approach. Use a lookup table that has all combinations of boxes for a specific number of bottles.
1 bottle - 8
...
31 bottle - 15-8-8,15-15-8,8-8-8-8, and so on
and so on
Use another lookup table for the different rates per box per country
In your function
get table row for country prices
get the different combinations for the number of bottles
do a foreach loop on combinations
save the price and combination of first loop to variables
compare the price of the next loop with the saved value
if it is lower, save price and combination/if not, continue
loop through all combinations
return lowest price/box combination

PHP round up list items to nearest five

I've got a grid of 10 square list items. A bit like a gallery. If the user adds another item there will be 11. However this will look strange as the 11th item will be on its own in a new row. How can I use PHP to round up to the nearest 5 and add in the some blank/dummy list items?
You could use the modulo operator to identify the remainder of a division:
10 % 5 = 0
11 % 5 = 1
12 % 5 = 2
13 % 5 = 3
14 % 5 = 4
15 % 5 = 0
With that you can identify if (and how large) such an uncomplete row would be. Knowing how many elements are in that last uncomplete row oviously allows you to calculate the number of remaining cells to fill the row.
($y+(($y%$x)?($x-($y%$x)):0))
...where $y is the number of items(e.g. 11) and $x is the number of items in a row(e.g. 5)

Categories