2015-11-07 32 views
-1

我在编码相当缺乏经验,但我已成功地写:我怎么能优化这个算法近似pi?

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace PiApprox 
{ 

    public class PiApprox 
    { 
     //side of the rectangle 
     public static int s; 

     //radius of the circle 
     public static int r; 

     //indexers of the coordinates, points 
     public static int ix; 
     public static int iy; 

     //current y 
     public static decimal cury; 
     //without rounding 
     public static decimal wcury; 

     //amounts of points relative to the circle 
     public static decimal inAmount; 
     public static decimal onAmount; 
     public static decimal outAmount; 

     //amount of all points 
     public static decimal allAmount; 

     //short for inAmount and on onAmount, 
     //used to make the calculations clearer in the final part 
     public static decimal inanon; 

     //final result, crude approximation of pi 
     public static decimal piApprox; 


     public static void Main() 
     { 
      while (true) 
      { 
       Calculate(); 
      } 
     } 

     public static void Calculate() 
     { 
      s = Convert.ToInt32(Console.ReadLine()); 


      //calculate the radius of the circle 
      r = s/2; 

      //calculate the total amount of points in the grid 
      //rectangle area 
      allAmount = (decimal) Math.Pow(s, 2); 

      //reset values 
      inAmount = 0; 
      onAmount = 0; 
      outAmount = 0; 

      //main loop 
      //iterate for y, from up to down 
      for (ix = -r; ix <= 0; ix++) 
      { 
       wcury = (decimal) Math.Sqrt(Math.Pow(r, 2) - Math.Pow(ix, 2)); 
       cury = Math.Floor(wcury); 


       outAmount += r - (int)cury; 

       if (wcury == cury) 
       { 
        onAmount++; 
       } 

       if (wcury == cury) 
       { 
        inAmount += (int)cury; 
       } 
       else 
       { 
        inAmount += (int)cury + 1; 
       } 
       Result(); 
      } 

      Result(); 
     } 

     public static void Result() 
     { 
      //total amount of points 
      inanon = 4 * (onAmount + inAmount - (r + 1)) + 1; 

      //proportion 
      piApprox = 4 * (inanon/allAmount); 

      Console.SetCursorPosition(1, 0); 
      Console.WriteLine(piApprox); 
     } 
    } 

} 

蒙特卡罗原理很简单;我计算了y值 的绘图f(x)= sqrt(r^2 - ix^2),它们代表了圆的第一个四分之一。然后我计算圆内的点并在最后输出。线上的乘法piApprox = 4 *(inanon/allAmount); (pi * r^2)/((2r)^ 2) - > (pi * r^2)/(4 * r^2) - > pi/4

有什么我可以做的,以加快计算?

+0

如果您有很多事情可以加快代码的执行速度,尤其是如果您对编程不熟悉并且没有努力加速代码的话,我不会感到惊讶。缺乏具体的问题陈述,这个问题对于Stack Overflow来说太广泛了。它可能适用于codereview.stackexchange.com;你可以尝试在那里发布。如果你想在这里得到答案,你需要解释你试图加快代码的速度,你如何衡量性能,你试图满足什么特定的,可衡量的性能目标,以及为什么你认为它可以被满足。 –

+0

为什么在循环中调用Result()'_every time_? 'WriteLine''每次都需要花费大量的时间,所以随着点数的增加而减少输出的速率 – 2015-11-07 01:12:08

+0

这不是Monte-Carlo,这个算法没有任何随机性。这只是使用最基本的积分方法在区间'[0,r]'上积分曲线'sqrt(r * r-x * x)'。探索Simpson方法或Romberg方法以获得更好的集成精度。 – LutzL

回答

1

我假设你是C#的新手,所以我只给你一些提示。

几件事情有潜力改善:

  • decimal很慢:它利用软件计算。另一方面,关于int,double等的计算以硬件实现。在这里使用int,反正你不使用小数部分。
  • Math.Pow很慢。请勿将其用于平方:将Math.Pow(x, 2)替换为x * x
  • Math.Sqrt很慢。而不是比较Math.Sqrt(x)y,而是将x改为y * y。或者只是在最后调用一次。
  • Math.Floor :)
  • 你可以使用并行利用多核CPU
  • 你应该使用局部变量,因为它们是优化更容易

请记住,当我的意思是这是相对。所有这些操作在绝对意义上都非常快 - 我只是说你可以使用更快的选择。

但有一件事情是痛得很慢(这对人类来说是显而易见的):Console。它在Windows 10上变得更好,但它仍然很慢,并且在代码的热路径中使用控制台。摆脱这些中间结果。

还有一件事,如果你在一个部门使用int,你会在C#中得到一个int。如果你想得到小数部分(如(double)x/y),你需要在分割之前将一个操作数转换为double

+0

谢谢你的洞察力。你推荐使用什么而不是地板?如果我的工作完成百分比增量为5,而不是每个x的计算值,会更好吗? – Lupilum

+0

我不能用int来进行最终计算,但float只能精确到7位数。 – Lupilum

+0

然后我使用底板来确定y是否在特定点上。 – Lupilum