【问题标题】:Whats wrong with my syntax and am i doing this efficiently?我的语法有什么问题,我这样做是否有效?
【发布时间】:2010-03-10 01:03:02
【问题描述】:

我正在尝试制作一种方法来告诉我天气是否是素数是真还是假。这是代码:

class prime
{

    public static boolean prime (int a, int b) 
    { 
        if (a == 0) 
        {
            return false; 
        }
        else if (a%(b-1) == 0) 
        {
            return false; 
        }
        else if (b>1) 
        {
            prime (a, b-1) ;
        }
        else
        {
            return true; 
        }

    }

    public static void main (String[] arg) 
    {
        System.out.println (prime (45, 45)) ; 
    }
}

当我尝试编译这个时,我得到这个错误信息:

prime.java:23: missing return statement
    }
    ^
1 error

我可能会误解错误消息的含义,但在我看来,没有缺少 return 语句,因为我对每组可能的条件都有一个 return 语句。如果 a 为 0,则返回 false,如果不是,则检查 a 是否可被 b 整除,如果是则返回,否则返回,如果 b 大于 1,则重新开始。如果 b 不大于 1 它也会返回。

  • 而且看起来有点乱 使此方法采用两个整数 是相同的int。

  • 我的语法有什么问题/为什么我会收到错误消息?有没有办法让我在 main 中使用的方法只需要一个 int(也许另一种方法将该 int 拆分为两个克隆,然后传递给public static boolean primeproper?

  • 或者有没有更有效的方法 关于我想念的这个 完全?

【问题讨论】:

  • David,我建议您浏览一下 Java 网站上的一些教程。谷歌搜索 java 并点击第一个结果。在尝试处理递归素数查找器之前,您需要一些基础知识。特别注意方法调用和控制结构。
  • 这不是我自学的技术作业。这不是为了学校,而是为了自己。我已经在用书了,我应该切换到 java 的教程还是继续使用我正在使用的书?
  • 如果您有书,请继续阅读。坚持就会有回报。也许最能帮助您的是使用 IDE 来帮助您在制作错误时指出错误。你看过 Netbeans 或 Eclipse 吗?
  • 我安装了 Eclipse。我不知道如何使用它。

标签: java syntax methods


【解决方案1】:

在您的prime 函数中,有四种可能的代码路径,其中一种不会返回任何东西。这就是错误消息所抱怨的。您需要更换:

prime (a, b-1) ;

与:

return prime (a, b-1) ;

else if (b>1) 的情况下。

话虽如此,这实际上不是计算一个数是否为素数的好方法。问题是每个递归调用都会分配一个堆栈帧,如果您试图计算 99,999,999 是否是质数,您会遇到严重的堆栈溢出问题?

对于某些问题,递归是一个非常好的工具,但您需要了解堆栈深度。至于更有效的解决方案,您可以执行许多测试来确定一个数字是否素数,然后只用蛮力测试检查其他数字。

您应该注意的一件事是首先检查较小数字的可除性,因为这会更快地缩小您的搜索范围。并且不要在乘法的地方使用除法,乘法通常更快(尽管并非总是如此)。

还有一些可能是鬼鬼祟祟的技巧:

  • 除 2 以外,以 2、4、6、8 或 0 结尾的所有数字都是非质数。
  • 除 5 以外的所有以 5 结尾的数字都是非质数。 仅这两条规则就会将您的搜索空间减少 60%。假设您将测试编号作为字符串获取,那么即使在转换为整数类型之前测试该字符串的最后一位数字也是一件简单的事情。

有一些更复杂的可分性检查规则。如果你取 9 的倍数并将所有数字相加得到一个新数字,然后对那个数字再做一次,然后继续直到你有一个数字,你会发现它总是 9。

这将使您的搜索空间再减少 10%,尽管检查时间更昂贵。请记住,这些检查仅对非常大的数字有利。对于 32 位整数来说,优势并不是那么大,因为在那里预先计算的位图会更有效(见下文)。

为了简单起见,我将使用以下迭代解决方案:

public static boolean prime (int num) {
    int t = 2;
    while (t * t <= num) {
        if ((num % t) == 0) {
            return false;
        }
        t++;
    }
    return true;
}

如果您希望代码中的真正速度,请不要每次都计算它。计算一次以创建一个包含您感兴趣的范围内所有素数的位数组(其中一种筛选方法会这样做),然后只需根据该位数组检查您的值。

如果您甚至不希望每次程序启动时都计算数组的成本,请执行一次并将位数组保存到磁盘文件中,并在程序启动时加载它。

我实际上有一个文件中前 1 亿个素数的列表,我使用 grep 来查找一个数字是否为素数比运行一些代码来计算它更容易和更快 :-)


至于为什么你的算法(用return 语句修复)坚持认为 7 不是素数,它会坚持每个数都是非素数(没有用负数检查,我很确定它们会导致一些严重的问题 - 您的第一次检查应该是 if (a &lt; 1) ...)。

让我们看看当您调用prime(3,3) 时会发生什么。

第一次通过,它达到第三个条件,所以调用prime(3,2)

然后它达到 second 条件,因为 3 % (2-1) == 0 为真(N % 1 总是 0)。

所以它返回 false。这可能可以通过将第三个条件更改为 else if (b&gt;2) 来解决,尽管我没有彻底测试过,因为我认为递归解决方案不是一个好主意。


以下完整的代码 sn-p 将满足您的需求,尽管我感谢您想知道自己做错了什么的好奇心。这是一个真正最终会成为优秀代码切割者的人的标志。

public class prime
{
    public static boolean isPrime (int num) {
        int t = 2;
        while (t * t <= num) {
            if ((num % t) == 0) {
                return false;
            }
            t++;
        }
        return true;
    }


    public static void main (String[] arg) 
    {
        System.out.println (isPrime (7)) ; 
    }
}

【讨论】:

  • while 是什么意思? t++ 是什么意思?既然 num/t=t 也不应该 num%t alwayse = 0?
  • @David, 'while' 将继续循环,只要条件为真(直到 t 大于 num 的平方根,保证 num 不能是t)。 t++ 将 1 加到 t。只有当 num 是 t 的精确倍数时,num%t 才会为 0,即该数字不是素数。
【解决方案2】:

您似乎有这样的印象,因为递归最终会找到一个会遇到 return 语句的基本情况,那么该 return 将在所有递归调用中冒泡。这不是真的。每个递归调用都必须像这样传递结果:

return prime(a, b - 1);

【讨论】:

  • 好的,但是当我做出改变时,它认为 7 不是素数。怎么了?
  • 大卫:当 b 为 2 时,表达式a%(b-1) == 0 对所有 a 都为真,所以它最终总是返回假。
【解决方案3】:

如果b 大于1,您的函数将不会返回任何内容。

可能是return prime (a, b-1) ; 吗?

【讨论】:

  • 也就是说,你必须使用return prime (a, b-1) ;,而不仅仅是prime (a, b-1) ;
  • 但如果 b>1 则它将再次通过素数,直到不是 b>1。
  • 这也不能让它正常工作。如果我这样做,那么它会说 7 不是素数。 7 是素数。
  • 汤姆在他的回答中说得很好。您仍然需要显式返回对 prime(...) 方法的调用结果。 每个方法的调用必须到达一个return语句,而不是最终到达一个。
  • (这令人困惑。我是来自评论 #1 和 #4 的大卫,而不是提问者)。如果逻辑错误,那就是另一个问题了。要解决您的语法问题,您只需将 return 放在对 prime(...) 的调用前面
【解决方案4】:

要提高效率,请多考虑您的条件。你真的需要测试从 2 到 N 的每一个因素吗?是否有不同的停止点可以帮助更快地完成素数测试?

要制作更好的 API,请考虑将递归方法设为私有,并使用有助于引导进程的公共入口点。例如:

public static boolean prime(int n) {
  return recurse(n, n);
}

private static boolean recurse(int a, int b) {
 ...
}

创建一个方法private 意味着它不能被另一个类调用。它对类的用户实际上是不可见的。这里的目的是通过提供一个公共帮助方法来隐藏“丑陋”的额外参数。

想想一些合数的因数。 5×2 的 10 个因素。 12 因子为 6×2。 14 个因子为 7×2。现在想想 5×5 的 25. 25 因子。 9号呢?你看到一个模式吗?顺便说一句,如果这不是家庭作业,请告诉我。这种说教对我来说很难。

【讨论】:

  • 我不需要测试 2-(n-1) 的每个因素吗? [n-1 不是 n,因为如果它测试除以 n,它会认为该数字不是素数,因为它%n 将 =0。
  • 另外说这个方法是私有的是什么意思?这是做什么的?
  • WTF?现在我去查找“说教”:-)
  • 好吧,这并不能很好地回答这个问题。
【解决方案5】:

要回答为什么 7 不起作用,请假装您是计算机并按照您的逻辑进行操作。这是你写的。

class prime
{

    public static boolean prime (int a, int b) 
    { 
        if (a == 0) 
        {
            return false; 
        }
        else if (a%(b-1) == 0) 
        {
            return false; 
        }
        else if (b>1) 
        {
            // Have to add the return statement
            // here as others have pointed out!
            return prime(a, b-1);
        }
        else
        {
            return true; 
        }

    }

    public static void main (String[] arg) 
    {
        System.out.println (prime (45, 45)) ; 
    }
}

让我们从 7 开始。

if(7 == 0) // not true, don't enter this block
else if(7 % 6 == 0) // not true
else if(7 > 1) // true, call prime(7, 6)

if(7 == 0) // not true, don't enter this block
else if(7 % 5 == 0) // not true
else if(6 > 1) // true, call prime(7, 5)

if(7 == 0) // not true, don't enter this block
else if(7 % 4 == 0) // not true
else if(5 > 1) // true, call prime(7, 4)

...继续调用 prime(7, 2)

if(7 == 0) // not true, don't enter this block

else if(7 % 1 == 0) true, return false

当你开始调用prime(n, 2)时,它总是会返回false,因为你有一个逻辑错误。

【讨论】:

    【解决方案6】:

    您的递归方法必须返回一个值才能展开。

        public static boolean prime (int a, int b) 
    {
        if (a == 0) 
        {
            return false; 
        }
        else if (a%(b-1) == 0) 
        {
            return false; 
        }
        else if (b>1) 
        {
            return prime (a, b-1) ;
        }
        else
        {
            return true; 
        }
    
    }
    

    我可能会以不同的方式编写它,但这就是您无法编译代码的原因。

    【讨论】:

    • @David 他的意思是当递归到达停止条件时,将有一系列returns,因为递归的prime 方法从堆栈中弹出。他的意思是返回值将传播回原来的 prime() 调用。
    【解决方案7】:

    我认为最初的问题已经得到解答 - 您需要在 else if (b&gt;1) 的正文中插入 return - 我只是想指出,当给定 1 作为 b 的值时,您的代码仍然会崩溃,抛出一个ArithmeticException 因为a%(b-1) 将被评估为a%0,导致除以零。

    您可以通过创建第一个 if 语句 if (a == 0 || b == 1) {} 来避免这种情况 这不会改善程序找到素数的方式,它只是确保少了一种方法来崩溃它。

    【讨论】:

      【解决方案8】:

      类似于@paxdiblo 的回答,但效率稍高。

      public static boolean isPrime(int num) {
          if (num <= 1 || (num & 1) == 0) return false;
          for (int t = 3; t * t <= num; t += 2)
              if (num % t == 0)
                  return false;
          return true;
      }
      

      一旦确定不是偶数,就可以跳过所有偶数。这将使需要检查的数字减半。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-19
        • 1970-01-01
        • 2011-02-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多