这来自http://programmers.blogoverflow.com/2012/08/20-controversial-programming-opinions/
“鉴于可以使用函数4 *(1 – 1/3 + 1/5 – 1/7 +…)来估计Pi,更多的项可以提供更高的精度,因此编写一个函数可以计算出Pi的精度为小数点后5位。 ”
- 注意,必须通过计算上面给出的顺序来完成估算。
这来自http://programmers.blogoverflow.com/2012/08/20-controversial-programming-opinions/
“鉴于可以使用函数4 *(1 – 1/3 + 1/5 – 1/7 +…)来估计Pi,更多的项可以提供更高的精度,因此编写一个函数可以计算出Pi的精度为小数点后5位。 ”
Answers:
ES6更新:事实证明,五年过去了,我们还有更多可用的功能。
let f=(i=0,a=0)=>i>1e6?a:f(i+4,a+8/-~i/(i+3))
理论上,此版本(45字节;是,let
必需)在ES6严格模式下工作。实际上,您可以使用V在V8中运行它(例如,带节点)--use-strict --harmony-tailcalls
。a,适当的语音提示功能尚未广泛实施。但是,它是特定的行为,所以应该没问题。
如果我们要坚持广泛实现的内容,并且不需要严格模式,则可以简单地对功能使用ES6胖箭头语法,但可以保留48字节的成本,与以前相同(由Brian H建议)。
a=>{for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}
名称为单参数的选择不确实的事,但我们不妨挑我们使用,以减少全球范围的污染的名称之一。
function(){for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}
这个版本是一个函数表达式;f
如果要命名,请添加两个字符(例如“ ”)。这个版本破坏了全局a
和i
; 如果我们a,i
在参数列表中添加“ ”,则可以避免这种情况。
为了避免减法,使用了算法的重新编制版本。
1/1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + ...
(3/3 - 1/3) + (7/35 - 5/35) + (11/99 - 9/99) + ...
2/3 + 2/35 + 2/99 + ...
2/(1*3) + 2/(5*7) + 2/(9*11) + ...
这是未经调整的“普通”版本:
function(){for(a=0,i=1;i<1e6;i+=2)a+=[,4,,-4][i%4]/i;return a}
时钟为64 62个字符。
感谢@ardnew提出的摆脱4*
之前的建议return
。
function(){for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a} // got rid of `i+=4`; restructured
// Old versions below.
function(){for(a=0,i=1;i<1e6;i+=4)a+=8/i/-~-~i;return a} // got rid of `4*`
function(){for(a=0,i=1;i<1e6;i+=4)a+=2/i/-~-~i;return 4*a}
a+=2/i/-~-~i;return 4*a
来a+=8/i/-~-~i;return a
print reduce(lambda x,p:p/2*x/p+2*10**999,range(6637,1,-2))
打印出1000位数;略大于所需的5。它不使用规定的迭代,而是使用以下代码:
pi = 2 + 1/3*(2 + 2/5*(2 + 3/7*(2 + 4/9*(2 + 5/11*(2 + ...)))))
的6637
(最里面的分母)可配制成:
数字* 2 *日志2(10)
这意味着线性收敛。每次更深的迭代将产生pi的另一个二进制位。
但是,如果您坚持使用 tan -1身份,那么即使您不介意解决该问题也可以实现类似的收敛。看一下部分和:
4.0,2.66667,3.46667,2.89524,3.33968,2.97605,3.28374,...
显然,每一项都来回跳到收敛点的任一侧;该系列具有交替收敛。此外,每一项比上一项更接近收敛点;就其收敛点而言,它绝对是单调的。这两个属性的组合暗示着,任何两个相邻项的算术平均值均比任一项本身更接近收敛点。为了让您更好地理解我的意思,请考虑以下图像:
外部序列是原始序列,内部序列是通过取每个相邻项的平均值来找到的。明显的不同。但是真正引人注目的是,这个新系列还具有交替收敛性,并且就其收敛点而言绝对是单调的。这意味着该过程可以一遍又一遍地应用于恶心。
好。但是如何?
一些正式的定义。令P 1(n)为第一个序列的第n 个项,P 2(n)为第二个序列的第 n 个项,类似地,P k(n)为第 k 个序列的第 n 个项,如上所述。
P 1 = [P 1(1),P 1(2),P 1(3),P 1(4),P 1(5),...]
P 2 = [(P 1(1)+ P 1(2))/ 2,(P 1(2)+ P 1(3))/ 2,(P 1(3)+ P 1(4))/ 2,(P 1(4)+ P 1(5))/ 2,...]
P 3 = [(P 1(1)+ 2P 1(2)+ P 1(3))/ 4,(P 1(2)+ 2P 1(3)+ P 1(4))/ 4,(P 1(3)+ 2P 1(4)+ P 1(5))/ 4,...]
P 4 = [(P 1(1)+ 3P 1(2)+ 3P 1(3)+ P 1(4))/ 8,(P 1(2)+ 3P 1(3)+ 3P 1(4) + P 1(5))/ 8,...]
毫不奇怪,这些系数正好遵循二项式系数,并且可以表示为Pascal三角形的单行。由于任意一列Pascal三角形的计算都是微不足道的,因此只需取前n个部分和,将每个n和乘以Pascal三角形的第 k行中的相应项,然后除以2,就可以找到任意“深”系列。K-1。
这样,仅需36次迭代就可以实现完整的32位浮点精度(〜14个小数位),此时部分和甚至没有收敛到第二个小数位。这显然不是打高尔夫球的:
# used for pascal's triangle
t = 36; v = 1.0/(1<<t-1); e = 1
# used for the partial sums of pi
p = 4; d = 3; s = -4.0
x = 0
while t:
t -= 1
p += s/d; d += 2; s *= -1
x += p*v
v = v*t/e; e += 1
print "%.14f"%x
如果您想要任意精度,只需稍作修改即可实现。在这里再次计算1000位数字:
# used for pascal's triangle
f = t = 3318; v = 1; e = 1
# used for the partial sums of pi
p = 4096*10**999; d = 3; s = -p
x = 0
while t:
t -= 1
p += s/d; d += 2; s *= -1
x += p*v
v = v*t/e; e += 1
print x>>f+9
p的初始值开始大2 10,以抵消s / d的整数除法效应,因为d变大,导致最后几位数字不收敛。再次注意这里3318
也是:
数字*日志2(10)
迭代次数与第一种算法相同(减半,因为t每次迭代减少1而不是2)。再次表明这是线性收敛:每次迭代pi的一个二进制位。在这两种情况下,都需要进行3318次迭代才能计算pi的 1000位数,这比100万次迭代计算5的配额更好。
4 * sum(1/(1+i*2) if not i%2 else -1/(1+i*2) for i in xrange(places*10**(places)))
P_1 = ..., P_2 = ..., P_3 = ..., P_4 = ...
,“ ...将每个数乘以kth
Pascal三角形行中的相应项,然后除以2^{k-1}
。”,而不是nth
行和2^{n-1}
?。
阿基米德的方法 26个字符
N@#*Sin[180 Degree/#]&
当输入为822时,达到标准。
问题:有人知道他是如何计算180度正弦的吗?我不。
莱布尼兹的方法(格雷戈里的系列)32个字符
这是问题构成者提供的示例相同的功能。它在大约一半的迭代中达到了标准。
N@4Sum[(-1)^k/(2k+1),{k,0,10^6}]
Madhava-Leibniz方法 37个字符
这个变体使用了更多的字符,但仅9次迭代就收敛到标准!
N@Sqrt@12 Sum[(-1/3)^k/(2k+1),{k,0,9}]
float r(){float p=0,s=4,i=1E6f;while(--i>0)p+=(s=-s)/i--;return p;}
请注意,这可以通过按正确的顺序将数字相加来避免重要性下降。
while(--i>0)
为while(i--)
并保存2个字符
float p(i){return i<1E6?4./++i-p(++i):0;}
那是41个字符,但是还必须进行编译-O2
才能获得优化器,以消除尾部递归。这也依赖于++
执行顺序的不确定行为。感谢ugoren指出这一点。我已经在64位Linux下使用gcc 4.4.3进行了测试。
请注意,除非优化程序也对总和进行重新排序,否则它将从最小的数开始进行加法运算,因此避免了重要性的损失。
呼叫为p()
。
q()
不是p()
。而且我不认为-O2
应该算(但是如果您算了算,由于需要的空间,它是4个字符)。
p(0)
。3.保存一个字符return++i...
。4.两个++i
使行为不确定。
q
-这将教会我重命名后再次检查。我认为我按照正常的惯例-O2
将3个字符计数,但是如果需要,我们可以在metas上打开它。meta.codegolf.stackexchange.com/questions/19是我能找到的唯一相关讨论。我添加了我正在使用的gcc版本,该版本允许我将其称为p()
。保存字符将停止优化程序并给出段错误。根据meta.codegolf.stackexchange.com/questions/21
p()
-您确定p()
在任何情况下都可以拨打电话吗?还是仅仅是测试堆栈中发生了什么?
p()
vs 生成了稍微不同的代码p(0)
,但是我不知道它记录了什么行为,而且我不是C程序员。
J,26个字符
+ / + / _ 2((4 _4)&%)>:+:i.100
从100个序列项目移至1e6个项目。现在它也是一个代码标记,可以从浏览器复制粘贴到控制台而不会出现错误。
+/+/_2((4 _4)&%)\>:+:i.1e6
-/4%>:2*i.1e6
-13个字符。(由于#jsoftware中的b_jonas使我意识到-/
可以计算具有交替符号的和。[这是因为J中的所有运算符都具有相同的优先级并且是右关联的,所以-/ 1 2 3 4
<=> 1 - (2 - (3 - 4))
<=> 1 - 2 + 3 - 4
。))
def f(n,k=n)k>0?(f(n,k-1)+f(n+1,k-1))/2:n<0?0:f(n-1,0)+(-1)**n/(2*n+1.0)end;4*f(9)
试试看:https : //repl.it/LQ8w
该方法通过数值加速方法间接使用给定的序列。结果输出是
pi ≈ 3.14159265161
与
pi = 3.14159265359
它开始于
f(n,0) = 1/1 - 1/3 + 1/5 - ... + ((-1)**n)/(2*n+1)
然后,由于这是交替的,我们可以使用
f(n,1) = (f(n,0) + f(n+1,0))/2
并反复应用:
f(n,k) = (f(n,k-1) + f(n+1,k-1))/2
为简单起见,f(n) = f(n,n)
。
如果您不介意长时间运行,则可以使用
def f(n)n<0?0:f(n-1)+(-1)**n/(2*n+1.0)end;4*f(1e7)
要么
a=0;for k in 0..1e7 do a+=(-1)**k/(2*k+1.0)end;4*a
float p,b;void main(a){b++<9e6?p+=a/b++,main(-a):printf("%f\n",4*p);}
a
被初始化为1)。void main
是奇怪且非标准的,但可以使事情正常。没有它,递归将被实现为真正的调用,从而导致堆栈溢出。另一种方法是添加return
。4*
如果使用三个命令行参数运行,则可以保存两个字符。int main(a)
甚至main(a)
GCC仅发出警告。void main
无论如何,它都会发出警告,甚至可能是因为您只有一个参数main
。
(fn [](* 4(apply +(map #(*(Math/pow -1 %1)(/ 1.0(+ 1 %1 %1)))(range 377000)))))
这将创建一个没有参数的函数,该函数将计算一个将pi正确近似为五个小数位的浮点数。请注意,这不会将函数绑定到诸如的名称pi
,因此,必须使用eval
as 来对代码进行评估,(<code>)
或者将解决方案绑定至名称,在这种情况下,解决方案是
(defn p[](* 4(apply +(map #(*(Math/pow -1 %1)(/ 1.0(+ 1 %1 %1)))(range 377000)))))
为82个字符
(defn nth-term-of-pi [n] (* (Math/pow -1 n) (/ 1.0 (+ 1 n n))))
(defn pi [c] (* 4 (apply + (map nth-term-of-pi (range c)))))
(def pi-accuracy-constant (loop [c 1000] (if (< (pi c) 3.14159) (recur (inc c)) c)))
; (pi pi-accuracy-constant) is then the value of pi to the accuracy of five decimal places
<?for($j=$i=-1;1e6>$j;){$p+=($i=-$i)/($j+=2);}echo$p*4;
我不知道在不违反算法规则的情况下可以缩小尺寸。
<?for(;1e6>$j;)$p+=($i=-$i|4)/~-$j+=2;echo$p;
嗯,我的python-fu不够强大。我再也看不到捷径了,但是也许经验丰富的高尔夫球手可以在这里找到一些合适的东西?
t=s=0
k=i=1
while t<1e6:t,s,i,k=t+1,k*4./i+s,i+2,-k
4.
-> 4
)保存一个字节。在其他新闻中,我刚刚发现一个案例,其中Python 3在代码高尔夫方面实际上胜过Python 2!
红宝石-54个字符
def a()p=0;1000000.times{|i|p+=8/(4*i*(4*i+2))};p;end;
我第一次尝试在控制台上
def a()i=1;p=0;while i<2**100 do p+=8/(i*(i+2));i+=4;end;p;end;
63个字符。
def a;
代替来保存一个字节def a()
。
$y=1e4;for$x(0..1e4-1){$y--while sqrt($x**2+$y**2)>1e4;$a+=$y}print 4*$a/1e8
(结果:3.14159052)
不是最短的解决方案,但也许很有趣。这是一个几何图形。我计算一个圆圈下的面积。
我有另一种有趣的方法,但是它确实很慢。它计算四分之一圆以下的正方形中离散点的数量,并从中计算出pi:
$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2
它期望将迭代次数作为命令行参数。在这里,您可以看到运行时间与准确性的关系。;)
$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 100
3.1796
real 0m0.011s
user 0m0.005s
sys 0m0.003s
$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 1000
3.14552
real 0m0.354s
user 0m0.340s
sys 0m0.004s
$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 10000
3.14199016
real 0m34.941s
user 0m33.757s
sys 0m0.097s
DECLARE @B int=3, @A varchar(max), @C varchar(max)='1'
WHILE @B<100000
BEGIN
SELECT @C=@C+(select case when (@B-1)%4=0 then'+'else'-'end)+
(SELECT cast(cast(1.0/@B as decimal(9,8)) as varchar(max)))
SELECT @B=@B+2
END
EXECUTE('SELECT 4*('+@C+')')
我会提供一个SQL Fiddle,但这会导致很多循环深入查找1/3 1/5 1/7等分数,并给出错误大声笑。但是,如果更改@B<100000
为,1000
那么它将运行(显然精度不相同)。
p=lambda:3.14159