Pi中的数字运行


13

您的目标是输出pi(π)的连续相同数字的严格递增的序列。序列中的每个术语都必须比前一个数字长一位。因此3(pi的第0个数字)是第一次出现一连串数字(长度1)。接下来出现的是33(pi的数字24和25)。当然,此序列要求pi的数字以10

到目前为止已知的数字和前六个数字都出现在前800位数内:

3
33
111
9999
99999
999999
3333333
44444444
777777777
6666666666
... (not in first 2 billion digits)

请注意,连续的9个都在同一运行中同时出现,因此,如果您发现下一个较大的运行恰好是1000个连续0s,则将填充该序列的多个项。

我的程序没有找到更多的用语。我知道前50000位或更多位中没有更多的术语。我的程序花了50万个数字花了太长时间,所以我放弃了。

参考实施

你可以:

  • 永远输出序列
  • 取一个整数n并找到n序列中的第一个数字
  • 取一个整数,n然后n按pi 的第一个数字中的顺序查找数字。

确保指定您的代码执行的操作。该数字n可以是零或一个索引。

受此mathoverflow问题的启发。


1
相关 -9s的运行引起了许多答案的头痛:P
Mego

是否允许以空序列开始输出?
LegionMammal978 '16

2
同样,该序列的下一项似乎是3333333,其数字为10 ^ -710100至10 ^ -710106。n = 8 的值不会出现在前5000000个数字中。
LegionMammal978 '16

4
另外两个术语:数字10 ^ -22931745至10 ^ -22931752的44444444和数字10 ^ -24658601至10 ^ -24658609的777777777。n = 10 的值不会出现在前1亿个数字中。
LegionMammal978 '16

1
再一个学期:6666666666,在10 ^ -386980412。第11个术语不会出现在前2000000000000个数字中。
primo

Answers:


5

Mathematica,85个字节

FromDigits/@DeleteDuplicatesBy[Join@@Subsets/@Split@RealDigits[Pi,10,#][[1]],Length]&

匿名函数。以n为输入,并以π 的前n个数字返回序列的元素。输出形式为{0, 3, 33, 111, ...}


4

Python 2,110个字节

n=input()
x=p=7*n|1
while~-p:x=p/2*x/p+2*10**n;p-=2
l=m=0
for c in`x`:
 l=l*(p==c)+1;p=c
 if l>m:m=l;print p*l

要检查的最大位数取自stdin。使用PyPy 5.3约需2s即可完成10,000位数。

样品用量

$ echo 10000 | pypy pi-runs.py
3
33
111
9999
99999
999999

有用的东西

from sys import argv
from gmpy2 import mpz

def pibs(a, b):
  if a == b:
    if a == 0:
      return (1, 1, 1123)
    p = a*(a*(32*a-48)+22)-3
    q = a*a*a*24893568
    t = 21460*a+1123
    return (p, -q, p*t)
  m = (a+b) >> 1
  p1, q1, t1 = pibs(a, m)
  p2, q2, t2 = pibs(m+1, b)
  return (p1*p2, q1*q2, q2*t1 + p1*t2)

if __name__ == '__main__':
  from sys import argv
  digits = int(argv[1])

  pi_terms = mpz(digits*0.16975227728583067)
  p, q, t = pibs(0, pi_terms)

  z = mpz(10)**digits
  pi = 3528*q*z/t

  l=m=0
  x=0
  for c in str(pi):
   l=l*(p==c)+1;p=c
   if l>m:m=l;print x,p*l
   x+=1

为此,我已经从Chudnovsky切换到Ramanujan 39。Chudnovsky在1亿个数字后不久就耗尽了我的系统的内存,但是Ramanujan在仅约38分钟的时间内就达到了4亿个内存。我认为这是另一种情况,至少在资源有限的系统上,术语增长率会最终下降。

样品用量

$ python pi-ramanujan39-runs.py 400000000
0 3
25 33
155 111
765 9999
766 99999
767 999999
710106 3333333
22931752 44444444
24658609 777777777
386980421 6666666666

更快的无限生成器

问题描述中给出的参考实现很有趣。它使用了无界生成器,直接从论文《 Pi的数字的无界子弹算法》中获取。根据作者的说法,所提供的实现是“故意模糊的”,因此,我决定对作者列出的所有三种算法进行全新的实现,而无需进行故意的混淆。我还添加了第四个,基于Ramanujan#39

try:
  from gmpy2 import mpz
except:
  mpz = long

def g1_ref():
  # Leibniz/Euler, reference
  q, r, t = mpz(1), mpz(0), mpz(1)
  i, j = 1, 3
  while True:
    n = (q+r)/t
    if n*t > 4*q+r-t:
      yield n
      q, r = 10*q, 10*(r-n*t)
    q, r, t = q*i, (2*q+r)*j, t*j
    i += 1; j += 2

def g1_md():
  # Leibniz/Euler, multi-digit
  q, r, t = mpz(1), mpz(0), mpz(1)
  i, j = 1, 3
  z = mpz(10)**10
  while True:
    n = (q+r)/t
    if n*t > 4*q+r-t:
      for d in digits(n, i>34 and 10 or 1): yield d
      q, r = z*q, z*(r-n*t)
    u, v, x = 1, 0, 1
    for k in range(33):
      u, v, x = u*i, (2*u+v)*j, x*j
      i += 1; j += 2
    q, r, t = q*u, q*v+r*x, t*x

def g2_md():
  # Lambert, multi-digit
  q, r, s, t = mpz(0), mpz(4), mpz(1), mpz(0)
  i, j, k = 1, 1, 1
  z = mpz(10)**49
  while True:
    n = (q+r)/(s+t)
    if n == q/s:
      for d in digits(n, i>65 and 49 or 1): yield d
      q, r = z*(q-n*s), z*(r-n*t)
    u, v, w, x = 1, 0, 0, 1
    for l in range(64):
      u, v, w, x = u*j+v, u*k, w*j+x, w*k
      i += 1; j += 2; k += j
    q, r, s, t = q*u+r*w, q*v+r*x, s*u+t*w, s*v+t*x

def g3_ref():
  # Gosper, reference
  q, r, t = mpz(1), mpz(180), mpz(60)
  i = 2
  while True:
    u, y = i*(i*27+27)+6, (q+r)/t
    yield y
    q, r, t, i = 10*q*i*(2*i-1), 10*u*(q*(5*i-2)+r-y*t), t*u, i+1

def g3_md():
  # Gosper, multi-digit
  q, r, t = mpz(1), mpz(0), mpz(1)
  i, j = 1, 60
  z = mpz(10)**50
  while True:
    n = (q+r)/t
    if n*t > 6*i*q+r-t:
      for d in digits(n, i>38 and 50 or 1): yield d
      q, r = z*q, z*(r-n*t)
    u, v, x = 1, 0, 1
    for k in range(37):
      u, v, x = u*i*(2*i-1), j*(u*(5*i-2)+v), x*j
      i += 1; j += 54*i
    q, r, t = q*u, q*v+r*x, t*x

def g4_md():
  # Ramanujan 39, multi-digit
  q, r, s ,t = mpz(0), mpz(3528), mpz(1), mpz(0)
  i = 1
  z = mpz(10)**3511
  while True:
    n = (q+r)/(s+t)
    if n == (22583*i*q+r)/(22583*i*s+t):
      for d in digits(n, i>597 and 3511 or 1): yield d
      q, r = z*(q-n*s), z*(r-n*t)
    u, v, x = mpz(1), mpz(0), mpz(1)
    for k in range(596):
      c, d, f = i*(i*(i*32-48)+22)-3, 21460*i-20337, -i*i*i*24893568
      u, v, x = u*c, (u*d+v)*f, x*f
      i += 1
    q, r, s, t = q*u, q*v+r*x, s*u, s*v+t*x

def digits(x, n):
  o = []
  for k in range(n):
    x, r = divmod(x, 10)
    o.append(r)
  return reversed(o)

笔记

上面是6种实现:作者提供的两个参考实现(表示为_ref),以及四个批量计算项(一次生成多个数字_md)的参考实现。已确认所有实现均为100,000位数字。选择批次大小时,我选择的值会随着时间的流逝逐渐失去精度。例如,g1_md每批生成10位数字,并进行33次迭代。但是,这只会产生〜9.93个正确的数字。当精度用完时,检查条件将失败,从而触发要运行的额外批处理。这似乎比随着时间的流逝逐渐获得多余的,不必要的精度要好得多。

  • g1(Leibniz / Euler)保留
    一个额外的变量j,表示2*i+1。作者在参考实现中执行相同的操作。计算n分别就简单得多了(少晦涩),因为它使用的电流值qr并且t,而不是下一个。
  • g2(Lambert)
    这张支票n == q/s相当宽松。这应该阅读n == (q*(k+2*j+4)+r)/(s*(k+2*j+4)+t),这里j2*i-1ki*i。在较高的迭代次数下,rt项变得越来越不重要。照原样,这对前100,000位数字是有好处的,因此对所有人都可能有好处。作者未提供参考实现。
  • g3(Gosper)
    作者推测,没有必要检查n在随后的迭代中不会改变,并且仅用于减缓算法。虽然可能是正确的,但是生成器比当前生成的数字多保留了约13%的正确数字,这似乎有些浪费。我重新添加了检查,然后等到50位数字正确无误后,立即生成所有数字,并获得明显的性能提升。
  • g4(Ramanujan 39)
    计算为

    不幸的是,s由于初始(3528÷)组成,并没有归零,但是它仍然比g3快得多。收敛是每个术语〜5.89位,一次生成3511位。如果太多,那么每46次迭代生成271位数字也是一个不错的选择。

时机

在我的系统上,仅用于比较目的。时间以秒为单位列出。如果时间超过10分钟,则我没有再进行任何测试。

            |  g1_ref |  g1_md  |  g2_md  |  g3_ref |  g3_md  |  g4_md 
------------+---------+---------+---------+---------+---------+--------
    10,000  |  1.645  |  0.229  |  0.093  |  0.312  |  0.062  |  0.062 
    20,000  |  6.859  |  0.937  |  0.234  |  1.140  |  0.250  |  0.109 
    50,000  |  55.62  |  5.546  |  1.437  |  9.703  |  1.468  |  0.234 
   100,000  |  247.9  |  24.42  |  5.812  |  39.32  |  5.765  |  0.593 
   200,000  |  2,158  |  158.7  |  25.73  |  174.5  |  33.62  |  2.156 
   500,000  |    -    |  1,270  |  215.5  |  3,173  |  874.8  |  13.51 
 1,000,000  |    -    |    -    |  1,019  |    -    |    -    |  58.02 

有趣的是,尽管收敛速度较慢,但g2最终还是超过g3了。我怀疑这是因为操作数的增长速度明显降低,从长远来看会胜出。最快的g4_md实现比g3_ref500,000个数字上的实现快大约235倍。话虽如此,以这种方式流式传输数字仍然存在大量开销。使用Ramanujan 39(python source)直接计算所有数字的速度约为10倍。

为什么不使用Chudnovsky?

Chudnovsky算法需要一个全精度的平方根,老实说,我不确定该如何使用-假设根本不可能。拉马努詹39在这方面有些特殊。但是,该方法似乎确实有助于诸如y-cruncher使用的类似Machin的公式,因此这可能是一个值得探索的途径。


TIL Ideone支持Pypy。那么第二个程序是为速度而构建的吗?
mbomb007'7

@ mbomb007 “那么第二个程序是为速度而构建的?” 它是。我认为挑战和最快的代码一样有趣。
primo

相同。我考虑了两者。了解人们对使用其他标签重新发布的感觉。如果将其添加到OEIS(不包含此序列)中,则可能会更有用
mbomb007 '16

3

Haskell,231个字节

import Data.List
g(q,r,t,k,n,l)|4*q+r-t<n*t=n:g(10*q,10*(r-n*t),t,k,div(10*(3*q+r))t-10*n,l)|0<1=g(q*k,(2*q+r)*l,t*l,k+1,div(q*(7*k+2)+r*l)(t*l),l+2)
p=nubBy(\x y->length x==length y).concatMap inits.group$g(1,0,1,1,3,3) 

这使用了Jeremy Gibbons于2004年提出的Pi位数无界插销算法。结果为p。从技术上讲,它应该支持无限的输出序列,但是可能要花一些时间(并受您的内存限制)。


3

Python 2,298字节

注意,用于生成pi的代码取自OP的实现。

def p():
 q,r,t,j=1,180,60,2
 while 1:
  u,y=3*(3*j+1)*(3*j+2),(q*(27*j-12)+5*r)//(5*t)
  yield y
  q,r,t,j=10*q*j*(2*j-1),10*u*(q*(5*j-2)+r-y*t),t*u,j+1
p=p()
c=r=0
d=[0]
while 1:
 t=p.next()
 if t==d[len(d)-1]:d.append(t)
 else:d=[t]
 if len(d)>r:r=len(d);print"".join([`int(x)`for x in d])
 c+=1

我第一次尝试使用Python打高尔夫球。永远输出序列。


您能否π在这里解释您的计算方式?您当然可以计算pi,对吗?
R. Kap

现在无法测试,但是您不在π那里永远计算吗?
Yytsi '16

@TuukkaX不会出现,因为它有一个yield可以阻止它的但我对python不太好
Downgoat

Downgoat是正确的-它使用了generator函数
Mego

1
我编写了所有代码,除了以下p部分之外,我没有看过您的实现
-acrolith

3

蟒3.5,278个 263字节:

import decimal,re;decimal.getcontext().prec=int(input());D=decimal.Decimal;a=p=1;b,t=1/D(2).sqrt(),1/D(4)
for i in[1]*50:z=(a+b)/2;b=(a*b).sqrt();t-=p*(a-z)**2;a=z;p*=2;pi=(z*2)**2/(4*t);i=0;C=lambda r:re.search(r'(\d)\1{%s}'%r,str(pi))
while C(i):print(C(i));i+=1

n作为第一个n数字的输入π,然后输出这些第一个n数字的序列的成员。现在,它使用Python的内置十进制模块超越了Python的浮点数限制,然后将精度或epsilon设置为用户输入的任何值。然后,为了进行计算π,使用高效的Gausse-Legendre算法进行了50次迭代,因为该算法每次显然会使正确数字的数量加倍,因此,在50次迭代中,我们最多可以得到2^501,125,899,906,842,624纠正数字。最后,计算完成后,它在while循环中使用带字符串格式的正则表达式来查找和打印re 匹配对象(我希望这是可以的),使其比循环中上一次迭代中的所有连续重复数字长1位。

我能够使用此算法成功且准确地计算π多达10,000,000(一千万)个数字,这需要大约4个小时12分钟才能完成。以下是最终输出:

<_sre.SRE_Match object; span=(0, 1), match='3'>
<_sre.SRE_Match object; span=(25, 27), match='33'>
<_sre.SRE_Match object; span=(154, 157), match='111'>
<_sre.SRE_Match object; span=(763, 767), match='9999'>
<_sre.SRE_Match object; span=(763, 768), match='99999'>
<_sre.SRE_Match object; span=(763, 769), match='999999'>
<_sre.SRE_Match object; span=(710101, 710108), match='3333333'> 

因此,我可以自信地说,序列中的第8个数字甚至都不会出现在前1000万个数字之内!π是一个随机数...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.