簡體   English   中英

查找字符串中第 n 個數字的高效算法 112123123412345

[英]Efficient algorithm to find the n-th digit in the string 112123123412345

什么是在以下字符串中查找第 n 個位置的數字的有效算法

112123123412345123456 ... 123456789101112 ...

將整個字符串存儲在內存中對於非常大的 n 是不可行的,所以我正在尋找一種算法,可以找到上述字符串中的第 n 個數字,如果 n 非常大(即只生成前 n 個數字的替代方法)字符串)。

這里有幾個層次:數字是數字 x 的一部分,數字 x 是序列 1,2,3...x...y 的一部分,該序列是導致像 y 這樣有 z 個數字的數字。 我們將一一處理這些級別。

有 1 個數字的 9 個數字:

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  

有 2 個數字的 90 個數字:

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 ... 來找出數字在哪個數字,然后數字是什么。

好吧,您有一系列序列,每個序列增加一個數字。

如果你有“x”個,那么到那個點的序列占據x * (x + 1) / 2字符位置。 或者,另一種說法是“x”序列從x * (x - 1) / 2 (假設從零開始索引)。 這些被稱為三角數。

因此,您需要做的就是找到累積量最接近給定“n”的“x”值。 以下是三種方式:

  • 搜索一個封閉的解決方案。 這是存在的,但公式相當復雜。 是三角數之和的一個參考。)
  • 在內存中預先計算一個表,其中的值高達 1,000,000。 這將使您達到 10^10 個尺寸。
  • 使用“二進制”搜索和公式。 因此,生成 1、2、4、8 等的值序列,然后進行二分搜索以找到確切的序列。

一旦您知道值所在的序列,確定該值只是一個算術問題。

我們希望能夠搜索三種類型的結構,(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('')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM