I need to write a function who prints Look-and-Say Sequence hat takes a number as nth iteration.
The first iteration is 0 and output has to contain every previous lines. For example, if we test sequence(3), the result need to be :
1
11
21
1211
SO I made a function for each line and another to stack them but it doesn't work =/
Here's my code
<?php
function Conway($n)
{
if ($n == 1)
return "1";
if ($n == 2)
return "11";
for ($i = 3; $i <= $n; $i++) {
$str = "11";
$str = $str.'$';
$len = strlen($str);
$cnt = 1;
$tmp = "";
for ($j = 1; $j < $len; $j++)
{
if ($str[$j] != $str[$j - 1])
{
$tmp = $tmp.$cnt + 0;
$tmp = $tmp. $str[$j - 1];
$cnt = 1;
}
else $cnt++;
}
$str = $tmp;
}
return $str;
}
function sequence($nth) {
for ($i = 1; $i <= $nth+1; $i++)
do {
echo Conway($i)."\n";
} while ($i > $nth+1);
}
As explained here en.wikipedia.org/wiki/Look-and-say_sequence, the idea is to construct a string based on the previous constructed string (in a loop):
To generate a member of the sequence from the previous member, read
off the digits of the previous member, counting the number of digits
in groups of the same digit. For example:
1 is read off as "one 1" or 11.
11 is read off as "two 1s" or 21.
21 is read off as "one 2, one 1" or 1211.
1211 is read off as "one 1, one 2, two 1s" or 111221.
111221 is read off as "three 1s, two 2s, one 1" or 312211.
An attempt to solve this problem, we'll have a function called lookAndSay($n) that accepts a number $n. Basically, that function will have a loop that runs $n times.
To simplify things, we'll have a another function, let's call it tokenize($str), that will basically count the occurrence of a number, concatenates that count to the number (like 31 where 3 is the number of occurrences of the number 1), repeat until all the numbers in the string $str are dealt with.
A usage of that function can result in (for example):
tokenize('11'); // returns: '21'
Our main function, lookAndSay($n), will call tokenize($str) after making some initialization and in the end will return the resulting string.
Here's a code sample illustrating what's being said:
/**
* A function that generate look-and-say sequence based on a number "$n"
*
* #param int $n
* #return string
*/
function lookAndSay(int $n): string {
if ($n <= 1) return '1';
/** "$lastSeq" will hold the latest sequence tokenized by "tokenize" function */
$r = $lastSeq = '1';
/** loop based on "$n" and construct the final string along the way */
while($n-- > 0) $r .= ' '.($lastSeq = tokenize($lastSeq));
/** return the look-and-say string for the number "$n" */
return $r;
}
/**
* A function that "tokenizes" a string of numbers
* and return a string having the format "CN" where C is the number of occurrences of a number N in the string
*
* #param string $str
* #return string
*/
function tokenize(string $str): string {
/** will hold the string of tokens that will be returned after the function finishes */
$r = '';
/** holds the count of a number in the string $str */
$c = 1;
for ($i = 0; $i < strlen($str); $i++) {
/** we still count the number of occurrences of the same number "$str[$i]" ... */
if($str[$i] == ($str[$i + 1] ?? '')) {
/** increment the count and skip the rest of the loop */
$c++;
continue;
}
/** at this point we have the count for "$str[$i]" number so we concatenate the result to the final string and reset the count to "1" to prepare it for the next number (if any) */
($r .= $c.$str[$i]) && ($c = 1);
}
/** return the "tokenized" string */
return $r;
}
A call to lookAndSay could result in:
echo lookAndSay(3); // prints: 1 11 21 1211
Also here's a live demo of the above attempt.
Is that you looking for ?
function sequence($str) {
$str .= '';
$len = strlen($str);
$count= 0;
$result= '';
$temp= $str[0];
for($i=0;$i<$len;$i++) {
if($temp!=$str[$i]) {
$result.=$count.$temp;
$temp = $str[$i];
$count=1;
} else {
$count++;
}
}
$result.=$count.$temp;
return $result;
}
$num = 1;
echo "$num\n";
for($i=0;$i<3;$i++) {
$num=sequence($num);
print $num."\n";
}
output 1 11 21 1211
Related
How would I go about implementing Boyer-Moore Search for streams? I understand how to implement this for a given string where we know the length of the entire string. However, what if we have no idea the size of the string (i.e it's an arbitrary length stream of bytes).
I'm trying to implement this in PHP so any code in PHP would be helpful.
Here's the implementation of Boyer-Moore Search I have in PHP:
function BoyerMooreSearch($haystack, $needle) {
$needleLen = strlen($needle);
$haystackLen = strlen($haystack);
$table = computeSkipTable($needle);
for ($i = $needleLen - 1; $i < $haystackLen;) {
$t = $i;
for ($j = $needleLen - 1; $needle[$j] == $haystack[$i]; $j--, $i--) {
if($j == 0) {
return $i;
}
}
$i = $t;
if(array_key_exists($haystack[$i], $table)) {
$i = $i + max($table[$haystack[$i]], 1);
} else {
$i += $needleLen;
}
}
return false;
}
function computeSkipTable($string) {
$len = strlen($string);
$table = [];
for ($i=0; $i < $len; $i++) {
$table[$string[$i]] = $len - $i - 1;
}
return $table;
}
This works fine if we give it a haystack string like "barfoobazquix" and a needle string like "foo", it will return 3 as expected. However, what if the input haystack were a stream split into 4 byte chunks. The first chunk would be "barf", which would return no match, the second would be "ooba" which also returns no match, and so on...
In this scenario we'd never be able to find the substring "foo" in any of the buffered chunks of the stream with the current implementation. I'm struggling to adapt the current implementation such that it would work even if the search string were split across multiple chunks.
Note that you only ever use the $needleLen most recent characters from the stream. So you can maintain a sliding window consisting of $needleLen characters and advance it as needed. Furthermore, $haystackLen is now unknown and should be replaced with EOF checks.
The code below is inefficient because sliding the window always takes O(n) regardless if we slide it by n characters or just 1. To achieve optimal sliding complexity, the $window should be implemented as a circular character buffer.
// sample call
var_dump(BoyerMooreSearch(STDIN, 'foo'));
function BoyerMooreSearch($resource, $needle) {
$needleLen = strlen($needle);
$table = computeSkipTable($needle);
// build the first window
if (($window = buildWindow($resource, $needleLen)) === false) {
// special case: the text is shorter than the pattern
return false;
}
$i = 0;
while (true) {
// check for a match
$j = $needleLen - 1;
while ($j >= 0 && $needle[$j] == $window[$j]) {
$j--;
}
if ($j < 0) {
return $i;
}
// determine slide distance
$t = $i;
$last = substr($window, -1);
if (array_key_exists($last, $table)) {
$i = $i + max($table[$last], 1);
} else {
$i += $needleLen;
}
// slide the window
if (($window = slideWindow($window, $resource, $i - $t)) === false) {
return false;
}
}
return false;
}
/**
* Initializes the sliding window to length $len.
*
* #return string A string of length $len or false if the $resource contains
* less than $len characters.
*/
function buildWindow ($resource, $len) {
$result = '';
while ($len--) {
$result .= fgetc($resource);
}
return feof($resource) ? false : $result;
}
/**
* Slides $window by $count positions into $resource.
*
* #return string The new window or false on EOF.
*/
function slideWindow(&$window, $resource, $count) {
$window = substr($window, $count) // discard the first $count chars
. fread($resource, $count); // and read $count new chars
return feof($resource) ? false : $window;
}
I'm looking for a function that searchs a word, like "ONE" in a multidimentional array, but I need to find the word in all the 8 ways, example "from left to right, from right to left, up to down, down to up and diagonal upper to diagonal lower"
The array's size may vary (3x3, 1x10, 5x5, 7x2).
I already tried to make this in nested if's, but I don't like that way.
I need to count how many times the word appears in my array, I dont know if exist a php or laravel function that resolve this problem or I must create one
I translate this C++ code into PHP. It works fine and returns the same result as C++ code.
/**
* This function searches in all 8-direction from point
* #param array $grid
* #param int $row in grid[][]
* #param int $col in grid[][]
* #param string $word
* #return bool
*/
function search2D(array $grid, int $row, int $col, string $word): bool {
$x = [-1, -1, -1, 0, 0, 1, 1, 1];
$y = [-1, 0, 1, -1, 1, -1, 0, 1];
// Rows and columns in given grid
$R = count($grid);
$C = count($grid[0]);
// If first character of word doesn't match with
// given starting point in grid.
if ($grid[$row][$col]!= $word[0])
return false;
$len = strlen($word);
for ($dir = 0; $dir < 8; $dir++)
{
// Initialize starting point for current direction
$rd = $row + $x[$dir];
$cd = $col + $y[$dir];
// First character is already checked, match remaining
// characters
for ($k = 1; $k < $len; $k++)
{
// If out of bound break
if ($rd >= $R || $rd < 0 || $cd >= $C || $cd < 0)
break;
// If not matched, break
if ($grid[$rd][$cd] != $word[$k])
break;
// Moving in particular direction
$rd += $x[$dir];
$cd += $y[$dir];
}
// If all character matched, then value of must
// be equal to length of word
if ($k == $len)
return true;
}
return false;
}
/**
* Searches given word in a given matrix in all 8 directions
* #param array $grid
* #param string $word
*/
function patternSearch(array $grid, string $word):void {
// Rows and columns in given grid
$R = count($grid);
$C = count($grid[0]);
// Consider every point as starting point and search
// given word
for ($row = 0; $row < $R; $row++)
for ($col = 0; $col < $C; $col++)
if (search2D($grid, $row, $col, $word))
echo "pattern found at {$row}, {$col}" . "\r\n";
}
$grid = [
['G','E','E','K','S','F','O','R','G','E','E','K','S'],
['G','E','E','K','S','Q','U','I','Z','G','E','E','K'],
['I','D','E','Q','A','P','R','A','C','T','I','C','E'],
];
patternSearch($grid, 'GEEKS');
echo "\r\n";
patternSearch($grid, 'EEE');
Output:
pattern found at 0, 0
pattern found at 0, 8
pattern found at 1, 0
pattern found at 0, 2
pattern found at 0, 10
pattern found at 2, 2
pattern found at 2, 12
function searchWord($array, $word) {
$rows = count($array);
$cols = count($array[0]);
$directions = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1]
];
$count = 0;
for ($i = 0; $i < $rows; $i++) {
for ($j = 0; $j < $cols; $j++) {
foreach ($directions as $dir) {
$k = 0;
$x = $i;
$y = $j;
while (isset($array[$x][$y]) && $array[$x][$y] === $word[$k]) {
$x += $dir[0];
$y += $dir[1];
$k++;
if ($k === strlen($word)) {
$count++;
break;
}
}
}
}
}
return $count;
}
The function takes in two parameters: $array which is the multi-dimensional array and $word which is the word to be searched.
The function first initializes the $directions array which contains 8 possible directions to search the word.
Then, it loops through the array and checks each character in the array. For each character, it loops through all 8 directions to see if the word can be found by following that direction. If the word is found, the count is incremented. The loop continues until all directions for all characters have been checked.
Finally, the function returns the count of the number of times the word appears in the array.
I would like to make a function that is able to generate a list of letters and optional numbers using a-z,0-9.
$output = array();
foreach(range('a','z') as $i) {
foreach(range('a','z') as $j) {
foreach(range('a','z') as $k) {
$output[] =$i.$j.$k;
}
}
}
Thanks
example:
myfunction($include, $length)
usage something like this:
myfunction('a..z,0..9', 3);
output:
000
001
...
aaa
aab
...
zzz
The output would have every possible combination of the letters, and numbers.
Setting the stage
First, a function that expands strings like "0..9" to "0123456789" using range:
function expand_pattern($pattern) {
$bias = 0;
$flags = PREG_SET_ORDER | PREG_OFFSET_CAPTURE;
preg_match_all('/(.)\.\.(.)/', $pattern, $matches, $flags);
foreach ($matches as $match) {
$range = implode('', range($match[1][0], $match[2][0]));
$pattern = substr_replace(
$pattern,
$range,
$bias + $match[1][1],
$match[2][1] - $match[1][1] + 1);
$bias += strlen($range) - 4; // 4 == length of "X..Y"
}
return $pattern;
}
It handles any number of expandable patterns and takes care to preserve their position inside your source string, so for example
expand_pattern('abc0..4def5..9')
will return "abc01234def56789".
Calculating the result all at once
Now that we can do this expansion easily, here's a function that calculates cartesian products given a string of allowed characters and a length:
function cartesian($pattern, $length) {
$choices = strlen($pattern);
$indexes = array_fill(0, $length, 0);
$results = array();
$resets = 0;
while ($resets != $length) {
$result = '';
for ($i = 0; $i < $length; ++$i) {
$result .= $pattern[$indexes[$i]];
}
$results[] = $result;
$resets = 0;
for ($i = $length - 1; $i >= 0 && ++$indexes[$i] == $choices; --$i) {
$indexes[$i] = 0;
++$resets;
}
}
return $results;
}
So for example, to get the output described in the question you would do
$options = cartesian(expand_pattern('a..z0..9'), 3);
See it in action (I limited the expansion length to 2 so that the output doesn't explode).
Generating the result on the fly
Since the result set can be extremely large (it grows exponentially with $length), producing it all at once can turn out to be prohibitive. In that case it is possible to rewrite the code so that it returns each value in turn (iterator-style), which has become super easy with PHP 5.5 because of generators:
function cartesian($pattern, $length) {
$choices = strlen($pattern);
$indexes = array_fill(0, $length, 0);
$resets = 0;
while ($resets != $length) {
$result = '';
for ($i = 0; $i < $length; ++$i) {
$result .= $pattern[$indexes[$i]];
}
yield $result;
$resets = 0;
for ($i = $length - 1; $i >= 0 && ++$indexes[$i] == $choices; --$i) {
$indexes[$i] = 0;
++$resets;
}
}
}
See it in action.
See this answer for a code that produces all possible combinations:
https://stackoverflow.com/a/8567199/1800369
You just need to add the $length parameter to limit the combinations size.
You can use a recursive function
assuming you mean it can be any number of levels deep, you can use a recursive function to generate an array of the permutations e.g.:
/**
* take the range of characters, and generate an array of all permutations
*
* #param array $range range of characters to itterate over
* #param array $array input array - operated on by reference
* #param int $depth how many chars to put in the resultant array should be
* #param int $currentDepth internal variable to track how nested the current call is
* #param string $prefix internal variable to know what to prefix the current string with
* #return array permutations
*/
function foo($range, &$array, $depth = 1, $currentDepth = 0, $prefix = "") {
$start = !$currentDepth;
$currentDepth++;
if ($currentDepth > $depth) {
return;
}
foreach($range as $char) {
if ($currentDepth === $depth) {
$array[] = $prefix . $char;
continue;
}
foo($range, $array, $depth, $currentDepth, $prefix . $char);
}
if ($start) {
return $array;
}
With the above function, initialize the return variable and call it:
$return = array();
echo implode(foo(range('a', 'z'), $return, 3), "\n");
And you're output will be all three char combinations from aaa, to zzz:
aaa
aab
...
zzy
zzz
The numeric parameter determins how recursive the function is:
$return = array();
echo implode(foo(range('a', 'z'), $return, 1), "\n");
a
b
c
...
Here's a live example.
$number= range(0, 9);
$letters = range('a', 'z');
$array= array_merge($number, $letters);
//print_r($array);
for($a=0;$a<count($array);$a++){
for($b=0;$b<count($array);$b++){
for($c=0;$c<count($array);$c++){
echo $array[$a].$array[$b].$array[$c]."<br>";
}
}
}
tested and working :)
I've created an array with $my_array = range(1,3) and I'd like to have an array created containing all the permutations of this one (1,2,3 1,3,2 2,1,3 ..).
The second number in the range function can change (it could be 4, 5, 7, 50 instead of 3).
Code from Christer's blog o' fun :
function permute($str) {
/* If we only have a single character, return it */
if (strlen($str) < 2) {
return array($str);
}
/* Initialize the return value */
$permutations = array();
/* Copy the string except for the first character */
$tail = substr($str, 1);
/* Loop through the permutations of the substring created above */
foreach (permute($tail) as $permutation) {
/* Get the length of the current permutation */
$length = strlen($permutation);
/* Loop through the permutation and insert the first character of the original
string between the two parts and store it in the result array */
for ($i = 0; $i <= $length; $i++) {
$permutations[] = substr($permutation, 0, $i) . $str[0] . substr($permutation, $i);
}
}
/* Return the result */
return $permutations;
}
$array_of_natural_numbers = range(1, 5);
$string_of_natural_numbers = implode("",$array_of_natural_numbers);
$permutations = permute($string_of_natural_numbers);
Given:
$this->objPHPExcelReader = PHPExcel_IOFactory::createReaderForFile($this->config['file']);
$this->objPHPExcelReader->setLoadSheetsOnly(array($this->config['worksheet']));
$this->objPHPExcelReader->setReadDataOnly(true);
$this->objPHPExcel = $this->objPHPExcelReader->load($this->config['file']);
I can iterate through the rows like this but it is very slow, i.e. in a 3MB Excel file with a worksheet that has "EL" columns, it takes about 1 second per row:
foreach ($this->objPHPExcel->setActiveSheetIndex(0)->getRowIterator() as $row)
{
$dataset = array();
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false);
foreach ($cellIterator as $cell)
{
if (!is_null($cell))
{
$dataset[] = $cell->getCalculatedValue();
}
}
$this->datasets[] = $dataset;
}
When I iterate like this, it it significantly faster (approx. 2000 rows in 30 seconds), but I will have to convert the letters e.g. "EL" to a number:
$highestColumm = $this->objPHPExcel->setActiveSheetIndex(0)->getHighestColumn(); // e.g. "EL"
$highestRow = $this->objPHPExcel->setActiveSheetIndex(0)->getHighestRow();
$number_of_columns = 150; // TODO: figure out how to get the number of cols as int
for ($row = 1; $row < $highestRow + 1; $row++) {
$dataset = array();
for ($column = 0; $column < $number_of_columns; $column++) {
$dataset[] = $this->objPHPExcel->setActiveSheetIndex(0)->getCellByColumnAndRow($column, $row)->getValue();
}
$this->datasets[] = $dataset;
}
Is there a way to get the highest column as an integer (e.g. "28") instead of in Excel-styled letters (e.g. "AB")?
$colNumber = PHPExcel_Cell::columnIndexFromString($colString);
returns 1 from a $colString of 'A', 26 from 'Z', 27 from 'AA', etc.
and the (almost) reverse
$colString = PHPExcel_Cell::stringFromColumnIndex($colNumber);
returns 'A' from a $colNumber of 0, 'Z' from 25, 'AA' from 26, etc.
EDIT
A couple of useful tricks:
There is a toArray() method for the worksheet class:
$this->datasets = $this->objPHPExcel->setActiveSheetIndex(0)->toArray();
which accepts the following parameters:
* #param mixed $nullValue Value returned in the array entry if a cell doesn't exist
* #param boolean $calculateFormulas Should formulas be calculated?
* #param boolean $formatData Should formatting be applied to cell values?
* #param boolean $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
* True - Return rows and columns indexed by their actual row and column IDs
although it does use the iterators, so would be slightly slower
OR
Take advantage of PHP's ability to increment character strings Perl Style
$highestColumm = $this->objPHPExcel->setActiveSheetIndex(0)->getHighestColumn(); // e.g. "EL"
$highestRow = $this->objPHPExcel->setActiveSheetIndex(0)->getHighestRow();
$highestColumm++;
for ($row = 1; $row < $highestRow + 1; $row++) {
$dataset = array();
for ($column = 'A'; $column != $highestColumm; $column++) {
$dataset[] = $this->objPHPExcel->setActiveSheetIndex(0)->getCell($column . $row)->getValue();
}
$this->datasets[] = $dataset;
}
and if you're processing a large number of rows, you might actually notice the performance improvement of ++$row over $row++
This is a somewhat simplified version of dqhendricks answer. I have added to copies, one function assuming you enter the full excel cell reference (ie. "AB12") and the other assuming you enter just the column reference (ie. "AB"). They both return a zero based index.
Input Full Cell Reference
function getIndex ($cell) {
// Strip cell reference down to just letters
$let = preg_replace('/[^A-Z]/', '', $cell);
// Iterate through each letter, starting at the back to increment the value
for ($num = 0, $i = 0; $let != ''; $let = substr($let, 0, -1), $i++)
$num += (ord(substr($let, -1)) - 65) * pow(26, $i);
return $num;
}
Input Column Reference Only
function getIndex ($let) {
// Iterate through each letter, starting at the back to increment the value
for ($num = 0, $i = 0; $let != ''; $let = substr($let, 0, -1), $i++)
$num += (ord(substr($let, -1)) - 65) * pow(26, $i);
return $num;
}
The function goes from the back of the string to the front to increase the value of the column. It uses the ord() function to get the numeric value of a character and then has the letter value subtracted to give the local column value. Finally it is multiplied by the current power of 26.
I suggest to convert excel to array, clean it from empty elements and then count the number of columns:
protected function getColumnsCheck($file, $col_number) {
if (strstr($file, ".xls") != false && strstr($file, ".xlsx") != false) {
$fileType = PHPExcel_IOFactory::identify($file);
$objReader = PHPExcel_IOFactory::createReader($fileType);
$objPHPExcel = $objReader->load($file);
$columns_empty = $objPHPExcel->getActiveSheet(0)->toArray()[0];
$columns = array_filter($columns_empty);
return ($col_number==count($columns));
}
return false;
}
/**
* Example 0 = A, 1 = B
*/
function getNameFromNumber(int $num): string {
if ($num < 0) throw new TypeError('$num must be at least 0');
$numeric = $num % 26;
$letter = chr(65 + $numeric);
$num2 = intval($num / 26);
if ($num2 > 0) {
return (__FUNCTION__)($num2 - 1) . $letter;
} else {
return $letter;
}
}
getNameFromNumber(0) // returns "A"
Not sure if your class has a built in method, but you could always use the ord() function on each letter of the column index string. You will of course have to subtract out the base value of 'A', and multiply by 26^x for each position from the far right of the string. Something like:
$input_string = 'BC';
$base_value = 64;
$decimal_value = 26;
$column_index = 0;
for ($i = 0; $i < strlen($input_string); $i++) {
$char_value = ord($input_string[$i]);
$char_value -= $base_value;
$char_value *= pow($decimal_value, (strlen($input_string) - ($i + 1)));
$column_index += $char_value;
}
echo $column_index;
Basically this would make 'BC' equal (2 * 26^1) + (3 * 26^0) = 55.
$input_string being the column index string, $base_value being the ord() value of 'A' minus 1, and $decimal_value being the value of A0. Should work up to any number column. Have tested. Hope this helps.
/**
*
*/
function number_to_alphabet($number) {
$number = intval($number);
if ($number <= 0) {
return '';
}
$alphabet = '';
while($number != 0) {
$p = ($number - 1) % 26;
$number = intval(($number - $p) / 26);
$alphabet = chr(65 + $p) . $alphabet;
}
return $alphabet;
}
/**
* Required PHP 5.6.
* #see: http://php.net/manual/en/language.operators.arithmetic.php
*/
function alphabet_to_number($string) {
$string = strtoupper($string);
$length = strlen($string);
$number = 0;
$level = 1;
while ($length >= $level ) {
$char = $string[$length - $level];
$c = ord($char) - 64;
$number += $c * (26 ** ($level-1));
$level++;
}
return $number;
}
Test:
for ($x=1; $x<=1000; $x++) {
echo 'number_to_alphabet('.$x.') = ',$y = number_to_alphabet($x),'; ';
echo 'alphabet_to_number('.$y.') = '.alphabet_to_number($y).'; ';
echo PHP_EOL;
}
Since this question is 10 years old, and the packages referenced here are not the newest ones any more:
Here is how you do it using phpspreadsheet:
$colNumber = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($colString); // e.g. 5
Source:
https://phpspreadsheet.readthedocs.io/en/latest/topics/accessing-cells/