You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Clojure core.logic/fd减法不符合预期且无文档,求优化方案

问题解决与代码优化方案

1. 修复相邻差值约束的方向依赖问题

你的stepLimit函数问题出在对fd/-的理解上:core.logic.fd/fd/-的语义是第一个参数减去第二个参数等于第三个参数(即a - b = c),并非无方向的差值。你当前的写法仅约束了lower - upper ∈ [-maxStep, maxStep],但实际需要的是相邻两数的绝对值差不超过maxStep,即|x - y| ≤ maxStep。

正确实现方式

可以直接通过两个不等式约束实现,无需额外的diff变量:

(defn step-limit [max-step]
  (fn [x y]
    (and* [(fd/<= (fd/- x y) max-step)
           (fd/<= (fd/- y x) max-step)])))

或者用fd/abs简化(需Clojure 1.10+版本的core.logic支持):

(defn step-limit [max-step]
  (fn [x y]
    (fd/<= (fd/abs (fd/- x y)) max-step)))

关于区间与域的互换问题:fd/domain和fd/interval都是定义变量取值范围的工具,多数场景下可以互换。比如(fd/interval (- max-step) max-step)完全可以替代你之前的diffDom,写法更简洁。

2. 实现最多maxDistinct个不同整数的约束

无法直接用普通Clojure的distinct处理逻辑变量,需要用core.logic的约束原语实现,这里提供两种通用方案:

方案A:基于候选值的约束(适合小maxDistinct)

当maxDistinct较小时,直接声明对应数量的逻辑变量作为候选值,约束结果中的每个元素都等于其中一个候选值:

(defn max-distinct-constraint [result max-distinct]
  (let [candidates (lvars max-distinct)]
    (and*
      ; 每个结果元素必须等于某一个候选值
      (map (fn [x] (someg #(== x %) candidates)) result)
      )))

方案B:递归统计不同元素数量(通用场景)

通过递归遍历结果列表,统计新增不同值的数量不超过maxDistinct:

(defn count-distinct [remaining seen count-so-far max-allowed]
  (conde
    [(emptyo remaining) (fd/<= count-so-far max-allowed)]
    [(fresh [x rest-seen new-count]
       (firsto remaining x)
       (resto remaining remaining-rest)
       ; 判断当前元素是否已在已见集合中
       (conde
         [(membero x seen)
          (== new-count count-so-far)
          (count-distinct remaining-rest seen new-count max-allowed)]
         [(conso x seen rest-seen)
          (fd/+ count-so-far 1 new-count)
          (fd/<= new-count max-allowed)
          (count-distinct remaining-rest rest-seen new-count max-allowed)]))]))

(defn max-distinct-constraint [result max-distinct]
  (fresh [init-count]
    (== init-count 0)
    (count-distinct result () init-count max-distinct)))

3. core.logic代码风格改进建议

  • 避免在run外部预定义逻辑变量:直接在run内部用fresh或lvars声明,更符合逻辑编程风格。
  • 使用Clojure标准命名规范:比如stepLimit改为step-limit,maxStep改为max-step。
  • 简化约束写法:run内部的约束默认是合取关系,无需用and*包裹所有map结果,可直接用everyg等原语。
  • 减少冗余变量:无需提前定义result再绑定到q,可在fresh中直接声明。

优化后的完整代码

(ns thickness-optimizer.core
  (:refer-clojure :exclude [==])
  (:use clojure.core.logic)
  (:require [clojure.core.logic.fd :as fd]))

(defn step-limit [max-step]
  (fn [x y]
    (and* [(fd/<= (fd/- x y) max-step)
           (fd/<= (fd/- y x) max-step)])))

(defn count-distinct [remaining seen count-so-far max-allowed]
  (conde
    [(emptyo remaining) (fd/<= count-so-far max-allowed)]
    [(fresh [x rest-seen new-count]
       (firsto remaining x)
       (resto remaining remaining-rest)
       (conde
         [(membero x seen)
          (== new-count count-so-far)
          (count-distinct remaining-rest seen new-count max-allowed)]
         [(conso x seen rest-seen)
          (fd/+ count-so-far 1 new-count)
          (fd/<= new-count max-allowed)
          (count-distinct remaining-rest rest-seen new-count max-allowed)]))]))

(defn max-distinct-constraint [result max-distinct]
  (fresh [init-count]
    (== init-count 0)
    (count-distinct result () init-count max-distinct)))

(defn solve [minimums valids max-step max-distinct]
  (let [result-length (count minimums)]
    (run* [q]
      (fresh [result]
        (== q result)
        (== (count result) result-length)
        ; 每个元素属于允许的集合
        (everyg #(fd/dom % (apply fd/domain valids)) result)
        ; 每个元素不小于对应最小值
        (everyg (fn [x min-val] (fd/>= x min-val)) result minimums)
        ; 相邻元素差值不超过max-step
        (everyg (step-limit max-step) result (rest result))
        ; 不同元素数量不超过max-distinct
        (max-distinct-constraint result max-distinct)))))

(defn main []
  (run! println
    (solve
      [10 10 50]
      [10 20 45 50]
      20
      2)))

运行这段代码后,你会得到预期的结果,比如[20 45 50]、[45 45 50]、[50 50 50]等。

内容的提问来源于stack exchange,提问作者mmachenry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 08:12:47