为什么这个平方根近似不起作用?

时间:2018-05-15 13:26:28

标签: c#

该程序基本上找到给定数字的平方根的近似值。我无法看到问题,为什么它不起作用。该程序正在编译但从未运行。它无限期地计算某些东西。

    public static void Main(string[] args)
    {
        Console.WriteLine("Enter number to find Square Root");
        var num = Convert.ToInt32(Console.ReadLine());

        var ans = SqaureRoot(num);
        Console.WriteLine("Square root of {0} : {1}",num,ans);

    }

问题显然必须在这个方法中,在我看来,代码不会从while循环中退出,我只是不明白为什么。必须使用Newton Raphson方法解决此问题仅接近平方根。可能是因为牛顿拉夫森方程没有括号吗?

    public static double SqaureRoot(double a)
    {
        if (a < 0)
            throw new Exception("Can not sqrt a negative number");
        double error = 0.00001;
        double x = 1;
        while (true)
        {
            double val = x * x;
            if (Math.Abs(a) <= error)
                return x;
            x = x / 2 + a / (2 * x);
        }

    }

3 个答案:

答案 0 :(得分:23)

  

我无法看到问题,为什么它不起作用。 ...代码没有退出while循环,我只是不明白为什么。

没错,你无法看到它。我一直在冰箱里看着牛奶,我看不到牛奶。你有冰箱盲,但编码。你正在直视一个明显的缺陷而你却看不到它。

这个问题在初学者和有经验的程序员中非常普遍。

它在人类中也很常见。你倾向于阅读你的想法,即使它不是:

           /\
          /I \
         /LOVE\
        /PARIS \
       / IN THE \
      /THE SPRING\
     --------------

他们第一次阅读时,大多数人都会读到&#34; 我在春天爱巴黎&#34;但并不是它所说的 EM>

您正在分析的程序是有效的程序,但该程序只存在于您的脑海中。你必须分析你实际编写并实际运行的程序!这基本上是一种确认偏见的形式 - 倾向于观察证据,证明代码是正确的,并且没有看到与之相矛盾的证据。

经验丰富的开发人员每天都会使用许多技术来摆脱代码盲目性并找到缺陷。

您正在使用的技术是让新人眼睛看待问题。这项技术有效;我只需要看一下您的代码就可以看到明显的问题,因为我正在阅读您编写的代码,并且您正在阅读代码中没有&#39;存在你认为你写的

但这是一项糟糕的技巧,因为它浪费了其他人的时间来解决你可以学会解决的琐碎问题。 今天将是学习如何自己解决这些问题的好日子。

第一种技术是学习如何使用调试器

您要做的是逐步调试调试器中的代码,一次一个语句,并在每个语句上预测该语句将执行的操作。 您必须在执行声明之前进行预测。然后执行声明,看看你的预测是否成真。最终,您会做出错误的预测,这是您理解程序错误的重点。

这也适用于我们的视错觉。如果你告诉别人读一个单词,暂停并阅读下一个单词,就会很容易看到错误。

下一项技术称为 rubber duck debugging

找一个橡皮鸭或研究生或其他可以与之交谈的对象,大声解释你的程序的每一行 - 最好是在调试时 - 并过分地给出详细解释为什么这条线是正确的。当你到达一条线路时,你无法证明这一点,要么你不了解你自己的程序,要么就是错误的。

你会觉得自己像一个白痴大声地对着橡皮鸭说话,但是让它发挥作用的一部分就是让你的大脑负责说话。再次,这对我们的错觉起作用:如果你读出每个单词并大声说出句子,而不是阅读整个句子然后说出整个句子,那么错误就会变得明显。

特别是,非常仔细地解释每个变量的用途和用法。在你的情况下,单独会很快找到问题,因为你有一个声明和写入但从未读过的变量。写入但从未读过的变量是巨大的红旗;这意味着你在你的机器中有一个无用的部分,或者更有可能的是,你有一个不被误用的关键部分。就像这里的情况一样。

在您的特定情况下,有一些特殊技术。

  • 你的直觉给了你两个很大的暗示:问题是一个无限循环,并且&#34;可能是因为牛顿拉夫森方程没有括号?&#34;第一个直觉就是现场。第二种直觉对我来说毫无意义,我不知道你在谈论什么。但无论如何,都要追平那些预感。

  • 您已将问题正确诊断为无限循环,因此请特别关注循环条件时关注您的注意力。是否有意义? (提示:不。)再次,大声朗读。它会有所帮助。

  • 您正在编写标准算法的代码版本。回到您对算法的描述,并验证算法中的每一步是否在代码中的某处找到。原始算法中的操作不在您的程序中,而且是错误所在的位置。

  • 重命名变量,使其名称更清晰地代表其概念。 error错了;这不是错误。这是errorTolerancex应为approximation。该错误由currentError = approximation * approximation - a;计算如果您查看自己的计划并问自己&#34; errorTolerancecurrentError相比在哪里?你很容易找到你的错误。如果你这样做,你会写出更少的愚蠢错误

  • 一概而论。很难看到您的错误,因为您没有阅读代码,而您正在阅读其预期含义。你看一下代码,你看到的是:

SOLVER
{
    VALIDATE ARGUMENT
    INITIALIZE APPROXIMATION
    while (true)
    {
      if (WITHIN TOLERANCES)
        RETURN APPROXIMATION
      REFINE APPROXIMATION
    }
}

代码是正确的。您查看if(Abs...并查看&#34;我正在检查我是否在公差范围内,并且这是执行此步骤的正确位置,而您我甚至不会思考&#34;但我是否正确地实施了它?&#34;

但是,既然你希望你的代码能够采用这种结构,那么你实际上就可以这样写

class Solver
{
  private double a;
  private double approximation;

  private Solver(double a) {
    this.a = a;
  }

  public static double Solve(double a)
  {
    Solver s = new Solver(a);
    s.Validate();
    s.Initialize();
    while (true) {
      if (s.WithinTolerances())
        return s.approximation;
      s.Refine();
    }
  }

  private void Validate() { ... }
  private void Initialize() { ... }
  private bool WithinTolerances() { ... }
  private void Refine() { ... }
}

当你有一个一件事并且非常好的方法时,你不太可能犯错误,并且奖金如果该方法可以独立测试,请指出。

请注意,上面我刚刚创建的类可能是一个抽象基类!我们可以拥有一整套求解器,使用不同的技术解决不同的问题。

  • 您的代码一般都很草率。 SqaureRoot代替SquareRootException代替ArgumentException。等等。别邋.. 养成制作每一行代码的习惯,你写的是一个清晰和精确的模型。这将使你更容易找到你不可避免的错误,因为你不必躲过一个小错误的海洋其中一些可能会掩盖更大的错误。

这些技术中的任何一种都可以快速找到您的错误。 立即了解所有这些

进一步阅读:

答案 1 :(得分:4)

if (Math.Abs(a) <= error)
    return x;

a在循环内部没有变化,因此永远不会返回。

正如评论中所述,您应该与错误字词Math.Abs(a-val)进行比较,而不是Math.Abs(a),即初始值。

答案 2 :(得分:0)

正如其他人指出的那样,你永远不会满足return的条件。 考虑改变这个:

if (Math.Abs(a) <= error)

if (Math.Abs(a - val) <= error)