我如何找到该系列中的百万号:2 3 4 6 9 13 19 28 42 63 ...?

时间:2010-05-15 15:11:40

标签: bash math recursion series

在我的comp中达到3000需要大约一分钟,但我需要知道系列中的第一百万个数字。该定义是递归的,所以我看不到任何快捷方式,除了计算百万分之前的所有内容。你如何快速计算系列中的百万号?

系列默认

n_{i+1} = \floor{ 3/2 * n_{i} }n_{0}=2

有趣的是,根据Google,只有一个网站列出了该系列:this one

Bash代码太慢

#!/bin/bash

function series 
{
        n=$( echo "3/2*$n" | bc -l | tr '\n' ' ' | sed -e 's@\\@@g' -e 's@ @@g' );
                                        # bc gives \ at very large numbers, sed-tr for it
        n=$( echo $n/1 | bc )           #DUMMY FLOOR func
}

n=2
nth=1

while [ true ]; #$nth -lt 500 ];
do
        series $n                        # n gets new value in the function through global value
        echo $nth $n
        nth=$( echo $nth + 1 | bc )     #n++
done

12 个答案:

答案 0 :(得分:19)

您可以通过考虑二进制问题轻松解决此问题。楼层(3/2 * i)基本上是右移,截断和添加。在伪代码中:

0b_n[0]   = 10              // the start is 2
0b_n[1]   = 10 + 1(0) = 11  // shift right, chop one bit off and add 
0b_n[i+1] = 0b_n[i] + Truncate(ShiftRight(0b_n[i]))

以任何形式实施都应该非常快。

我刚刚在Mathematica中实现了这个功能,看起来BitShiftRight操作也会超出单位位置,因此可以自动处理。这是一个班轮:

In[1] := Timing[num = Nest[(BitShiftRight[#] + #) &, 2, 999999];]
Out[2] = {16.6022, Null}

16秒,数字打印得很好,虽然很长:

In[2] := IntegerLength[num]
Out[2] = 176092

In[3] := num
Out[3] = 1963756...123087

完整结果here

答案 1 :(得分:13)

你几乎找到了它。下次,请查看Online Encyclopedia of Integer Series

这是条目:http://oeis.org/A061418

     FORMULA    

a(n) = ceiling[K*(3/2)^n] where K=1.08151366859...

The constant K is 2/3*K(3) (see A083286). - Ralf Stephan, May 29, 2003 

那说:

>>> def f(n):
...     K = 1.08151366859
...     return int(ceil(K*(1.5)**n))

完整性测试:

>>> for x in range(1, 10):
...     print f(x)
...     
2
3
4
6
9
13
19
28
42

真棒!现在怎么样1000000:

>>> f(1000000)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 3, in f
OverflowError: (34, 'Result too large')

好吧,我试过了。 :]但你明白了。

再次编辑:找到解决方案!请参阅TimoLasse V. Karlsen的答案。

编辑:使用Timo的移位理念:

import gmpy
n=gmpy.mpz(2)
for _ in xrange(10**6-1):
    n+=n>>1
print(n)

产量

1963756763 ... 226123087(176092位)

% time test.py > test.out

real    0m21.735s
user    0m21.709s
sys 0m0.024s

答案 2 :(得分:11)

您的脚本速度太慢的原因是,它会在{@ 1}}生成bc一次,tr一次,sed一次生成循环。

重写bc中的所有内容,并在最后执行sed。我的测试显示bc - 仅版本的速度超过600倍。在bc版本的旧慢速系统上花了不到16分钟才能找到第100,000个值(仅打印最后一个)。

另外,请注意您的“floor”功能实际上是“int”。

#!/usr/bin/bc -lq
define int(n) {
    auto oscale
    oscale = scale
    scale = 0
    n = n/1
    scale = oscale
    return n
}

define series(n) {
    return int(3/2*n)
}

n = 2
end = 1000
for (count = 1; count < end; count++ ) {
    n = series(n)
}
print count, "\t", n, "\n"
quit

请注意,print是一个扩展程序,bc的某些版本可能没有它。如果是这样,只需自己引用变量,并输出其值。

现在您可以执行chmod +x series.bc并将其称为:

./series.bc | tr -d '\n' | sed 's.\\..g'

答案 3 :(得分:6)

我使用了以下Java程序:

import java.math.*;

public class Series {
    public static void main(String[] args) {
        BigInteger num = BigInteger.valueOf(2);
        final int N = 1000000;

        long t = System.currentTimeMillis();
        for (int i = 1; i < N; i++) {
            num = num.shiftLeft(1).add(num).shiftRight(1);
        }
        System.out.println(System.currentTimeMillis() - t);
        System.out.println(num);
    }
}

输出,裁剪:(full output on pastebin

516380 (milliseconds)
196375676351034182442....29226123087

所以在我的普通机器上花了大约8.5分钟。我使用-Xmx128M,但不确定是否真的有必要。

可能有更好的算法,但总共需要10分钟,包括编写天真的实现和运行程序。

样品运行

答案 4 :(得分:5)

这是一个Python版本,在我10岁的笔记本电脑上运行大约220秒:

import math;
import timeit;

def code():
  n = 2
  nth = 1

  while nth < 1000000:
    n = (n * 3) >> 1
    nth = nth + 1

  print(n);

t = timeit.Timer(setup="from __main__ import code", stmt="code()");
print(t.timeit(1));

它产生与this answer在pastebin上相同的结果(也就是说,我验证了它的开始和结束,而不是所有内容。)

答案 5 :(得分:3)

嗯,bash不是我用于高速数字处理的东西。给自己一份GMP并整理一个C程序来完成它。

可能有一个数学公式可以快速给你,但是,在你弄清楚的时候,GMP可能会把结果扔给你: - )

答案 6 :(得分:2)

这在sequences站点中被识别为序列A061418(AKA“整数序列的在线百科全书”);每the relevant page

  

FORMULA a(n) =A061419(n)+1   = ceiling[K*(3/2)^n]其中K=1.08151366859 ...常数K是   2/3*K(3)(见A083286)。

并且使用合适的高精度库(已经建议的GMP,或MPIR,也可能是我的宝贝gmpy上面的包装器用于Python),您可以使用封闭形式的公式来加快计算速度“系列中的第一百万项”等。

通常可以将递归指定的重复放入已关闭的公式中。对于这个主题的广泛的初学者介绍,Concrete Mathematics(Graham,Knuth和Patashnik)真的很难被击败。

答案 7 :(得分:2)

使用更合适的语言可能会更接近,例如,Scheme:

(define (series n) (if (= n 0) 2
                       (quotient (* 3 (series (- n 1))) 2)))

这会在我的机器上计算约8秒内(series 100000)的17610位数。不幸的是,(series 1000000)仍然需要太长时间才能让更新/更快的机器在一分钟内完成任务。

使用大整数库(在本例中为NTL)切换到C ++:

#include <NTL/zz.h>
#include <fstream>
#include <time.h>
#include <iostream>

int main(int argc, char **argv) {
    NTL::ZZ sum;

    clock_t start = clock();
    for (int i=0; i<1000000; i++) 
        sum = (sum*3)/2;
    clock_t finish = clock();
    std::cout << "computation took: " << double(finish-start)/CLOCKS_PER_SEC << " seconds.\n";

    std::ofstream out("series_out.txt");
    out << sum;

    return 0;
}

这会在我的机器上计算4分35秒的1000000系列。这足够快,我可以几乎相信一台非常快的新机器至少可以在一分钟内接近完成(是的,我检查了当我使用轮班而不是乘法/除法时发生的事情 - 它慢了。

不幸的是,其他人建议的封闭式计算似乎没什么帮助。要使用它,您需要将常数K计算到足够的精度。我没有看到K的封闭式计算,所以这实际上只是将迭代转移到计算K,看起来计算K到足够的精度几乎(如果有的话)比计算原始系列更快。

答案 8 :(得分:2)

Pari中很容易做到:

n=2;for(k=1,10^6,n+=n>>1)

我的机器需要14秒。当然还有更快的方法 - 想到GMP - 但为什么要这么麻烦?您将无法在运行时间内停留超过10秒,开发时间将为分钟。 :)

小点:在原始公式中,是否需要第100个术语n 999999 或n 1000000 ,指数为100万;我给后者,因为我看到前者已经计算过了。

答案 9 :(得分:1)

这几乎是一阶递归关系,除了地板,它会让事情变得混乱。如果你不想发言,

http://en.wikipedia.org/wiki/Recurrence_relation

另外,不要使用bash。

答案 10 :(得分:0)

递归公式在大多数情况下需要相当长的时间 因为它必须维护机器堆栈的情况。为什么不使用动态 编程而不是?

即。 (伪代码)

bignum x = 2
for (int i = 1; i < 1000000; i++) {
    x = floor(3.0/2*x)
}

当然,要获得有意义的结果,您需要一个高精度数字库。

答案 11 :(得分:0)

我将蒂莫的想法转变为了精灵。它以100失败,给出负数。请失败,请参阅no BigNums

(progn
  (let ((a 2)
        (dummy 0))
    (while (< dummy 100)
      (setq a (+ a (lsh a -1)))
      (setq dummy (1+ dummy)))
    (message "%d" a)))
-211190189 #WRONG evalution