在我的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
答案 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。
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')
好吧,我试过了。 :]
但你明白了。
再次编辑:找到解决方案!请参阅Timo或Lasse 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分钟,包括编写天真的实现和运行程序。
N=500
(同意OEIS 061418)N=100K
(1445232038814....522773508
)答案 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)
答案 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