这个寻找素数的程序如何运作?

时间:2015-04-14 08:57:29

标签: java primes

我必须编写一个简单的程序来打印从2到100的素数。

首先,我做了一些关于质数是什么的研究。我试了很长时间,最后我在书中找到了答案,因为每次写100%工作代码时我都没有成功。

我理解大部分答案代码,但有一部分我不明白。让我试着解释一下。首先是这本书中的代码:

// Find prime numbers between 2 and 100.
class Prime {

    public static void main(String args[]) {
        int i, j;
        boolean isprime;
        for(i=2; i < 100; i++) {
            isprime = true;
            // see if the number is evenly divisible
            for(j=2; j < i/j; j++)
               // if it is, then it's not prime
               if((i%j) == 0) isprime = false;
            if(isprime)
               System.out.println(i + " is prime.");
        }
    }
}

好的,所以我知道这一点:素数只能​​由它自己和1来分割。

让我先取4号,这不是素数,因为它也可以除以2.所以在代码中我遵循for循环,但是我被困在'看看数字是否可以被整除'部分。在4的情况下,4%4没有余数,因为==0部分它是假的。

因此isprime为假。但是,我读错了或者想错了,因为如果拿一个素数,比如5,我会得到相同的结果:5%5没有余数,因为5/5 == 1。因此,在这种情况下,5%5也等于0,因此isprime也应该为false,但在这种情况下,数字5是素数。

所以我真的不明白这项检查在代码中是如何工作的。

在开头i是2而j是2,所以你得2%2,也没有余数,但2也是素数,所以我知道我看到它错了。

如果有人可以解释这是如何运作的,我在网上搜索了一个小时但却找不到它。

5 个答案:

答案 0 :(得分:1)

首先,应该说 - 在其他答案中已经提到过 - 你从书中得到的代码是错误的(或者你错过了复制它)。内部循环的条件应该是

for(j=2; j <= i/j; j++)

这样做的一个重要教训是,您必须尝试运行书籍示例,看看它们是否有效。如果你运行它,你会看到它标记为4,9,25,49为素数,而不是。

但您对代码的阅读也不正确。你说4被判断为不是素数因为4%4 == 0。但事实上,一个素数被允许自己整数(任何数字都是)。事实上,通过将素数除以自身来测试素数是行不通的。

让我们看看这是什么。你了解外部循环 - 它给你的候选人测试他们是否是素数。

然后它的工作方式如下:假设当前候选人是素数。尽量用所有可能的除数除以它。如果任何除数均分,则将其标记为&#34;而不是素数&#34;。

如果在对所有可能的除数进行测试之后,没有一个达到标记为&#34;而不是素数&#34;,那么它确实是素数并且你可以打印它。

因此i循环代表候选者,j循环代表潜在的除数。以15为例{@ 1}}。

  • i开始j i/j15/2,因此我们进入循环。
    • 2是15的除数?不,它不是。什么都没做。
  • 2 < 7现在是j3i/j15/35,所以我们继续。
    • 3是15的除数?是!所以设置3 < 5
  • isprime=false现在是j4i/j15/43 false ,因此我们停止。

正如您所看到的,上面4 < 3 j=3设置为isprime的步骤为false,因此我们不打印它 - 它不是素数。

现在让我们采用一个真正的素数,如13:

  • j i/j开始13/22 < 6即6. j,因此我们进入循环。
    • 2是13的除数?不,它不是。什么都没做。
  • 3现在是i/j13/343 < 4j,所以我们继续。
    • 3是13的除数?不,它不是。什么都没做。
  • 4现在是i/j13/434 < 3isprime false ,因此我们停止。

没有将false设置为true的步骤。它仍然是j,因此数字13是素数,我们打印它。

现在,这里的诀窍是我们为i提供哪些候选人。天真地,第一次写这篇文章的人会认为候选人应该是1和1之间的所有数字,不包括ifor ( j = 2; j < i; j++ ) 。所以他们会编写一个循环:

15 = 3 x 5

但这很浪费。为什么?假设您检查了数字15.您发现3是它的除数。那个怎么样?因为5。但这意味着5也是一个除数。你不需要测试除数,如果除以它,你得到一个你已经测试过的除数。因此,无需测试3,因为我们已经测试了for ( j = 2; j <= i / 2; j++ )

所以,再一次,天真的程序员可能会决定我们只能测试一半的候选人:

i

但事实上,从数学上讲,这仍然是浪费。实际上,我们应该只达到j的平方根。为什么?因为如果除数i大于j x k = i的平方根,那么k,则i将小于k的平方根。如果j x k更大,那么sqrt(i) x sqrt(i)将大于j x k > i,这意味着他们i。但我们知道它们等于i

这意味着如果潜在的除数大于i的平方根,我们已经找到了另一个除数,小于i的平方根,并对其进行了测试并标记为{{ 1}} as&#34; not prime&#34;。

这就是你的候选循环测试的内容。它基本上是一种简单的写作方式

for ( j = 2; j <= Math.sqrt(i); j++ )

不调用Math类而不将整数转换为双精度。

但正如我从一开始就说的,条件是<=而不是<非常重要。对于素数的平方数,(4 = 2 x 2,9 = 3 x 3),唯一合适的除数是等于到它们的平方根的数字。


最后一点注意:这本书的例子仍然很浪费,因为在找到一个使数字不是素数的除数后它不会停止检查。找到一个除数就足够了。一种方法是更改​​循环条件,如下所示:

for ( j = 2; isprime && j <= i/j; j++ )

所以只要我们仍然有理由相信这个数字是素数,那么循环才会继续。在将j标记为isprime的{​​{1}}后,循环将自动停止。

(当然,这个算法不是最好的算法。有更好的算法。它只是每个人开始的算法。)


评论中提出的问题的答案:

内循环总是重新执行。可以这样想:

如果循环显示:

false

它相当于

for ( int i = 0; i < 3; i++ ) {
     doSomething();
}

所以如果&#34;做某事&#34; part本身就是一个循环:

doSomething();
doSomething();
doSomething();

相当于:

for ( int i = 0; i < 3; i++ ) {
    for ( int j = 0; j < 2; j++ ) {
        runSomething();
    }
}

所以你看,每次它都是从零开始的新for ( int j = 0; j < 2; j++ ) { runSomething(); } for ( int j = 0; j < 2; j++ ) { runSomething(); } for ( int j = 0; j < 2; j++ ) { runSomething(); }

关于花括号的问题:正式,j语句的格式为:

for

该语句可以是单个语句,如方法调用,内部for循环,if语句或其他内容。在这种情况下,它不需要大括号。或者语句可以是,它是一对包含零个或多个语句的大括号。您习惯于看到一个块,实际上它建议始终使用块而不是单个语句,因为它更容易添加语句。

答案 1 :(得分:0)

你的内循环标题中有一个小错误。

// ...
for(j=2; j <= i/j; j++)
// ...

因此,您必须使用<而不是<=

答案 2 :(得分:0)

尝试理解这一点的最佳方法是将System.out.println放入循环中,其值为i,j。

我为你做了,2,3,4和5.(只有4不是素数)。

i = 2 - &gt; 循环的条件是2&lt; 2/2是假的,你不会进入循环,它是素数。

i = 3 - &gt; 循环的条件是2&lt; 3/2是假的,你不进入循环,它是素数。

i = 4 - &gt; 循环的条件是2&lt; 4/2是假的,你不进入循环并且它是素数。也许循环条件应该是&lt; =

i = 5 - &gt; 循环的条件是2&lt; 5/2为真,你进入循环,if条件为false,你将1添加到j并执行循环: 3&lt; 5/3是假的,你不会进入循环,它是素数。

答案 3 :(得分:0)

您的问题出现在第二个for循环中

for(j=2; j < i/j; j++)

对于4及以下,控件永远不会进入内部for循环,并且在开始检查之前你已经假设你的数字是素数,所以如果控制从不进入循环,它将被视为素数。

条件应该是:

for(j=2; j <= i/j; j++)

这会给你正确的结果。

答案 4 :(得分:0)

我刚刚完成了同样的问题。虽然最后花了几个小时,但我强迫自己不要在问题之前的任何地方寻找答案。我仍然不知道书中的解决方案是什么。我的观点是试图通过恶化来对抗,审查手头的材料,你就会得到它。这是我的代码,丑陋而又情感。

class primeNum {

public static void main(String args[]){

    int x = 2;
    int y = 2;
    int count;
    System.out.println("The prime numbers between 2 and 100 are: ");

    while (x < 100) {
        count = 0;
        y = 2;

        while (y <= x) {

            if (x % y == 0)  {
                count ++;
            }
        ++y;
        }   
                if (count == 1 ) {
                    System.out.println(" " + x);
            }

        ++x;
    }
}

}