PHP - create a spiral - php

assuming I have $rows = 4, and $cols = 4;, How do I create a array with 16 elements in a spiral order, like:
1, 2, 3, 4,
12,13,14,5,
11,16,15,6,
10,9, 8, 7,

My solution is quite verbose, because the intent is for you to examine it and learn how it works.
<?php
/**
* Creates a 2D array with the given dimensions,
* whose elements are numbers in increasing order
* rendered in a 'spiral' pattern.
*/
function createSpiral($w, $h) {
if ($w <= 0 || $h <= 0) return FALSE;
$ar = Array();
$used = Array();
// Establish grid
for ($j = 0; $j < $h; $j++) {
$ar[$j] = Array();
for ($i = 0; $i < $w; $i++)
$ar[$j][$i] = '-';
}
// Establish 'used' grid that's one bigger in each dimension
for ($j = -1; $j <= $h; $j++) {
$used[$j] = Array();
for ($i = -1; $i <= $w; $i++) {
if ($i == -1 || $i == $w || $j == -1 || $j == $h)
$used[$j][$i] = true;
else
$used[$j][$i] = false;
}
}
// Fill grid with spiral
$n = 0;
$i = 0;
$j = 0;
$direction = 0; // 0 - left, 1 - down, 2 - right, 3 - up
while (true) {
$ar[$j][$i] = $n++;
$used[$j][$i] = true;
// Advance
switch ($direction) {
case 0:
$i++; // go right
if ($used[$j][$i]) { // got to RHS
$i--; $j++; // go back left, then down
$direction = 1;
}
break;
case 1:
$j++; // go down
if ($used[$j][$i]) { // got to bottom
$j--; $i--; // go back up, then left
$direction = 2;
}
break;
case 2:
$i--; // go left
if ($used[$j][$i]) { // got to LHS
$i++; $j--; // go back left, then up
$direction = 3;
}
break;
case 3:
$j--; // go up
if ($used[$j][$i]) { // got to top
$j++; $i++; // go back down, then right
$direction = 0;
}
break;
}
// if the new position is in use, we're done!
if ($used[$j][$i])
return $ar;
}
}
/**
* Assumes the input is a 2D array.
*/
function print2DGrid($array) {
foreach ($array as $row) {
foreach ($row as $col) {
print sprintf("% 2d ", $col);
}
print "\n";
}
}
$width = 12;
$height = 8;
print2DGrid(createSpiral($width, $height));
?>
Here it is in action, giving the following output:
0 1 2 3 4 5 6 7 8 9 10 11
35 36 37 38 39 40 41 42 43 44 45 12
34 63 64 65 66 67 68 69 70 71 46 13
33 62 83 84 85 86 87 88 89 72 47 14
32 61 82 95 94 93 92 91 90 73 48 15
31 60 81 80 79 78 77 76 75 74 49 16
30 59 58 57 56 55 54 53 52 51 50 17
29 28 27 26 25 24 23 22 21 20 19 18
Hope this helps.

Use a vector and boolean values. Iterate on the X axis to start with until you reach the end of the row. Set the value to true for each position as you traverse it. Anytime you reach a border or boolean true, turn right.
So on the first row your "vector" would change the X increment to zero and the Y increment to 1. Then you would check the value of the increment at each turn and accommodate the 4 scenarios.
This is the first way I thought of.
The second way would not use booleans, but would instead simply keep track of how many columns or rows are left in front of the cursor on its path, decreasing the X or Y remaining on each turn. The rest of the logic would remain the same.
When you reach the center, you will hit a loop where there are no more iterations possible. You can catch this by verifying the number of possibilities around the square, or simply counting down from N number of total squares from where you began, and stopping when the number hits zero.
You can use the above method for a square of any size.

The algorith is not very dificult. You have to iterate from 1 up to $rows*$cols. During the iteration, you have to calculate the position of the current number in the matrix ($x,$y). The first on will be in (1,1) of course. The following one will be ($x+1,$y) because you are heading east. So, the position of each number is base on the position of the previous one and the currect direction.
The tricky part of the algorith is to calculate the direction. But if you look at it, you will see that the direction changes clockwise each time you bump into a used cell, or you get out of bounds.
All in all, this is going to be ~30 lines of PHP code.
I hope this helps.
There is a similar challenge in PHP golf: http://www.phpgolf.org/challenge/Spiral. Somebody solved it in only 130 characters!

Related

Get dimensions of 2D array with respect to size of 1D array

I have one dimensional array like this
$arr=array('1','2','3','4','5','6','7','8','9','10','11','12','13'......'21');
From this i want to create a two dimensional array look like this.
The dimension of the 2D array is depends on the number of elements in the 1D array.
Conditions
1.The number of rows of 2D array is fixed as 5.
2.The number of columns may vary.
3.Third row will be empty except for the last element
NOTE
Size of the one dimensional array is varied.
We need to get the dimension of 2D array also how can i print it?
UPDATE
Here is my code
$k=0;
$l=0;
$i=0;
$A=array('1','2','3','4','5','6','7','8','9','10');
//size of 1D array
$size=count($arr);
//2D array
$B=[];
$x=?;//no of columns of 2d array
$y=5;//no of rows of 2d array
for($i=0;$i<$size;$i++){
$B[k][l]=$A[i];
$k++;
if($k==2 && $l!=$x){
$k++;
}
if($k==4){
$l++;
}
}
How can i get the value of $x it is columns size of 2D array
Try this: (Edit: Array solution also added below)
Instead of the 1D array I have used a loop which emulates an array.
Assumptions : if there is any item in the last column (e.g sample input 10) the second last column will have a * regardless of the items reaching upto row 3 or not.
$itemCount = 49;
$residual = $itemCount % 4;
$starCount = ceil($itemCount/4);
if ($residual > 1) {
$starCount -= 1;
} else if ($residual) {
$starCount -= 2;
}
$itemsArray = [];
$key = 0;
for ($i = 1 ; $i <= $itemCount ; $i++ ) {
$key = $key % 5 ; // fixing the offset and row number
if ($key == 2 && $starCount) {
$itemsArray[$key][] = '*';
$starCount--;
$key++;
$itemsArray[$key][] = $i;
} else {
$itemsArray[$key][] = $i;
}
$key++;
}
print_r($itemsArray); //check output
Tested for 21, 49, 50, 51. Will show Stars 1 less than number of columns as mentioned in assumption.( it can be changed if you want by changing the residual check count)
Note: I am leaving the printing part as that is upto you(you want to print it on the command line or on a web page). Moreover its just a matter of looping over the result.
And for the array version of this code you can put
$itemCount = count($yourArray);
replace the for loop with foreach ($yourArray as $i) (or change $i with something meaningful all over.)
Output
21
1 5 9 13 17
2 6 10 14 18
* * * * 19
3 7 11 15 20
4 8 12 16 21
49
1 5 9 13 17 21 25 29 33 37 41 45
2 6 10 14 18 22 26 30 34 38 42 46
* * * * * * * * * * * 47
3 7 11 15 19 23 27 31 35 39 43 48
4 8 12 16 20 24 28 32 36 40 44 49
50
1 5 9 13 17 21 25 29 33 37 41 45 49
2 6 10 14 18 22 26 30 34 38 42 46 50
* * * * * * * * * * * *
3 7 11 15 19 23 27 31 35 39 43 47
4 8 12 16 20 24 28 32 36 40 44 48
51
1 5 9 13 17 21 25 29 33 37 41 45 49
2 6 10 14 18 22 26 30 34 38 42 46 50
* * * * * * * * * * * * 51
3 7 11 15 19 23 27 31 35 39 43 47
4 8 12 16 20 24 28 32 36 40 44 48
Array based solution for reference:
<?php
$itemCount = 21;
$array = range(1,$itemCount);// the 1D array
$residual = $itemCount % 4;
$starCount = ceil($itemCount/4);
if ($residual > 1) {
$starCount -= 1;
} else if ($residual) {
$starCount -= 2;
}
$itemsArray = [];
$key = 0;
foreach ($array as $i) {
$key = $key % 5 ; // fixing the offset and row number
if ($key == 2 && $starCount) {
$itemsArray[$key][] = '*';
$starCount--;
$key++;
$itemsArray[$key][] = $i;
} else {
$itemsArray[$key][] = $i;
}
$key++;
}
print_r($itemsArray); //check output
You can use the following method to generate the desired grid structure. This method is applicable to any arbitrary sized array.
Updated code:
$arr = array('1','2','3','4','5','6','7','8','9','10', '11');
$arrLength = count($arr);
$columns = ceil($arrLength / 4);
$rows = ($columns == 1) ? (($arrLength > 2) ? $arrLength + 1 : $arrLength) : 5;
echo "Grid dimension: " . $rows . " x " . $columns . "<br />";
$output_array = array();
$index = 0;
for($i = 0; $i < $columns; ++$i){
for($j = 0; $j < $rows; ++$j){
if($j == 2 && ($i != $columns - 1 || $columns == 1)){
$output_array[$j][$i] = "*";
continue;
}
$output_array[$j][$i] = isset($arr[$index]) ? $arr[$index] : "";
++$index;
}
}
// display $output_array
for($i = 0; $i < $rows; ++$i){
for($j = 0; $j < $columns; ++$j){
echo $output_array[$i][$j] . " ";
}
echo "<br />";
}
Output:
Grid dimension: 5 x 3
1 5 9
2 6 10
* * 11
3 7
4 8
This works for any array of any length:
<?php
$arr=array('1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21');
$len=count($arr);
$key=0;
$rows=5;
$cols=floor(($len - 2)/4) + 1;
for ($j=0;$j<$cols;$j++)
for ($i=0;$i<$rows;$i++)
if ($i==2 && $j!=$cols-1) {
$arr2D[$i][$j] = '*';
} else {
$arr2D[$i][$j] = $key>=$len ? "" : $arr[$key++];
}
//print array
echo "<table>";
for ($i=0;$i<$rows;$i++){
echo "<tr>";
for ($j=0;$j<$cols;$j++)
echo "<td width='30px'>" . $arr2D[$i][$j] . "</td>";
echo "</tr>";
}
echo "</table>";
?>
output:
1 5 9 13 17
2 6 10 14 18
* * * * 19
3 7 11 15 20
4 8 12 16 21

fill 2 dimensional array random in a bingo way

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);

PHP read in every 10 out of 100 lines

I'm trying to read in a text file of 5000 lines. However I only want to get the first 10 lines of every 100 lines. So line 1-10, line 101 - 110, line 200 - 210 etc. I just cant figure out the logic to use.
$count = count(file($text))
for ($i = 0; $i < $count; $i++)
{
$test = fgets($f_text)
}
Use % 100 and only print lines where $n % 100 > 0 && $n % 100 <= 10.
$lines = file($text);
foreach ($lines as $n => $line) {
if ($n % 100 > 0 && $n % 100 <= 10) {
echo $line; // or whatever
}
}
A sample of how the logic works:
foreach (range(0,250) as $n => $line) {
if ($n % 100 >= 1 && $n % 100 <= 10 ) {
echo $i . "\n";
}
}
1 2 3 4 5 6 7 8 9 10 101 102 103 104 105 106 107 108 109 110 201 202 203 204 205 206 207 208 209 210
You can easily do this with an SPLFileObject, as long as you know how many lines are in the file:
$file = new SplFileObject( $filename, "r");
$lines = 0;
while ( !$file->eof()) {
$file->fgets();
$lines++;
}
Now you know there are $lines number of lines in the file. If that is static, say 5000, just initialize $lines to 5000. If it's not, let's round that down to the nearest 100:
$lines -= ($lines % 100);
Now, we just loop over every hundred, seek to the grouping of 10, and grab the data we want:
$data = array();
for( $i = 0; $i < $lines; $i += 100) {
$file->seek( $i);
for( $j = 0; $j < 10; $j++) {
$data[] = $file->fgets();
}
}
With this solution, you never load the whole file into memory, which will save resources when dealing with large files. Also, if you know the size of the file beforehand, you will only read the lines of data that you require.
Based on both other answers, probably the most memory-efficient way to do it:
foreach(new SplFileObject($filename) as $n => $line)
if($n % 100 < 10)
echo $line;

Create a 3D array from a list

I have the following list:
1945/01/05 BA 87 34 1 59 50
1945/01/05 CA 18 17 45 49 82
1945/01/13 BA 6 66 1 16 48
1945/01/13 CA 40 60 32 50 80
and so on....
I want to arrange them as an array like the following:
$array['BA'][0][0] = 87;
$array['BA'][0][1] = 34;
$array['BA'][0][2] = 1;
$array['BA'][0][3] = 59;
$array['BA'][0][4] = 50;
$array['CA'][0][0] = 18;
$array['CA'][0][1] = 17;
$array['CA'][0][2] = 45;
$array['CA'][0][3] = 49;
$array['CA'][0][4] = 82;
$array['BA'][1][0] = 6;
$array['BA'][1][1] = 66;
$array['BA'][1][2] = 1;
$array['BA'][1][3] = 16;
$array['BA'][1][4] = 48;
$array['CA'][1][0] = 48;
$array['CA'][1][1] = 60;
$array['CA'][1][2] = 32;
$array['CA'][1][3] = 50;
$array['CA'][1][4] = 80;
I know that I can use preg_split('/\s+/', $list); to create an array, but how can I create a 3D Array from this list?
Thank you for your help
Try something like this:
Updated the code so you can also load a file since you have memory issues.
Consider: If loading the amount of data already creates memory issues your end result, the array, might also generate memory issues! Since that's even more data!
So this will read per line so it does not load the total file in memory directly. But since you have memory issues you should likely not write to an array but to a database for example. Otherwise the memory issue will keep coming.
<?PHP
/*
* Example when small amount of data is in a string
*/
/*
//your input
$str='1945/01/05 BA 87 34 1 59 50
1945/01/05 CA 18 17 45 49 82
1945/01/13 BA 6 66 1 16 48
1945/01/13 CA 40 60 32 50 80';
//we work per line
$lines=explode("\n", $str);
//loop each line
foreach($lines AS $line) {
*/
/*
* Example when big amount of data is in a file
*/
//this will contain the end result
$result=array();
$filename='lines.txt'; //contains the data like in your question
$fp = fopen($filename,'r');
while($line=fgets($fp)) {
//explode each line on space so we get the different fields
$fields=explode(' ', $line);
//we remove the date, not needed
unset($fields[0]);
//we get the key (BA/CA/etc) and remove it also
$key=$fields[1];
unset($fields[1]);
//we write the result to the array
//using array_values so the indexes are from 0-4 again
//because we removed items
$result[$key][]=array_values($fields);
}
fclose($fp);
//show the result in html
echo '<pre>';
print_r($result);
THE QUESTION CHANGED
This does answer the question asked in the comments, not the actual question.
<?PHP
/*
* Example when big amount of data is in a file
*/
//this will contain the end result
$result=array();
$filename='lines.txt'; //contains the data like in your question
$fp = fopen($filename,'r');
while($line=fgets($fp)) {
//explode each line on space so we get the different fields
$fields=explode(' ', $line);
//we remove the date, not needed
unset($fields[0]);
//we get the key (BA/CA/etc) and remove it also
$key=$fields[1];
unset($fields[1]);
//We start counting the numbers
foreach($fields AS $nr) {
$nr=trim($nr);
if(empty($result[$key][$nr])) {
$result[$key][$nr]=1;
}else{
$result[$key][$nr]++;
}
}
}
fclose($fp);
//show the result in html
echo '<pre>';
print_r($result);
<?php
$a = '1945/01/05 BA 87 34 1 59 50
1945/01/05 CA 18 17 45 49 82
1945/01/13 BA 6 66 1 16 48
1945/01/13 CA 40 60 32 50 80';
$array = array();
$list = explode("\n", $a);
echo '<pre>';
print_r($list);
echo '</pre>';
$first_index = 0;
$counter = 0;
foreach($list as $key=>$value) {
if($counter >=2) {
$first_index++;
$counter= 0;
}
$row = explode(" ",$value);
$k = $row[1];
for($i=2; $i< count($row); $i++) {
$array[$k][$first_index][] = $row[$i];
}
$counter++;
}
echo '<pre>';
print_r($array);
echo '</pre>';
?>

Generate Distinct Combinations PHP [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
I need an efficient algorithm to generate distinct combinations(not allowed to repeat). Each combination has 5 distint numbers(different numbers) that ranges between 1 and 99. The result must be stored in an array. If possible I would like the numbers and range allowed to be customized. The order of number doesn't matter(01 02 03 = 03 01 02)
Ex.:
01 02 03 04 05
02 03 04 05 06
...
Does anyone could help me to build it? I would like to pick up some random combinations from the array. Nowdays I am generating random combinations using mt_rand but It takes too much time, SO SLOW! I believe happens to repeat so often then takes time to generate new one and new one...
I threw this together quickly, seems to work.
<?php
$range_low = 1;
$range_hi = 99;
$num_sets = 10;
$set = generateSet($range_low, $range_hi, $num_sets);
print_set($set);
function generateSet($range_low, $range_hi, $numSets = 5, $numPerSet = 5)
{
$return = array();
$numbers = array();
for($i = $range_low; $i <= $range_hi; ++$i) {
$numbers[] = $i;
}
for ($s = 0; $s < $numSets; ++$s) {
$set = array_values($numbers);
shuffle($set);
$return[$s] = array();
for ($i = 0; $i < $numPerSet; ++$i) {
$val = array_shift($set);
$return[$s][] = $val;
}
}
return $return;
}
function print_set($set)
{
foreach($set as $subset) {
foreach($subset as $value) {
echo str_pad($value, 2, '0', STR_PAD_LEFT) . ' ';
}
echo "\n";
}
}
Sample output:
90 75 89 43 57
24 54 38 35 10
77 21 55 33 83
37 15 61 09 44
25 31 85 17 20
48 37 45 13 20
82 70 74 64 72
07 24 33 64 45
34 13 39 33 05
13 77 87 70 64
To Fisher-Yates shuffle the array, see this comment on shuffle for a function you could use in place of shuffle.
Hope that helps.
If you really absolutely need a full set of every possible combination:
function combinations($set,$length) {
$combinations = array();
$setCount = count($set);
for($i = 0, $n = $setCount; $i <= ($n - $length); $i++) {
$combination = array();
$combination[] = $set[$i];
if($length > 1) {
$combination = array_merge(
$combination,
combinations(array_slice($set,1+$i), $length-1)
);
}
$combinations[] = $combination;
}
return $combinations;
}
$allYourNumbers = range(1,99);
$allCombinations = combinations($allYourNumbers, 5);
Then you can shuffle $allCombinations and extract as many as you want, but you'll need a lot of memory and a lot of time... doing this can never be efficient
Here's the simple code that should run rather fast and do what you describe.
$numbers = range(1, 99); // numbers to pick from
$length = 5; // amount of items in the set
$sets_amount = 15; // amount of sets you want to generate
shuffle($numbers); // randomize
function get_set($length, &$numbers) {
return array_splice($numbers, 0, $length);
}
for ($i = 0; $i < $sets_amount; $i++)
print_r(get_set($length, $numbers));
Note: it only works when you need a few combinations. You don't state that you want all of the possible ones, so I thought if you need just a bunch of them - here's very quick and easy way to do it.
For a bit slower (the more you generate - the slower it goes), but that generates any amount of sets, you can use this code.
$numbers = range(1, 99); // numbers to pick from
$length = 5; // amount of items in the set
$sets_amount = 200; // amount of sets you want to generate
$existing = array(); // where we store existing sets
$shuffle_period = count($numbers) - $length - 1; // how often we re-randomize the elements
$j = 0;
for ($i = 0; $i < $sets_amount; $i++, $j++) {
if (!($i % $shuffle_period)) {
shuffle($numbers); // randomize at first go and on $shuffle_period
$j = 0;
}
do {
$arr = array_slice($numbers, $j, $length);
} while (in_array($arr, $existing));
$existing[] = $arr;
}
print_r($existing);

Categories