首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >数独- SAT的实现

数独- SAT的实现
EN

Code Review用户
提问于 2017-09-02 09:20:02
回答 1查看 312关注 0票数 3

我用减缩从sudoku到塞特实现了求解sudoku的算法。

现在,我正在尝试使用代码评审原则重构代码。我发现在应用这些原则时有些困难。正因为如此,我想分享下面的代码片段,以获得一些如何有效地解决这个问题的提示。

下面的方法在CNF中生成布尔表达式,以便为任何条目puzzle_{ij}生成布尔表达式E,从而生成E = true当且仅当有一个字面x_{ijk}, k in {1, 2, ..., 9}这样的x_{ijk} = true

代码语言:javascript
复制
// DIGITS := {1, 2, ..., 9}
// Combinatorics.product(DIGITS) = {(1,1), (1,2), ..., (9,8), (9,9)}

static List<List<Integer>> exactlyOneDigitForEachEntry() {
    ArrayList<Pair> pairs = Combinatorics.product(DIGITS);
    List<List<Integer>> clauses = new ArrayList();
    for (Pair pair : pairs) {
        ArrayList<Integer> literals = new ArrayList();
        for (int k : DIGITS) {
            int literal = (100 * pair.getA()) + (10 * pair.getB()) + k;
            literals.add(literal);
        }
        List<List<Integer>> expression = exactlyOneOf(literals);
        extendClauses(clauses, expression);
    }

    return clauses;
}

代码语言:javascript
复制
/**
 *
 * @param literals A set of literals
 * @return a boolean expression E in conjunctive normal form such that E =
 * true iff there is exactly one literal that is true.
 */
private static List<List<Integer>> exactlyOneOf(ArrayList<Integer> literals) {
    // The arrayList represents a boolean formula F such that F = true iff
    // there is exaclty one literal l such that l = true
    List<List<Integer>> expression = new ArrayList();
    expression.add(literals);
    ArrayList<Pair> pairs = Combinatorics.combinations(literals);
    for (Pair pair : pairs) {
        List<Integer> clause = new ArrayList();
        clause.add(-1 * pair.getA());
        clause.add(-1 * pair.getB());
        expression.add(clause);
    }
    return expression;
}
EN

回答 1

Code Review用户

发布于 2017-09-02 13:09:57

代码很短,所以没有什么可评论的。

幻数

代码语言:javascript
复制
int literal = (100 * pair.getA()) + (10 * pair.getB()) + k

我不知道那是什么意思。100是什么?10是什么?你为什么要把它们加起来?我建议创建一个方法int getLiteralId(int firstDigit, int secondDigit, int thirdDigit),并添加一个注释来解释实际发生的事情。

代码语言:javascript
复制
clause.add(-1 * pair.getA());
clause.add(-1 * pair.getB());

为什么要将它们乘以-1呢?至少创建一个具有适当名称的方法(类似于int negateLiteral(int literalId))。

变量名

kpairs并不是很有描述性。我建议像digitdigitPairs这样的东西。

总体设计

将布尔表达式表示为整数列表会导致混淆。我会用以下方式重新设计它:

  • 一个BooleanExpression抽象基类。
  • 表示变量的Variable子类。
  • 表示否定的Negation子类。
  • 表示连词的Conjunction子类。
  • 用于表示析取的Disjunction子类。
  • 签名将为 BooleanExpression exactlyOneOf(List<BooleanExpression> expressions) and BooleanExpression exacltyOneDigitForEachEntry()

它将使代码更加可读性。例如,

代码语言:javascript
复制
Conjunction conjunction = new Conjunction();
...
conjunction.add(new Negation(expression));

代码语言:javascript
复制
Disjunction disjunction = new Disjunction();
disjunction.add(new Variable(firstDigit));
disjunction.add(new Variable(secondDigit));
disjunction.add(new Variable(thirdDigit));
conjunction.add(disjunction);

看起来比一些看似无关的数字的“魔法”乘法和加法要好得多,不是吗?

从某种意义上说,更安全的是,任意整数不会意外地变成文字/表达式。

这也更有意义,因为布尔表达式就是布尔表达式。它不是一个整数列表(它可能是这样实现的,但它是一个实现细节)。

如果您使用第三方求解器,而它需要一个特定格式的整数列表,该怎么办?这真的不重要。您仍然可以使用上面所示的设计。只需将toList方法添加到BooleanExpression中,并根据求解器的规则在具体的子类中实现它。在将表达式传递给求解者之前只调用一次。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/174629

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档