动机
扫雷艇叫Minesweeper Flags,您必须注意不要向对手透露太多信息。这通常是将优秀玩家与经验不足的玩家区分开的原因。如果您有80%的机会成为地雷,但有20%的机会公开地雷(这可能会向您的对手展示10个明显的地雷),您会冒险吗?计算期望值(本身是一个完全不同的主题)告诉我们,这可能是个坏主意。 6枚地雷:
将字段分组为FieldGroups时(发现字段组的定义,请参见前面的问题),我们发现: />
3个字段仅连接到1(
b
)4个字段同时连接到1和3(
c
)3个字段仅连接到3(
d
)44字段均未连接(
a
)在此板上执行防雷概率分析时,我们发现有两个解决方案组: />
4c = 1,44a = 3,3b = 0,3d = 2,158928组合(
c
组中的4个字段应具有一个地雷,a
组中的44个字段应具有3个地雷,等等) /> 4c = 0、44a = 2、3b = 1、3d = 3、2838个组合现在,数字“ 2”直接位于“ 1”上方的概率是多少?让我们将
1
上方的字段称为x
。通过向x
(2a + 3d + 1b = 2
)的邻居的现有董事会添加FieldRule,以及对该字段不是地雷的规则(x = 0
),可以计算出该规则。但是,如果我们要对董事会的每个领域都这样做,那将花费大量时间。更不用说我们必须对每个数字(0-8)执行此操作。 因此,我们可以将字段分组为与之相邻的字段组:
Field Groups Probability Groups
aaaaaaaa abbbbbba
aaaaaaaa bcdefghb
aabccdaa bijklmnb
aab13daa bop13qrb
aabccdaa bijklmnb
aaaaaaaa bcdefghb
aaaaaaaa abbbbbba
k是我们的字段
x
所属的位置,它与字段组3*a, 2*b, 1*c
相邻。此信息可以存储在上一个问题中介绍的GroupValues
结构中,它实际上是Map<FieldGroup, Integer>
。为了提高查询速度,它的hashCode
值是在首次计算后存储的,以提高性能。我们可以看到在概率组图上实际上有两个
k
,因此我们将它们组合在一起可以获得一些性能,因为它们具有相同的详细概率。在地图上也有22 b,因此我们可以通过让他们共享几率来获得很多性能。请注意,概率组b
中的所有字段都与FieldGroup a
的5个相邻。计算概率组的详细概率44a = 3,3b = 0,3d = 2,158928个组合
我们知道
x
字段(在概率组k
中)具有以下邻居:3 * a,2 * b,1 * c。 首先我们处理1c(因为这是Eclipse调试器选择的)。那个特定的邻居有一个地雷,或者没有。假设它没有地雷。然后有2种组合,因为距
c
场有2个x
场,其中一个场必须有一个地雷。其次,3a。
a
组共有44个场,在此解决方案中应具有3个地雷。该字段与这些字段中的3个相邻。假设这些邻居中有1个是我的,那么(根据超几何分布)有\ $ \ binom {3} {1} * \ binom {44-3} {3-1-1} = 2460 \ $个组合。然后邻居2b。从我们的解决方案中我们知道,尽管
b
中没有地雷,所以这很简单:一个组合。我们不与任何d相邻,因此有3个组合(3个字段,2地雷)。
因此,对于
c
中没有地雷,a
中没有地雷,b
中没有地雷,总共有一个地雷,并且\ $ 2 * 2460 * 1 * 3 = 14760 \ $组合。由于目前在x
字段周围未发现地雷,因此将14760
添加到字段x
的组合中,成为1
。处理完所有邻居组和解后,将其除以整个地图的组合总数即可得出实际概率。最后,我们得到此
double[]
我们领域的概率x
:[0.4004549781783564, 0.2998343285980985, 0.05172285894440117, 0.0023552538852416455, 1.854530618300508E-5, 0.0, 0.0, 0.0, 0.0]
也就是说,有40%的机会成为开放领域,有29.9%的机会为1,5.1%为2,等等...
速度反射
在概率组上有一个迭代,一个用于解,一个用于邻近的FieldGroup,然后一个递归循环用于特定分配。因此,类似于\ $ O(probabilityGroups * solutions * K ^ {neighboringGroups})\ $此外,还使用二项式系数和超几何分布对实际组合进行了一些计算。
这导致当拥有许多解决方案,许多领域组和/或许多概率组时,就会出现巨大的性能问题,例如在“超级死亡委员会”中,导致此类委员会实际上无法解决,因为解决它们将花费太多时间。幸运的是,这种情况很少发生。当机器人使用此算法玩游戏时,该机器人会简化进行地雷挖掘所需的分析。在使用此算法的其他情况下,它通常用于分析玩家比较聪明的游戏,因此游戏很少变得过于复杂。而且...该算法比我所知道的所有其他算法/方法要快。
典型值:
字段组通常介于10- 50,在极少数情况下可达100。
解决方案组通常在2至50之间,在某些游戏中,如果两个坏玩家在互相比赛,则很容易达到1500(甚至更高)。 />概率组通常约为50,在某些情况下最高为100。
当这些值太大时,此算法会超时。板(至少目前可能会支持更大的板),“字段组”和“概率组”的绝对最大值为256。对于解决方案组,没有已知的最大值,因为具有大量解决方案组的游戏花费的时间太长即使使用“简单”分析也可以进行分析(仅适用于挖掘概率)。
类摘要(7个文件)
DetailAnalyze:入口点,其中包含一种静态方法执行分析
DetailedResults:用于访问结果的接口
DetailedResultsImpl:上述接口的实现
FieldProxy:单个字段的详细概率的容器
NeighborFind:执行邻居检查并确定当前已知值的接口在某个字段周围进行挖掘
知识点:
FieldProxy
类的公共接口ProxyProvider:创建结果以访问其他字段的数据时使用的接口
依赖项
分析结果,组合对象,FieldGroup,GroupValues,RuntimeTimeoutException,我的Minesweeper Analyze项目其他部分的解决方案。在我的Minesweeper Analyze github存储库中找到
首先,修改后的版本
GroupValues
public class GroupValues<T> extends HashMap<FieldGroup<T>, Integer> {
private static final long serialVersionUID = -107328884258597555L;
private int bufferedHash = 0;
public GroupValues(GroupValues<T> values) {
super(values);
}
public GroupValues() {
super();
}
@Override
public int hashCode() {
if (bufferedHash != 0) {
return this.bufferedHash;
}
int result = super.hashCode();
this.bufferedHash = result;
return result;
}
public int calculateHash() {
this.bufferedHash = 0;
return this.hashCode();
}
}
然后,其余代码: />
DetailAnalyze.java:(62行,2139字节)
/**
* Creator of {@link DetailedResults} given an {@link AnalyzeResult} and a {@link NeighborFind} strategy
*
* @author Simon Forsberg
*/
public class DetailAnalyze {
public static <T> DetailedResults<T> solveDetailed(AnalyzeResult<T> analyze, NeighborFind<T> neighborStrategy) {
// Initialize FieldProxies
final Map<T, FieldProxy<T>> proxies = new HashMap<T, FieldProxy<T>>();
for (FieldGroup<T> group : analyze.getGroups()) {
for (T field : group) {
FieldProxy<T> proxy = new FieldProxy<T>(group, field);
proxies.put(field, proxy);
}
}
// Setup proxy provider
ProxyProvider<T> provider = new ProxyProvider<T>() {
@Override
public FieldProxy<T> getProxyFor(T field) {
return proxies.get(field);
}
};
// Setup neighbors for proxies
for (FieldProxy<T> fieldProxy : proxies.values()) {
fieldProxy.fixNeighbors(neighborStrategy, provider);
}
double totalCombinations = analyze.getTotal();
Map<GroupValues<T>, FieldProxy<T>> bufferedValues = new HashMap<GroupValues<T>, FieldProxy<T>>();
for (FieldProxy<T> proxy : proxies.values()) {
// Check if it is possible to re-use a previous value
FieldProxy<T> bufferedValue = bufferedValues.get(proxy.getNeighbors());
if (bufferedValue != null && bufferedValue.getFieldGroup() == proxy.getFieldGroup()) {
proxy.copyFromOther(bufferedValue, totalCombinations);
continue;
}
// Setup the probabilities for this field proxy
for (Solution<T> solution : analyze.getSolutionIteration()) {
proxy.addSolution(solution);
}
proxy.finalCalculation(totalCombinations);
bufferedValues.put(proxy.getNeighbors(), proxy);
}
int proxyCount = bufferedValues.size();
return new DetailedResultsImpl<T>(analyze, proxies, proxyCount);
}
}
DetailedResults.ja va:(46行,1162字节)
/**
* Interface for retreiving more detailed probabilities, for example 'What is the probability for a 4 on field x?'
*
* @author Simon Forsberg
*
* @param <T> The field type
*/
public interface DetailedResults<T> {
Collection<ProbabilityKnowledge<T>> getProxies();
/**
* Get the number of unique proxies that was required for the calculation. As some can be re-used, this will always be lesser than or equal to <code>getProxyMap().size()</code>
*
* @return The number of unique proxies
*/
int getProxyCount();
/**
* Get a specific proxy for a field
*
* @param field
* @return
*/
ProbabilityKnowledge<T> getProxyFor(T field);
/**
* Get the underlying analyze that these detailed results was based on
*
* @return {@link AnalyzeResult} object that is the source of this analyze
*/
AnalyzeResult<T> getAnalyze();
/**
* @return The map of all probability datas
*/
Map<T, ProbabilityKnowledge<T>> getProxyMap();
}
DetailedResultsImpl.java :( 46行,1211字节)
public class DetailedResultsImpl<T> implements DetailedResults<T> {
private final AnalyzeResult<T> analyze;
private final Map<T, ProbabilityKnowledge<T>> proxies;
private final int proxyCount;
public DetailedResultsImpl(AnalyzeResult<T> analyze, Map<T, FieldProxy<T>> proxies, int proxyCount) {
this.analyze = analyze;
this.proxies = Collections.unmodifiableMap(new HashMap<T, ProbabilityKnowledge<T>>(proxies));
this.proxyCount = proxyCount;
}
@Override
public Collection<ProbabilityKnowledge<T>> getProxies() {
return Collections.unmodifiableCollection(proxies.values());
}
@Override
public int getProxyCount() {
return proxyCount;
}
@Override
public ProbabilityKnowledge<T> getProxyFor(T field) {
return proxies.get(field);
}
@Override
public AnalyzeResult<T> getAnalyze() {
return analyze;
}
@Override
public Map<T, ProbabilityKnowledge<T>> getProxyMap() {
return Collections.unmodifiableMap(proxies);
}
}
FieldProxy.java:(182行,5711字节)
public class FieldProxy<T> implements ProbabilityKnowledge<T> {
private static int minK(int N, int K, int n) {
// If all fields in group are neighbors to this field then all mines must be neighbors to this field as well
return (N == K) ? n : 0;
}
private double[] detailedCombinations;
private double[] detailedProbabilities;
private final T field;
private int found;
private final FieldGroup<T> group;
private final GroupValues<T> neighbors;
public FieldProxy(FieldGroup<T> group, T field) {
this.field = field;
this.neighbors = new GroupValues<T>();
this.group = group;
this.found = 0;
}
void addSolution(Solution<T> solution) {
recursiveRemove(solution.copyWithoutNCRData(), 1, 0);
}
/**
* This field has the same values as another field, copy the values.
*
* @param copyFrom {@link FieldProxy} to copy from
* @param analyzeTotal Total number of combinations
*/
void copyFromOther(FieldProxy<T> copyFrom, double analyzeTotal) {
for (int i = 0; i < this.detailedCombinations.length - this.found; i++) {
if (copyFrom.detailedCombinations.length <= i + copyFrom.found) {
break;
}
this.detailedCombinations[i + this.found] = copyFrom.detailedCombinations[i + copyFrom.found];
}
this.finalCalculation(analyzeTotal);
}
/**
* Calculate final probabilities from the combinations information
*
* @param analyzeTotal Total number of combinations
*/
void finalCalculation(double analyzeTotal) {
this.detailedProbabilities = new double[this.detailedCombinations.length];
for (int i = 0; i < this.detailedProbabilities.length; i++) {
this.detailedProbabilities[i] = this.detailedCombinations[i] / analyzeTotal;
}
}
/**
* Setup the neighbors for this field
*
* @param neighborStrategy {@link NeighborFind} strategy
* @param proxyProvider Interface to get the related proxies
*/
void fixNeighbors(NeighborFind<T> neighborStrategy, ProxyProvider<T> proxyProvider) {
Collection<T> realNeighbors = neighborStrategy.getNeighborsFor(field);
this.detailedCombinations = new double[realNeighbors.size() + 1];
for (T neighbor : realNeighbors) {
if (neighborStrategy.isFoundAndisMine(neighbor)) {
this.found++;
continue; // A found mine is not, and should not be, in a fieldproxy
}
FieldProxy<T> proxy = proxyProvider.getProxyFor(neighbor);
if (proxy == null) {
continue;
}
FieldGroup<T> neighborGroup = proxy.group;
if (neighborGroup != null) {
// Ignore zero-probability neighborGroups
if (neighborGroup.getProbability() == 0) {
continue;
}
// Increase the number of neighbors
Integer currentNeighborAmount = neighbors.get(neighborGroup);
if (currentNeighborAmount == null) {
neighbors.put(neighborGroup, 1);
}
else neighbors.put(neighborGroup, currentNeighborAmount + 1);
}
}
}
@Override
public T getField() {
return this.field;
}
@Override
public FieldGroup<T> getFieldGroup() {
return this.group;
}
@Override
public int getFound() {
return this.found;
}
@Override
public double getMineProbability() {
return this.group.getProbability();
}
@Override
public GroupValues<T> getNeighbors() {
return this.neighbors;
}
@Override
public double[] getProbabilities() {
return this.detailedProbabilities;
}
private void recursiveRemove(Solution<T> solution, double combinations, int mines) {
if (Thread.interrupted()) {
throw new RuntimeTimeoutException();
}
// Check if there are more field groups with values
GroupValues<T> remaining = solution.getSetGroupValues();
if (remaining.isEmpty()) {
this.detailedCombinations[mines + this.found] += combinations;
return;
}
// Get the first assignment
Entry<FieldGroup<T>, Integer> fieldGroupAssignment = remaining.entrySet().iterator().next();
FieldGroup<T> group = fieldGroupAssignment.getKey();
remaining.remove(group);
solution = Solution.createSolution(remaining);
// Setup values for the hypergeometric distribution calculation. See http://en.wikipedia.org/wiki/Hypergeometric_distribution
int N = group.size();
int n = fieldGroupAssignment.getValue();
Integer K = this.neighbors.get(group);
if (this.group == group) {
N--; // Always exclude self becuase you can't be neighbor to yourself
}
if (K == null) {
// This field does not have any neighbors to that group.
recursiveRemove(solution, combinations * Combinatorics.nCr(N, n), mines);
return;
}
// Calculate the values and then calculate for the next group
int maxLoop = Math.min(K, n);
for (int k = minK(N, K, n); k <= maxLoop; k++) {
double thisCombinations = Combinatorics.NNKK(N, n, K, k);
recursiveRemove(solution, combinations * thisCombinations, mines + k);
}
}
@Override
public String toString() {
return "Proxy(" + this.field.toString() + ")"
+ "\n neighbors: " + this.neighbors.toString()
+ "\n group: " + this.group.toString()
+ "\n Mine prob " + this.group.getProbability() + " Numbers: " + Arrays.toString(this.detailedProbabilities);
}
}
NeighborFind.java:(30行,718字节)
/**
* Interface strategy for performing a {@link DetailAnalyze}
*
* @author Simon Forsberg
*
* @param <T> The field type
*/
public interface NeighborFind<T> {
/**
* Retrieve the neighbors for a specific field.
*
* @param field Field to retrieve the neighbors for
*
* @return A {@link Collection} of the neighbors that the specified field has
*/
Collection<T> getNeighborsFor(T field);
/**
* Determine if a field is a found mine or not
*
* @param field Field to check
*
* @return True if the field is a found mine, false otherwise
*/
boolean isFoundAndisMine(T field);
}
ProbabilityKnowledge.java:(39行,1031字节)
public interface ProbabilityKnowledge<T> {
/**
* @return The field that this object has stored the probabilities for
*/
T getField();
/**
* @return The {@link FieldGroup} for the field returned by {@link #getField()}
*/
FieldGroup<T> getFieldGroup();
/**
* @return How many mines has already been found for this field
*/
int getFound();
/**
* @return The mine probability for the {@link FieldGroup} returned by {@link #getFieldGroup()}
*/
double getMineProbability();
/**
* @return {@link GroupValues} object for what neighbors the field returned by {@link #getField()} has
*/
GroupValues<T> getNeighbors();
/**
* @return The array of the probabilities for what number this field has. The sum of this array + the value of {@link #getMineProbability()} will be 1.
*/
double[] getProbabilities();
}
ProxyProvider.java:(7行,132字节)
public interface ProxyProvider<T> {
FieldProxy<T> getProxyFor(T field);
}
用法/测试
在Github上可用:https://github.com/Zomis/Minesweeper-Analyze/blob/master/src /test/java/net/zomis/minesweeper/analyze/DetailAnalyzeTest.java
要查看实际分析的结果,请执行以下步骤:
转到我的扫雷标志统计页面上的一个随机游戏
转到该游戏中的随机情况
单击地图
单击底部的分析链接
,然后点击地图上的随机字段。
弹出元素将显示该字段的详细概率。这个存在吗?有没有现有的库?
关于此代码和/或此方法的任何一般性评论都欢迎
可以使其更快吗? (除了一些优化之外,我本人对此表示怀疑,但如果可以对其进行重大改进,我将非常喜欢它)。
#1 楼
专注于优化:GroupValues
中的:
@Override
public int hashCode() {
if (bufferedHash != 0) {
return this.bufferedHash;
}
int result = super.hashCode();
this.bufferedHash = result;
return result;
}
到
@Override
public int hashCode() {
if (bufferedHash == 0) {
this.bufferedHash = super.hashCode();
}
return this.bufferedHash;
}
1个本地商店已删除。单个出口点可能会有所帮助。
在
DetailAnalyse
中:Map<GroupValues<T>, FieldProxy<T>> bufferedValues = new HashMap<GroupValues<T>, FieldProxy<T>>();
bufferedValues
可能是final
。在
FieldProxy
中:for (int i = 0; i < this.detailedCombinations.length - this.found; i++) {
if (copyFrom.detailedCombinations.length <= i + copyFrom.found) {
break;
简体
for (int i = 0; i < a - b; i++) {
if (a2 <= i + b2) {
break;
我们可以将
iMax
分配为Math.min(a - b, a2 - b2)
这样可以节省对常量值的不断重新评估:
final int iMax = Math.min(this.detailedCombinations.length - this.found, copyFrom.detailedCombinations.length - copyFrom.found);
for (int i = 0; i < iMax; i++) {
它最可能更快(一个人永远不知道微优化是什么,查看代码,在这种情况下进行分析会更好。)
新代码段:
final int iMax = Math.min(this.detailedCombinations.length - this.found, copyFrom.detailedCombinations.length - copyFrom.found);
for (int i = 0; i < iMax; i++) {
this.detailedCombinations[i + this.found] = copyFrom.detailedCombinations[i + copyFrom.found];
}
我建议运行测试并替换
System.arrayCopy
整个for循环。System.arrayCopy(copyFrom.detailedCombinations, copyFrom.found, this.detailedCombinations, this.found, Math.min(this.detailedCombinations.length - this.found, copyFrom.detailedCombinations.length - copyFrom.found));
也许更快,因为它可以一次性完成整个程序块。也许速度较慢,因为整个本机复制一些项目会增加很多开销。重新进行分析并找出。
再次,
FieldProxy
:private void recursiveRemove(Solution<T> solution, double combinations, int mines) {
if (Thread.interrupted()) {
throw new RuntimeTimeoutException();
}
// Check if there are more field groups with values
GroupValues<T> remaining = solution.getSetGroupValues();
if (remaining.isEmpty()) {
this.detailedCombinations[mines + this.found] += combinations;
return;
}
// Get the first assignment
Entry<FieldGroup<T>, Integer> fieldGroupAssignment = remaining.entrySet().iterator().next();
FieldGroup<T> group = fieldGroupAssignment.getKey();
remaining.remove(group);
solution = Solution.createSolution(remaining);
// Setup values for the hypergeometric distribution calculation. See http://en.wikipedia.org/wiki/Hypergeometric_distribution
int N = group.size();
int n = fieldGroupAssignment.getValue();
Integer K = this.neighbors.get(group);
if (this.group == group) {
N--; // Always exclude self becuase you can't be neighbor to yourself
}
if (K == null) {
// This field does not have any neighbors to that group.
recursiveRemove(solution, combinations * Combinatorics.nCr(N, n), mines);
return;
}
// Calculate the values and then calculate for the next group
int maxLoop = Math.min(K, n);
for (int k = minK(N, K, n); k <= maxLoop; k++) {
double thisCombinations = Combinatorics.NNKK(N, n, K, k);
recursiveRemove(solution, combinations * thisCombinations, mines + k);
}
}
CTRL + F
solution
:<
1. private void recursiveRemove(Solution<T> solution, double combinations, int mines) {
2. GroupValues<T> remaining = solution.getSetGroupValues();
3. solution = Solution.createSolution(remaining);
4. recursiveRemove(solution, combinations * Combinatorics.nCr(N, n), mines);
5. recursiveRemove(solution, combinations * thisCombinations, mines + k);
Solution
仅用于函数头中,用于获取组值并将其传递回函数中。它还构造一次... 这会改变组值吗?
br />
,然后
public static <T> Solution<T> createSolution(GroupValues<T> values) {
return new Solution<T>(values).nCrPerform();
}
,它调用
private Solution(GroupValues<T> values) {
this.setGroupValues = values;
}
,链的末端。
因此不会更改组值。
以下更改将因此提高性能:
替换旧版本的带有新功能的功能
private Solution<T> nCrPerform() {
double result = 1;
for (Entry<FieldGroup<T>, Integer> ee : this.setGroupValues.entrySet()) {
result = result * Combinatorics.nCr(ee.getKey().size(), ee.getValue());
}
this.nCrValue = result;
return this;
}
public static double nCr(int n, int r) {
if (r > n || r < 0) {
return 0;
}
if (r == 0 || r == n) {
return 1;
}
double value = 1;
for (int i = 0; i < r; i++) {
value = value * (n - i) / (r - i);
}
return value;
}
这将大大提高性能。我不知道它是否仍然超时?
另外,由于最近造成混淆,我建议您将
Solution.getSetGroupValues()
重命名为Solution.getCopyOfGroupValues()
或类似名称。这是导致我的答案的较早版本出现错误的原因。评论
\ $ \ begingroup \ $
通常,本地调用通常比非本地调用要快,因为它们不通过JIT运行。也许有足够的运行可以启动热点优化,但是通常由于转换不准确,本机代码比Java字节码更快。
\ $ \ endgroup \ $
–Vogel612♦
2014年12月1日下午13:46
#2 楼
@Pimgd在答案的最后部分肯定是重要的,但还不够。仅仅将Solution<T>
更改为GroupValues<T>
并没有多大帮助。并循环访问列表而无需进行任何修改。方法是这样的:private void recursiveRemove(List<Entry<FieldGroup<T>, Integer>> solution, double combinations, int mines, int listIndex) {
if (Thread.interrupted()) {
throw new RuntimeTimeoutException();
}
// Check if there are more field groups with values
if (listIndex >= solution.size()) {
// TODO: or if combinations equals zero ?
this.detailedCombinations[mines + this.found] += combinations;
return;
}
// Get the assignment
Entry<FieldGroup<T>, Integer> fieldGroupAssignment = solution.get(listIndex);
FieldGroup<T> group = fieldGroupAssignment.getKey();
// Setup values for the hypergeometric distribution calculation. See http://en.wikipedia.org/wiki/Hypergeometric_distribution
int N = group.size();
int n = fieldGroupAssignment.getValue();
Integer K = this.neighbors.get(group);
if (this.group == group) {
N--; // Always exclude self becuase you can't be neighbor to yourself
}
if (K == null) {
// This field does not have any neighbors to that group.
recursiveRemove(solution, combinations * Combinatorics.nCr(N, n), mines, listIndex + 1);
return;
}
// Calculate the values and then calculate for the next group
int maxLoop = Math.min(K, n);
for (int k = minK(N, K, n); k <= maxLoop; k++) {
double thisCombinations = Combinatorics.NNKK(N, n, K, k);
recursiveRemove(solution, combinations * thisCombinations, mines + k, listIndex + 1);
}
}
并这样调用:
recursiveRemove(new ArrayList<Entry<FieldGroup<T>, Integer>>(solution.copyWithoutNCRData().getSetGroupValues().entrySet()), 1, 0, 0);
迭代而不是连续复制和修改。
这意味着为什么在地球上我从来没有想到过?
这意味着在“超级死亡委员会”上进行如此大的分析现在只需大约3.5分钟而不是35分钟!!
#3 楼
对于您的用例(一种玩扫雷的AI),使用近似的快速算法而不是精确但较慢的算法可能更合适。根据您先前的问题,我了解您对如何枚举所有具有确切概率的解决方案有完全的了解:您只需在每个FieldGroup上随机分布地雷。因此,您可以编写一种算法来提取所有可能解中具有相等概率的随机解。如果这样做,那么蒙特卡洛方法很简单:随机提取N个解,并计算出您的随机变量的结果值(即,在一个领域中相邻地雷的数量),并用比率:positive_cases / total_cases。
这样做的好处是,您可以使用相同的随机样本同时评估所有排名。您还可以决定在计算上花费多少时间:花费的时间越多,近似结果越好。
评论
\ $ \ begingroup \ $
我感谢您的建议,当可能发生超时(由于大量的解决方案/字段组/概率组)时,可以采用这种方法。但是在9999例10000中,这种方法足够有效。
\ $ \ endgroup \ $
–西蒙·福斯伯格
2014年9月9日14:09
\ $ \ begingroup \ $
由于我现在已经设法将速度提高了九倍,所以我不太可能使用蒙特卡洛方法。
\ $ \ endgroup \ $
–西蒙·福斯伯格
2014年12月15日12:31
评论
我知道这是一个古老的问题,但是“ stats.minesweeperflags.net”的链接已断开。是否有另一个链接可以遵循这些步骤?@dustytrash不幸的是暂时没有,但希望它将在明年的某个时候回来。