假设我有一系列数字,例如:
12345678910111213141516... (until unlimited)
然后我想从给定数字中得到一个数字。例如:
我试图通过使用PHP来实现该算法,但是如果我给出的给定数字大于 10.000,由于循环不足,我总是会向我显示错误。 000 。 Allowed Memory Size of 134217728 Bytes Exhausted
如何处理而不必修改php.ini文件上的memory_limit
?
以下是我试图弄清楚该算法的方法:我对本机可以处理的循环的最大值进行了基准测试,发现它是 10.000.000 ,然后我假设如果给定的数字/参数大于10.000.000,则需要进行单独的循环。但是最后我仍然遇到内存不足的错误。真的非常感谢。
<?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);
答案 0 :(得分:3)
您可以使用这样的事实,总是有10^n - 10^(n-1)
个n位长数字(甚至是1位数字,因为我看不到0)。
有了这些知识,您就可以跳过大量的数字。 您从n = 1开始,并检查n位数字的数量是否低于所需的数字。如果是这样,则从所需的数字中减少n位数字的数目,将n增加1并重新开始。
例如:您想知道该数字中的第512位数字 1位数字(10)的数量是否低于所需的数字(512)? 是的,因此所需的数字应减少那么多(512-9)。 2位数字的数量(90)是否低于所需的数字(现在为503)? 是的,因此所需的位数应减少那么多(503-90)。 3位数字的数量(900)是否低于所需的数字(现在为413)? 否,因此所需的数字是3位数的数字之一。 413/3是137(向下舍入),因此它是第137个3位数的数字之一(即237)。 413%3(模)为2,所以它是第二个数字,所以应该是3。
其中可能存在错误的计算,但总体逻辑不应太远。
编辑:您还可以使用生成器,但这会增加大数的运行时间
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
答案 1 :(得分:1)
<?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;
}
算法:
要找到第n个 位,我们将首先找到该数字,然后选择该数字的哪个数字作为答案。
查找号码:
表格:
| 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 |
上表向我们显示,如果要查找,假设第500个 位数字,则为3位数字的某个数字。如果我们输入第17个 位,那么它是2位数字的某个数字,依此类推。
现在,让我们以第200位 位为例。由于它小于2889
且大于189
,因此来自 3位数数字。
我们要做的是将200
分解为较小的数字,例如200 - 189 = 11
。 11
表示它是某个3位数字中的第11位,以3
的初始100
位数字开头(3位的起始数字)。
现在,我们进行11 / 3
(其中3
是位数)并将商得到为{{1}}。 3
表示它是3
个数字,过去是起始数字3
,我们可以将其称为100
(因为它是100,101,102,然后是第4个一个为103)。
现在,我们知道数字是100 + 3 = 103
。剩下的就是找出103
中的哪个数字。
请注意,有时我们遇到偶数除法的极端情况,例如12 /3。在这种情况下,我们从商中减去1,因为我们的3位数系列从100开始而不是101(其他数字依此类推)。
找出数字:
103
位的数字是103
(也就是我们上面计算的200
)。为了找出哪一个,我们依次记下3位数字并仔细观察它们。 序列:
11
如果您观察到,则可以理解最高MSB数字遵循序列1,4,7,10,13等。第二高MSB数字遵循序列2,5,8,11,14等最后一个MSB(即LSB)遵循3、6、9、12、15等的顺序。
因此,从上述序列来看,很明显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
(最初分解为11
之后得到的)属于第二个 大多数MSB数字。
因此, 103 的最终答案是 0 (从左数第二位)。
答案 2 :(得分:0)
$num = '12345678910111213141516';
echo $num[16];
结果: 3