繁体   English   中英

按给定数字查找数字

[英]Find a number by given digit

假设我有一系列数字,例如:

12345678910111213141516... (until unlimited)

然后我想从给定的数字中得到一个数字。 例如:

  • 第10位: 1
  • 第17位: 3
  • ...

我试图使算法通过使用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);

您可以使用这样的事实,即总是有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
<?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;
}

演示: https //3v4l.org/3l0I7

算法:

要找到第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是位数),并将商得到3 3意味着它3过去起始编号100 ,我们可以说作为100 + 3 = 103 (因为它是100101102,然后将第四酮,为103)。

  • 现在,我们知道数字是103 剩下的就是找出103哪一位。

  • 请注意,有时我们遇到偶数除法的极端情况,例如12 /3。在这种情况下,我们从商中减去1,因为我们的3位数系列从100开始,而不是101(以此类推,对于其他数位依此类推,依此类推)。

找出数字:

  • 现在,我们知道第200位数字是103 (也就是我们上面计算的11 )。 为了找出哪一个,我们依次记下3位数字并仔细观察它们。

序列:

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
  • 如果您观察到,您可以理解最高的MSB数字遵循序列1,4,7,10,13等。第二高的MSB遵循序列2,5,8,11,14等,最后一个MSB(是LSB)遵循3、6、9、12、15等的顺序。

  • 因此,从上述序列来看,很明显, 11 (我们最初将200分解后得到)属于MSB数字第二大的序列。

  • 因此,来自103的最终答案是0 (从左数第二位)。

$num = '12345678910111213141516';

echo $num[16];

结果: 3

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM