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

-1 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);

    }
Run Code Online (Sandbox Code Playgroud)

问题显然必须在这个方法中,在我看来,代码不会从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);
        }

    }
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 23

我不明白为什么它不起作用的问题....代码不会从while循环中退出,我只是看不出原因.

这是真的,你看不到它.我一直在冰箱里看着牛奶,我看不到牛奶.你有冰箱盲,但编码.你正在直视一个明显的缺陷而你却看不到它.

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

它在人类中也很常见.你倾向于阅读你认为的东西,即使它不是:

           /\
          /I \
         /LOVE\
        /PARIS \
       / IN THE \
      /THE SPRING\
     --------------
Run Code Online (Sandbox Code Playgroud)

他们第一次阅读时,大多数人都读到了" 我在春天爱巴黎 ",但这不是它所说的.

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

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

你正在使用的技术是要求有新眼睛的人来看问题.这项技术有效; 我只需要看一下您的代码就可以看到明显的问题,因为我正在阅读您编写的代码,并且您正在阅读您认为自己编写的代码.

但这是一种糟糕的技术,因为它会浪费别人的时间来解决你可以学会解决的琐碎问题.今天是学习如何自己解决这些问题的好日子.

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

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

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

下一个技术称为橡皮鸭调试.

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

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

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

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

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

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

  • 您正在编写标准算法的代码版本.回到您对算法的描述,并验证算法中的每一步都在代码中的某处找到.在原始算法中有一个不在你的程序中的操作,这就是bug的地方.

  • 重命名变量,使其名称更清晰地表示其概念.error是错的; 那不是错误.那就是errorTolerance. x应该是approximation.错误的计算方法currentError = approximation * approximation - a;如果你看一下自己的程序并问自己"在哪里errorTolerance比较currentError?你很容易找到你的错误.如果你这样做,你会写出更少的愚蠢错误.

  • 概括.很难看到你的错误,因为你没有阅读代码,你正在阅读它的意图.你看一下代码,你看到的是:


SOLVER
{
    VALIDATE ARGUMENT
    INITIALIZE APPROXIMATION
    while (true)
    {
      if (WITHIN TOLERANCES)
        RETURN APPROXIMATION
      REFINE APPROXIMATION
    }
}
Run Code Online (Sandbox Code Playgroud)

该代码是正确的.你看看if(Abs...并看到"我正在检查我是否在容忍范围内",这是做这一步的正确位置,你甚至没有想到"但我是否正确地实施了它?"

但由于这是您希望代码采用的结构,您实际上可以这样写:

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() { ... }
}
Run Code Online (Sandbox Code Playgroud)

你是这样不太可能发生错误时,你有,做的方法一两件事,并且做得非常非常好,并奖励积分,如果该方法可以独立测试.

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

  • 你的代码一般都很草率. SqaureRoot而不是SquareRoot. Exception而不是ArgumentException.等等.别邋.. 养成制作每一行代码的习惯,你就要写出清晰和精确的模型.这将使你发现不可避免的错误变得更加容易,因为你不必躲过一个小错误的海洋,其中一些可能会掩盖这个更大的错误.

任何这些技术都可以快速找到您的错误.立即学习所有这些知识.

进一步阅读:

  • 埃里克一如既往的伟大写作!你已经把一些东西放在了我脑子里长久以来的话语中. (4认同)
  • "_你正在分析的程序是有效的程序,但该程序只存在于你的头脑中."它每次都发生在我身上,不仅仅是计算机编程,还有笔和纸.有时候,当我想要"100"时,我会写"001"而不管是什么,我只会看到"100".因为这个原因,我在考试中失去了很多分. (2认同)