如果它在每次迭代后返回到其原始状态,我可以安全地改变我正在迭代的数组吗?

时间:2023-01-22 17:18:28

I am writing a minimax algorithm for a game in Java, and, for speed purposes, mutating the game state as I recursively work through the decision tree. However, this involves modifying the list of moves I am iterating over.

我正在为Java中的游戏编写一个minimax算法,并且出于速度目的,当我递归地通过决策树时,改变游戏状态。但是,这涉及修改我正在迭代的移动列表。

public int minimax(int currentDepth) {
    if (currentDepth == depth || board.legalMoves.isEmpty()) {
        int eval = board.eval();
        board.takeBack(1);
        return eval;
    }
    int x = Integer.MIN_VALUE;
    for (Tuple move : board.legalMoves) {
        board.move(move);
        x = max(x, -1*minimax(currentDepth+1));
        board.takeBack(1);
    }
    return x
}

The board.move() method mutates the ArrayList legalMoves, but takeBack(1) brings it back to its original state. Could this cause any problems?

board.move()方法改变了ArrayList legalMoves,但takeBack(1)将其恢复到原始状态。这会导致任何问题吗?

2 个解决方案

#1


5  

In a word, yes.

总之,是的。

You don't specify the type of board.legalMoves. You say that it's array, but it can't be, since you're calling isEmpty() on it. I therefore suspect that you mean ArrayList. If that's the case, the documentation is pretty clear:

您没有指定board.legalMoves的类型。你说它是数组,但它不可能,因为你在它上面调用isEmpty()。因此我怀疑你的意思是ArrayList。如果是这种情况,文档很清楚:

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

这个类的iterator和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出ConcurrentModificationException。因此,在并发修改的情况下,迭代器快速而干净地失败,而不是在未来的未确定时间冒任意,非确定性行为的风险。

I see two ways around this:

我看到两种方法:

1) Avoid structural modifications. In other words, it's OK to change the values of the elements, but it's not OK to add/remove elements.

1)避免结构修改。换句话说,可以更改元素的值,但添加/删除元素不合适。

2) Iterate over the ArrayList using indices:

2)使用索引迭代ArrayList:

for (int i = 0; i < board.legalMoves.size(); i++) {
    Tuple move = board.get(i);
    ...
}

#2


0  

Yes, you can but it's dangerous. I suggest to move the minmax algorithm to a new class and pass the data to analyze into the constructor.

是的,你可以,但这很危险。我建议将minmax算法移动到一个新类并传递数据以分析到构造函数中。

Now, you can copy the data once in the constructor and the methods can operate on the copy. The algorithm can now modify the data in any way it wishes, it won't influence other parts of the game or other threads. Also, if you have any problems, they must come from the code in one class.

现在,您可以在构造函数中复制一次数据,并且方法可以在副本上运行。该算法现在可以以任何方式修改数据,它不会影响游戏的其他部分或其他线程。此外,如果您有任何问题,它们必须来自一个类中的代码。

The general goal is to give each modifying part of the code a copy of the data it needs to cut dependencies which can cause trouble.

总体目标是为代码的每个修改部分提供所需数据的副本,以减少可能导致问题的依赖性。

#1


5  

In a word, yes.

总之,是的。

You don't specify the type of board.legalMoves. You say that it's array, but it can't be, since you're calling isEmpty() on it. I therefore suspect that you mean ArrayList. If that's the case, the documentation is pretty clear:

您没有指定board.legalMoves的类型。你说它是数组,但它不可能,因为你在它上面调用isEmpty()。因此我怀疑你的意思是ArrayList。如果是这种情况,文档很清楚:

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

这个类的iterator和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出ConcurrentModificationException。因此,在并发修改的情况下,迭代器快速而干净地失败,而不是在未来的未确定时间冒任意,非确定性行为的风险。

I see two ways around this:

我看到两种方法:

1) Avoid structural modifications. In other words, it's OK to change the values of the elements, but it's not OK to add/remove elements.

1)避免结构修改。换句话说,可以更改元素的值,但添加/删除元素不合适。

2) Iterate over the ArrayList using indices:

2)使用索引迭代ArrayList:

for (int i = 0; i < board.legalMoves.size(); i++) {
    Tuple move = board.get(i);
    ...
}

#2


0  

Yes, you can but it's dangerous. I suggest to move the minmax algorithm to a new class and pass the data to analyze into the constructor.

是的,你可以,但这很危险。我建议将minmax算法移动到一个新类并传递数据以分析到构造函数中。

Now, you can copy the data once in the constructor and the methods can operate on the copy. The algorithm can now modify the data in any way it wishes, it won't influence other parts of the game or other threads. Also, if you have any problems, they must come from the code in one class.

现在,您可以在构造函数中复制一次数据,并且方法可以在副本上运行。该算法现在可以以任何方式修改数据,它不会影响游戏的其他部分或其他线程。此外,如果您有任何问题,它们必须来自一个类中的代码。

The general goal is to give each modifying part of the code a copy of the data it needs to cut dependencies which can cause trouble.

总体目标是为代码的每个修改部分提供所需数据的副本,以减少可能导致问题的依赖性。