有人可以解释一下下面的代码是如何运作的。
问题:你有64个门,最初都是关闭的。你在门口进行了64次通行证。第一次,你访问每扇门并切换门(如果门关闭,你打开它;如果它是打开的,你关闭它)。第二次你只访问每一个第二门(#2号门,#4号,#6号......)。第三次,每隔3门(#3号门,#6号,#9号......等),直到你只访问第64门。
我发现以下代码有效,但我想知道究竟发生了什么。我不理解循环。
public class doors {
public static void main(String args[]) {
// assume true=open, false=closed. Doors will all default to closed.
boolean[] doors = new boolean[64];
for (int i=0; i<64; i++) {
for (int j=i; j<64; j=j+i+1) {
doors[j] = !doors[j];
}
}
// at the end, print out all the doors that are closed
System.out.println("These doors are opened:");
for (int i=0;i<64;i++){
if (doors[i]) {
// printing out i+1 to negate the zero-indexing
System.out.println(i+1);
}
}
}
}
答案 0 :(得分:2)
假设你知道for loops
的工作原理,那么让我们来看看下面的例子:
首先假设所有门都关闭,关闭意味着false
f ,打开意味着true
t 。为了更简单的理解,我们也假设门的总数= 4
。
index : 0 1 2 3
+-----+ +-----+ +-----+ +-----+
begin : | f | | f | | f | | f | // say all doors closed
+-----+ +-----+ +-----+ +-----+
door number : 1 2 3 4
现在
for (int i=0; i<4; i++) { // our example has 4 doors instead of 64
// first iteration of outer loop, i = 0
for (int j=i; j<4; j=j+i+1) {
// as i = 0, j = 0 too
// and j = j+i+1 = j + 0 + 1 = j +1
// so j will increment by 1
// hence, j < 4 means the loop will rotate 4 (j = 0 to 3) times
doors[j] = !doors[j]; // this line do the trick, See bellow for details.
}
}
doors[j] = !doors[j];
切换当前状态。怎么样?假设doors[j]
包含false
表示门已关闭,则!doors[j]
将值false
更改为true
意味着关闭以打开!塔达,这就是我们想要的!
index(value of j) : 0 1 2 3
+-----+ +-----+ +-----+ +-----+
after 1st iteration : | t | | t | | t | | t |
+-----+ +-----+ +-----+ +-----+
door number : 1 2 3 4
所有四扇门都打开了!
现在,对于外循环的第二次迭代,
for (int i=0; i<4; i++) { // our example has 4 doors instead of 64
// 2nd iteration of outer loop, i = 1
for (int j=i; j<4; j=j+i+1) {
// as i = 1, j = 1 too
// and j = j+i+1 = j + 1 + 1 = j + 2
// so j will increment by 2
// hence, j < 4 means the loop will rotate 2 (j = 1 and j = 3) times
doors[j] = !doors[j];
}
}
所以,
index(value of j) : 0 1 2 3
+-----+ +-----+ +-----+ +-----+
after 2nd iteration : | t | | f | | t | | f |
+-----+ +-----+ +-----+ +-----+
door number : 1 2 3 4
仅关闭2门和4门门!是的,我们走在正确的轨道上!
现在您清楚地了解到,在外循环的第三次迭代中,j
将以值2
开头并按3
递增,意味着只会切换门3
!
index(value of j) : 0 1 2 3
+-----+ +-----+ +-----+ +-----+
after 3rd iteration : | t | | f | | f | | f |
+-----+ +-----+ +-----+ +-----+
door number : 1 2 3 4
希望这有助于您了解64
门的代码如何解决问题!
最终(第4次)迭代将使所有的门,看起来像:
index(value of j) : 0 1 2 3
+-----+ +-----+ +-----+ +-----+
after final iteration : | t | | f | | f | | t |
+-----+ +-----+ +-----+ +-----+
door number : 1 2 3 4
答案 1 :(得分:1)
第一份工作:对门1 - 64进行编号。让它们为零会使数学变得混乱。
当且仅当其数量中的因子数量为奇数时,门才会保持打开状态。
仅对于完美正方形才有效,因为任何其他数字都具有偶数个因子。 (例如,10有1,10,2和5.但25有1,25和5)。
所以保持打开的门是1,4,9,16,25,36,49和64.
所以你需要做的就是迭代完美的正方形,那就是O(N)。
你的代码解决这个问题的方式非常低效,因为它是O(N * N)。您的代码通过反复遍历门集来工作。关键表达是j=j+i+1
,它绕过了越来越多的门。 doors[j] = !doors[j];
改变了门的状态。
尝试使用完美平方法重新编码O(N)。这会给你的教授留下深刻的印象。
答案 2 :(得分:0)
如果你的意思是你不理解这个循环:
for (int i=0; i<64; i++) {
for (int j=i; j<64; j=j+i+1) {
doors[j] = !doors[j];
}
}
这是最直接的解决方案。在外循环中,i代表步骤(所以你总共有64个步骤),我也表示要跳过的门数(你跳多少门)来切换。因此,在循环0中,您跳过没有门切换,在循环1中,您跳过1门,在循环编号2中,每次跳过2门,...依此类推。内部循环将找到切换并存储在索引j中的实际门。因为你将在步骤编号i中跳过i门,所以你将从i开始到64并且每次将索引增加i + 1(相当于跳过i门)。希望你明白。