找到字符串112123123412345中第n个数字的高效算法

时间:2017-09-10 13:36:47

标签: algorithm

在以下字符串中找到第n个位置的数字的有效算法是什么

112123123412345123456 ... 123456789101112 ...

将整个字符串存储在内存中对于非常大的n是不可行的,所以我正在寻找一种能够在上面的字符串中找到第n个数字的算法,如果n非常大(即只是生成第一个数字的替代方法) n个数字的字符串)。

3 个答案:

答案 0 :(得分:3)

这里有几个级别:数字是数字x的一部分,数字x是序列1,2,3 ... x ... y的一部分,该序列是序列块的一部分导致y等具有z数字的数字。我们将逐一解决这些问题。

有9个数字,1位数:

first: 1 (sequence length: 1 * 1)  
last: 9 (sequence length: 9 * 1)  
average sequence length: (1 + 9) / 2 = 5  
1-digit block length: 9 * 5 = 45  

有90个数字,2位数字:

first: 10 (sequence length: 9 * 1 + 1 * 2)  
last: 99 (sequence length: 9 * 1 + 90 * 2)  
average sequence length: 9 + (2 + 180) / 2 = 100  
2-digit block length: 90 * 100 = 9000  

有900个数字,包含3位数字:

first: 100 (sequence length: 9 * 1 + 90 * 2 + 1 * 3)  
last: 999 (sequence length: 9 * 1 + 90 * 2 + 900 * 3)  
average sequence length: 9 + 180 + (3 + 2,700) / 2 = 1,540.5  
3-digit block length: 900 * 1,540.5 = 1,386,450  

如果您继续计算这些值,您将找到您要查找的数字位于哪个数据块(最多可包含多少位数),并且您将知道开始和这个街区的终点。

说你想要百万分之一的数字。您发现它位于3位数字块中,并且该块位于以下总序列中:

start of 3-digit block: 45 + 9,000 + = 9,045  
start of 4-digit block: 45 + 9,000 + 1,386,450 = 1,395,495  

所以在这个区块中我们正在寻找数字:

1,000,000 - 9,045 = 990,955  

现在您可以使用例如二进制搜索,找出第990,955位数字所在的序列;你从3位数字块中途的3位数字开始:

first: 100 (sequence length: 9 + 180 + 1 * 3)  
number: 550 (sequence length: 9 + 180 + 550 * 3)  
average sequence length: 9 + 180 + (3 + 1650) / 2 = 1,015.5  
total sequence length: 550 * 1,015.5 = 558,525  

哪个太小了;所以我们尝试550 * 3/4 = 825,看看它是太小还是大,然后以越来越小的步长向上或向下移动,直到我们知道990,995位数在哪个序列。

在数字n的序列中说出来;然后我们计算直到n-1的所有3位数序列的总长度,这将给出我们在数字n的序列中寻找的数字的位置。然后我们可以使用数字9 * 1,90 * 2,900 * 3 ...来找出数字所在的数字,然后是数字是什么。

答案 1 :(得分:0)

嗯,你有一系列序列,每个序列都增加一个数字。

如果你有" x"其中,直到那一点的序列占据x * (x + 1) / 2个字符位置。或者,另一种说法是" x" s序列从x * (x - 1) / 2开始(假设从零开始索引)。这些被称为三角数。

所以,你需要做的就是找到" x"累积金额最接近给定" n"的值。这有三种方式:

  • 从解决方案中搜索已关闭的内容。这存在,但公式相当复杂。 (Here是三角数之和的一个参考。)
  • 预先计算内存中的表,其值最多为1,000,000。这将使你达到10 ^ 10尺寸。
  • 使用"二进制"搜索和公式。因此,生成1,2,4,8等值的序列,然后进行二分查找以找到确切的序列。

一旦知道值所在的序列,确定该值只是算术问题。

答案 2 :(得分:0)

我们希望能够搜索三种类型的结构,(1)连接d位数字的序列,例如一位数字:

123456...

或3位数:

100101102103

(2)节中的行, 其中每个部分都基于添加到前缀的上一部分。例如,第1节:

1
12
123
...

或第3节:

1234...10111213...100
1234...10111213...100102
1234...10111213...100102103
<----- prefix ----->

和(3)完整的部分,尽管我们可以枚举完整的部分,因为它们呈指数增长,并有助于构建我们的部分前缀。对于(1),如果我们知道位数,则可以使用简单的除法;对于(2),我们可以进行二进制搜索。

这里的Python代码也可以回答那些大问题:

def getGreatest(n, d, prefix):
  rows = 9 * 10**(d - 1)
  triangle = rows * (d + rows * d) // 2
  l = 0
  r = triangle
 
  while l < r:
    mid = l + ((r - l) >> 1)
    triangle = mid * prefix + mid * (d + mid * d) // 2
    prevTriangle = (mid-1) * prefix + (mid-1) * (d + (mid-1) * d) // 2
    nextTriangle = (mid+1) * prefix + (mid+1) * (d + (mid+1) * d) // 2
 
    if triangle >= n:
      if prevTriangle < n:
        return prevTriangle
      else:
        r = mid - 1
    else:
      if nextTriangle >= n:
        return triangle
      else:
        l = mid
 
  return l * prefix + l * (d + l * d) // 2
 
def solve(n):
  debug = 1
  d = 0
  p = 0.1
  prefixes = [0]
  sections = [0]
 
  while sections[d] < n:
    d += 1
    p *= 10
    rows = int(9 * p)
    triangle = rows * (d + rows * d) // 2
    section = rows * prefixes[d-1] + triangle
    sections.append(sections[d-1] + section)
    prefixes.append(prefixes[d-1] + rows * d)
 
  section = sections[d - 1]
 
  if debug:
    print("section: %s" % section)
 
  n = n - section
  rows = getGreatest(n, d, prefixes[d - 1])
 
  if debug:
    print("rows: %s" % rows)
 
  n = n - rows
 
  d = 1
 
  while prefixes[d] < n:
    d += 1;
 
  if prefixes[d] == n:
    return 9;
 
  prefix = prefixes[d - 1]
 
  if debug:
    print("prefix: %s" % prefix)
 
  n -= prefix
 
  if debug:
    print((n, d, prefixes, sections))
 
  countDDigitNums = n // d
  remainder = n % d
 
  prev = 10**(d - 1) - 1
  num = prev + countDDigitNums
 
  if debug:
    print("num: %s" % num)
 
  if remainder:
    return int(str(num + 1)[remainder - 1])
  else:
    s = str(num);
    return int(s[len(s) - 1])
 
ns = [
  1, # 1
  2, # 1
  3, # 2
  100, # 1
  2100, # 2
  31000, # 2
  999999999999999999, # 4
  1000000000000000000, # 1
  999999999999999993, # 7
]
 
for n in ns:
  print(n)
  print(solve(n))
  print('')