How Do I Implement Incomplete Answer to Old Question - php

This comment looks like it would work if the author included the value for $numbers. They say it is some type of array, but don't provide enough information to replicate it. I picture some hard coded array ranging from 0 to 9, but I can't help think that such an array would miss numbers greater than 9. What does the numbers array in this example look like?
$text = "1 out of 23";
if(preg_match_all('/\d+/', $text, $numbers))
$lastnum = end($numbers[0]);
I would just post a comment asking whoever wrote that to paste the value for $numbers, but it says I need reputation points to do that.
See How do I grab last number in a string in PHP?

To answer your initial question print_r() can be used to output all contents of an array. e.g. print_r($numbers)
https://3v4l.org/2jA1b
To explain the code:
\d is a single number
+ is a quantifier meaning one or more of the previous character or group
so this would find all numbers in a string. The $numbers[0] would be all numbers, 1 per index, and the end() pulls to the last number/index. Each index would be a number, the 0 is all matches, each indice at the root level is a capture group.
This code wouldn't work as intended for decimals or comma delimited integers. In those cases the numbers would be split up at the delimiter. 1.0 would become 1 and 0 (2 different numbers).
You could rewrite this as:
$text = "1 out of 23";
if(preg_match('/.*\K\D\d+/', $text, $numbers))
echo $numbers[0];
so the end function is not needed. This pulls everything until the last number then forgets everything before the last number.

What you are trying to do is likely easier using preg_split instead of preg_match_all. We can split the input text by the matched regex (digits) and then rebuild the string while incrementing the numbers as we go.
<?php
function incrementNumbers($text) {
// NOTES:
// parenthesis are important in the regex in order to return the captured values
// the -? will capture negative numbers too if necessary
// PREG_SPLIT_DELIM_CAPTURE allows the captured values to be returned too
$split = preg_split('/(-?\d+)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
$return = '';
foreach($split as $i => $s) {
// because we didn't use PREG_SPLIT_NO_EMPTY, $split[0] will either be an empty string if
// $text began with a number, or the text before the first number. Either way, $split alternates
// between non-number [0], number [1], non-number [2], number [3], etc which is why we can detect
// even or odd indexes to determine if this is a number that needs to be incremented or not
if ($i % 2 === 0) {
$return .= $s;
} else {
$return .= (intval($s) + 1);
}
}
return $return;
}
Examples:
echo incrementNumbers("1 out of 23 with 1 and 1 and 24 and 23");
echo incrementNumbers("1 1 2 2 3 3 2 2 1 1");
echo incrementNumbers("0 1 2 3 4 5 6 7");
echo incrementNumbers("-3 -2 -1 0 1 2 3 4 5 6 7");
echo incrementNumbers("there are no numbers in this text");
echo incrementNumbers("does not start 999 with a number 123 nor end 17 with a number");
Outputs:
2 out of 24 with 2 and 2 and 25 and 24
2 2 3 3 4 4 3 3 2 2
1 2 3 4 5 6 7 8
-2 -1 0 1 2 3 4 5 6 7 8
there are no numbers in this text
does not start 1000 with a number 124 nor end 18 with a number
Working example at https://3v4l.org/iKskO

Related

How To Get Concatenate Number Or String PHP With Long Consecutive Numbers In PHP?

So I want to get number with Consecutive Numbers like below.
1234567891011121314151617181920212223..............
when i input 10 then output is 1. and so on like below.
10th digit is 1
11th digit is 0
12th digit is 1
13th digit is 1
14th digit is 1
15th digit is 2
16th digit is 1
17th digit is 3
This is my code to make Consecutive Numbers. Its work as i want expected.
<?php
$i=1;
$urut='';
$a = $_GET['button1'];
echo "Your number ", $a, "<br>";
while ($i<=$a){
$urut=$urut.''.$i;
$i++;
}
$pecah = str_split($urut,1);
echo "Urut angka ke ".$a."adalah ". $pecah[$a-1];
?>
But my case is how to get number when Consecutive up to 1 million or more.
I tried that but when set 1 millions or more its load very long time and cant show the result number
You'll probably need to do something like calculating the length of each range of numbers.
For example you know that:
1 through 9 are 1 character
10 through 99 are 2 characters
100 through 999 are 3 characters
And so on
Doing so will allow you to calculate the length of the string up to a certain number.
For example the amount of characters before 1000 can be calculated like so.
9x1 + (99 - 10) x 2 + (999 - 100) x 3
Using this you can distil a formula which allows you to calculate the value for any given number.

Find a number by given digit

Let’s say I have a series of numbers like:
12345678910111213141516... (until unlimited)
Then I would like to get a number from it by given digit. For example:
Digit 10th: 1
Digit 17th: 3
...
I have tried to make the algorithm to do it by using PHP but it always showed me an error due to the looping that I made was out of memory size if the given digit that I gave is more than 10.000.000. Allowed Memory Size of 134217728 Bytes Exhausted
How do I deal with this without having to modify memory_limit on php.ini file?
Here are what I have tried to figure the algorithm out: I benchmark the maximum of upper limit of the loop that my local machine could handle, and I found out it's 10.000.000, then I assumed I need to make a separate loop if the given digit/parameter is more than 10.000.000. But in the end I still got that error of out of memory size. Really grateful in advance.
<?php
/*
* benchmark result:
* max digit = 10.000.000
*/
$benchmarkedDigit = 10000000;
$digit = 1000000000000; // it could be dynamically assigned, i.e. a parameter. In this case will show an error since the given digit is 10 trillion
$s = '';
if ($digit > $benchmarkedDigit) {
$mod = fmod($digit, $benchmarkedDigit);
$div = $digit / $benchmarkedDigit;
for ($x = 1; $x <= $div; $x++) {
$upperLimit = ($x * $benchmarkedDigit);
for ($y = ($upperLimit - $benchmarkedDigit + 1); $y <= $upperLimit; $y++) {
$s .= $y;
}
// so it could be:
// 1 - 10.000.000
// 10.000.001 - 20.000.000
// 20.000.001 - 30.000.000
// ...
}
// loop for the rest of the fmod(), if its result is not 0
for ($i = ($upperLimit + 1); $i <= ($upperLimit + $mod); $i++) {
$s .= $i;
}
} else {
for ($x = 1; $x <= $digit; $x++) {
$s .= $x;
}
}
echo substr($s, ($digit - 1), 1);
You can use the fact that there's always 10^n - 10^(n-1) number of n-digit long numbers (even 1 digit, because I see 0 is not there).
With this knowledge, you can skip potentially huge number of numbers.
You start with n=1, and check if the number of n digit numbers is lower than the desired digit. If it is, then reduce the number of n digit numbers from the desired number, increase n by one and start again.
For example: you want to know the 512th digit in that number
Is the number of 1 digit numbers (10) lower than the desired digit (512)?
Yes, so the desired digit should be reduced by that many (512 - 9).
Is the number of 2 digit numbers (90) lower than the desired digit (503 now)?
Yes, so the desired digit should be reduced by that many (503 - 90).
Is the number of 3 digit numbers (900) lower than the desired digit(413 now)?
No, so the desired digit is one of the digits of a 3 digit number.
413 / 3 is 137 (rounded down), so it's one of the digits of the 137th 3 digit numbers (so 237).
413 % 3 (modulo) is 2, so it's the 2nd digit, so it's supposed to be 3.
There can be miscalculations in this, but the overall logic should not be far.
Edit: you could also use a generator, but this can increase the runtime for big numbers
function getNthDigit() {
for ($i = 0;; ++$i) { // Start with 0, which is the 0-th digit
foreach (str_split((string)$i) as $digit) {
yield $digit;
}
}
}
$desiredDigit = 512;
foreach (getNthDigit() as $number => $digit) {
if ($number == $desiredDigit) {
break;
}
}
// $digit should be the desired digit
<?php
function getDigit($Nth){
if($Nth < 10) return $Nth;
$no_of_digits = 1;
$current_contribution = 9;
$actual_length = 9;
$prev_length = 0;
$starting_number = 1;
$power_of_10 = 1;
while($actual_length < $Nth){
$no_of_digits++;
$current_contribution *= 10;
$prev_length = $actual_length;
$actual_length += ($current_contribution * $no_of_digits);
$power_of_10 *= 10;
$starting_number *= 10;
}
$Nth = $Nth - $prev_length;
$offset = $Nth % $no_of_digits === 0 ? intval($Nth / $no_of_digits) - 1 : intval($Nth / $no_of_digits);
$number = strval($starting_number + $offset);
for($i=1;$i<=$no_of_digits;++$i){
if(($Nth - $i) % $no_of_digits === 0){
return $number[$i-1];
}
}
}
// first 100 Digits
for($i=1;$i<=100;++$i){
echo getDigit($i),PHP_EOL;
}
Demo: https://3v4l.org/3l0I7
Algorithm:
To find the nth digit, we will first find the number and then which digit of that number to choose as an answer.
Find the number:
If we carefully observe, the series increases in a sequential manner, such as shown in the table.
Table:
| Digits| Total numbers(of current digit)| Total Digits | Total digits of whole string |
|-------|--------------------------------|--------------|-------------------------------|
| 1 | 9 | 9 | 9 |
| 2 | 90 | 180 | 189 |
| 3 | 900 | 2700 | 2889 |
| 4 | 9000 | 36000 | 38889 |
The above table shows us that if we want to find, let's say 500th digit, then it's some digit of 3 digit number. If we go for 17th digit, then it's some digit of a 2 digit number and so on.
Now, let's take 200th digit as an example. Since it's less than 2889 and greater than 189, it's from a 3 digit number.
What we would do is breakdown the 200 into a smaller number such as 200 - 189 = 11. This 11 means that it's 11th digit of some 3 digit number which started with initial 3 digit number of 100(the starting number for 3 digit).
Now, we do 11 / 3(where 3 is number of digits) and get the quotient as 3. This 3 means that it's 3 numbers past the starting number 100, which we can say as 100 + 3 = 103(since it's 100,101,102 and then the 4th one as 103).
Now, we came to know that the number is 103. All is left to find out is which digit from 103.
Note that sometimes we come across a corner case of even divisibility such as 12 / 3. In this case, we subtract 1 from the quotient since our series of 3 digits starts from 100 and not 101( and so on and so forth for other digits).
Find out the digit:
Now, we know that the number is 103 for a 200 th digit( a.k.a 11 as we calculated above). To find out which one, we write down numbers of 3 digits in sequence and closely observe them.
Sequence:
1 0 0 1 0 1 1 0 2 1 0 3 1 0 4 1 0 5 1 0 6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
If you observe, you can understand that the most MSB digit follows a sequence of 1,4,7,10,13 etc. Second most MSB follows a sequence of 2,5,8,11,14 etc and the last MSB(which is LSB) follows a sequence of 3,6,9,12,15 etc.
So, from th above sequence, it's pretty evident that 11(which we got after breaking down 200 initially) belongs to a sequence of the 2nd most MSB digit.
So, the final answer from 103 is 0 (the 2nd digit from left).
$num = '12345678910111213141516';
echo $num[16];
Result: 3

php remove redundant words from string range

I have a range of information. For example:
Volume 1 Chapter 3 Page 5 TO Volume 1 Chapter 5 Page 10
what is the fastest way to remove redundant information and convert this to:
Volume 1 Chapter 3 Page 5 TO Chapter 5 Page 10
OR if the input is
Volume 1 Chapter 3 Page 5 TO Volume 1 Chapter 3 Page 10
then output
Volume 1 Chapter 3 Page 5 TO Page 10
The hardest part here is to split the input into tokens, as it's not structured well enough. I used a recursive function to sequentially clean the string of the first element duplicates. It works correctly for this input, but I am not sure, that it's 100% correct, as input structure is unclear:
<?php
$str = 'Volume 1 Chapter 3 Page 5 TO Volume 1 Chapter 3 Page 10';
$str = clear_first_element_duplicates($str);
var_dump($str);
function clear_first_element_duplicates($str)
{
if (preg_match('/(.*?\d)\s(.*)/', $str, $tokens))
{
$regexp = preg_quote($tokens[1]);
$str = preg_replace("/$regexp\s?/", '', $tokens[2]);
return $tokens[1]." ".clear_first_element_duplicates($str);
}
return $str;
}
Prints:
"Volume 1 Chapter 3 Page 5 TO Page 10"
My script seems complex but worth it:
I Added variable Levels, So it's not restricted to just Volume, chapter and pages, you can add for example paragraph line and character if desired, and you can even change the Wording. See examples at the end.
** BE careful with the $separator parameter, it must be Exact(case sensitive) and might appear only once on the script, this is easy to fix but I focused on the important part of the function **
function redundancy($string, $separator){
list($a, $b) = explode($separator, $string);
//getting the numeric values of both sides
$pattern = '/[0-9]+/';
preg_match_all($pattern, $a, $a_values);
preg_match_all($pattern, $b, $b_values);
$a_values = $a_values[0];
$b_values = $b_values[0];
//getting the wording and cleaning out the numbers, I guess this can be improved through a better REGEX
preg_match_all('/\b\w+\b/', $a, $matches);
foreach($matches[0] as $match){
if(!is_numeric($match)) $words[] = $match;
}
//algorithm
$length = count($a_values) - 1; // excluding the last element, to be checked separately
$output = $a.$separator." ";
$same_full_path = true; // check if the levels has been altered to check the last element
$same_parent = true; // check the previous level
for($i = 0; $i < $length; $i++){
if($a_values[$i] !== $b_values[$i] || $same_parent === false){
$same_parent = false;
$same_full_path = false;
$output .= $words[$i]." ".$b_values[$i]." ";
}
}
//adding the word to the last element or not, The last element check must be outside the loop because it's special;
if($same_full_path === false || end($a_values) === end($b_values)) $output .= end($words)." ";
$output .= end($b_values);
echo "$string <Br/> $output; <br/><br/> ";
}
redundancy('Volume 1 Chapter 3 Page 5 TO Volume 1 Chapter 5 Page 10', 'TO');
redundancy('Serie 1 Season 2 Chapter 2 Minute 5 Second 6 Until Serie 1 Season 3 Chapter 4 Minute 3 Second 1', 'Until');
redundancy('District 4 Building 2 Floor 4 Door 5 To District 4 Building 2 Floor 4 Door 8', 'To');
Outputs :
Volume 1 Chapter 3 Page 5 TO Volume 1 Chapter 5 Page 10
Volume 1 Chapter 3 Page 5 TO Chapter 5 Page 10;
-
Serie 1 Season 2 Chapter 2 Minute 5 Second 6 Until Serie 1 Season 3 Chapter 4 Minute 3 Second 1
Serie 1 Season 2 Chapter 2 Minute 5 Second 6 Until Season 3 Chapter 4 Minute 3 Second 1;
-
District 4 Building 2 Floor 4 Door 5 To District 4 Building 2 Floor 4 Door 8
District 4 Building 2 Floor 4 Door 5 To 8;

An odd assignment about adding dashes to strings. PHP

I have a need for a function that will do the following thing:
If I have a string like this "2 1 3 6 5 4 8 7" I have to insert dashes between pairs of numbers following some rules.
The rules are simple.
Put a dash between two numbers if the first one of the pair is smaller then the one that follows it. Do all possible combinations of this and if a pair already has a dash then the space next to it can't have a dash.
Basically my results for above string would be
2 1-3 6 5 4 8 7
2 1-3 6 5 4-8 7
2 1 3-6 5 4 8 7
2 1 3-6 5 4-8 7
2 1 3 6 5 4-8 7
I did create a function that does this but I am thinking it is pretty sluggish and I don't want to taint your ideas with it. If possible I would like to know how you guys are thinking about this and even some pseudo code or code would be great.
EDIT 1:
here is the code I have so far
$string = "2 1 3 6 5 4 8 7";
function dasher($string){
global $dasherarray;
$lockcodes = explode(' ', $string);
for($i = 0; $i < count($lockcodes) - 1; $i++){
if(strlen($string) > 2){
$left = $lockcodes[$i];
$right = $lockcodes[$i+1];
$x = $left . ' ' . $right;
$y = $left . '-' . $right;
if (strlen($left) == 1 && strlen($right) == 1 && (int)$left < (int)$right) {
$dashercombination = str_replace($x, $y, $string);
$dasherarray[] = $dashercombination;
dasher($dashercombination);
}
}
}
return array_unique($dasherarray);
}
foreach(dasher($string) as $combination) {
echo $combination. '<br>';
}
Perhaps this will be helpful in terms of offering different methods to parse the string.
$str="2 1 3 6 5 4 8 7";
$sar=explode(' ',$str);
for($i=1;$i<count($sar);$i++)
if($sar[$i-1]<$sar[$i])
print substr_replace($str,'-',2*($i-1)+1,1) . "\n";
Note that the code expects only single digits numbers in the string.
Note that the code expects that the string is formatted as per your example. It would be good to add some sanity checks (collapse multiple spaces, strip/trim blanks at the beginning/end).
We can improve upon this by finding all the spaces in the string and using them to index substrings for comparison, still assuming that only a single spaces separates adjacent numbers.
<?php
$str="21 11 31 61 51 41 81 71";
$letter=' ';
#This finds the locations of all the spaces in the strings
$spaces = array_keys(array_intersect(str_split($str),array($letter)));
#This function takes a start-space and an end-space and finds the number between them.
#It also takes into account the special cases that we are considering the first or
#last space in the string
function ssubstr($str,$spaces,$start,$end){
if($start<0)
return substr($str,0,$spaces[$end]);
if($end==count($spaces))
return substr($str,$spaces[$start],strlen($str)-$spaces[$start]);
return substr($str,$spaces[$start],$spaces[$end]-$spaces[$start]);
}
#This loops through all the spaces in the string, extracting the numbers on either side for comparison
for($i=0;$i<count($spaces);$i++){
$firstnum=ssubstr($str,$spaces,$i-1,$i);
$secondnum=ssubstr($str,$spaces,$i,$i+1) . "\n";
if(intval($firstnum)<intval($secondnum))
print substr_replace($str,'-',$spaces[$i],1) . "\n";
}
?>
Note the explicit conversion to integers in order to avoid lexicographic comparison.

Count unique appearance of substring in a list of words without knowing the substr?

*I try to count the unique appearances of a substring inside a list of words *
So check the list of words and detect if in any words there are substrings based on min characters that occur multiple times and count them. I don't know any substrings.
This is a working solution where you know the substring but what if you do not know ?
Theres a Minimum Character count where words are based on.
Will find all the words where "Book" is a substring of the word. With below php function.
Wanted outcome instad:
book count (5)
stor count (2)
Given a string of length 100
book bookstore bookworm booking book cooking boring bookingservice.... ok
0123456789... ... 100
your algorithm could be:
Investigate substrings from different starting points and substring lengths.
You take all substrings starting from 0 with a length from 1-100, so: 0-1, 0-2, 0-3,... and see if any of those substrings accurs more than once in the overall string.
Progress through the string by starting at increasing positions, searching all substrings starting from 1, i.e. 1-2, 1-3, 1-4,... and so on until you reach 99-100.
Keep a table of all substrings and their number of occurances and you can sort them.
You can optimize by specifying a minimum and maximum length, which reduces your number of searches and hit accuracy quite dramatically. Additionally, once you find a substring save them in a array of searched substrings. If you encounter the substring again, skip it. (i.e. hits for book that you already counted you should not count again when you hit the next booksubstring). Furthermore you will never have to search strings that are longer than half of the total string.
For the example string you might run additional test for the uniquness of a string.
You'd have
o x ..
oo x 7
bo x 7
ok x 6
book x 5
booking x 2
bookingservice x 1
with disregarding stings shorter than 3 (and longer than half of total textstring), you'd get
book x 5
booking x 2
bookingservice x 1
which is already quite a plausible result.
[edit] This would obviously look through all of the string, not just natural words.
[edit] Normally I don't like writing code for OPs, but in this case I got a bit interested myself:
$string = "book bookshelf booking foobar bar booking ";
$string .= "selfservice bookingservice cooking";
function search($string, $min = 4, $max = 16, $threshhold = 2) {
echo "<pre><br/>";
echo "searching <em>'$string'</em> for string occurances ";
echo "of length $min - $max: <br/>";
$hits = array();
$foundStrings = array();
// no string longer than half of the total string will be found twice
if ($max > strlen($string) / 2) {
$max = strlen($string);
}
// examin substrings:
// start from 0, 1, 2...
for ($start = 0; $start < $max; $start++) {
// and string length 1, 2, 3, ... $max
for ($length = $min; $length < strlen($string); $length++) {
// get the substring in question,
// but search for natural words (trim)
$substring = trim(substr($string, $start, $length));
// if substring was not counted yet,
// add the found count to the hits
if (!in_array($substring, $foundStrings)) {
preg_match_all("/$substring/i", $string, $matches);
$hits[$substring] = count($matches[0]);
}
}
}
// sort the hits array desc by number of hits
arsort($hits);
// remove substring hits with hits less that threshhold
foreach ($hits as $substring => $count) {
if ($count < $threshhold) {
unset($hits[$substring]);
}
}
print_r($hits);
}
search($string);
?>
The comments and variable names should make the code explain itself. $string would come for a read file in your case. This exmaple would output:
searching 'book bookshelf booking foobar bar booking selfservice
bookingservice cooking' for string occurances of length 4 - 16:
Array
(
[ook] => 6
[book] => 5
[boo] => 5
[bookin] => 3
[booking] => 3
[booki] => 3
[elf] => 2
)
Let me know how you implement it :)
This is my first approximation: unfinished, untested, has at least 1 bug, and is written in eiffel. Well I am not going to do all the work for you.
deferred class
SUBSTRING_COUNT
feature
threshold : INTEGER_32 =5
biggest_starting_substring_length(a,b:STRING):INTEGER_32
deferred
end
biggest_starting_substring(a,b:STRING):STRING
do
Result := a.substring(0,biggest_starting_substring_length(a,b))
end
make_list_of_substrings(a,b:STRING)
local
index:INTEGER_32
this_one: STRING
do
from
a_index := b_index + 1
invariant
a_index >=0 and a_index <= a.count
until
a_index >= a.count
loop
this_one := biggest_starting_substring(a.substring (a_index, a.count-1),b)
if this_one.count > threshold then
list.extend (this_one)
end
variant
a.count - a_index
end
end -- biggest_substring
list : ARRAYED_LIST[STRING]
end

Categories