明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 7673|回复: 32

[讨论] 【e派】数独——LSP求解

  [复制链接]
发表于 2018-3-26 22:38:19 | 显示全部楼层 |阅读模式
本帖最后由 xyp1964 于 2018-3-27 07:40 编辑

数独的布局分为行、列和宫三类,利用LSP的表处理强项,实现数独求解也许可以练练手……
81码数字字符串:
000000980020010000004057010000000506019060340203000000080420100000070030076000000

起始布局如下:


有兴趣的可以来讨论讨论。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x

评分

参与人数 2金钱 +65 收起 理由
qjchen + 15 很给力!
叮咚 + 50 赞一个!

查看全部评分

"觉得好,就打赏"
还没有人打赏,支持一下
发表于 2018-3-29 18:56:20 | 显示全部楼层
本帖最后由 yxp 于 2018-3-30 00:02 编辑

最近经常开会无聊的时候就玩玩数独,顺手查了一下数独的相关知识。
终盘数量:
数独中的数字排列千变万化,那么究竟有多少种终盘的数字组合呢?
答案是6,670,903,752,021,072,936,960(约为6.67×10的21次方)种组合,2005年由Bertram Felgenhauer和Frazer Jarvis计算出该数字,并将计算方法发布在他们网站上,如果将等价终盘(如旋转、翻转、行行对换,数字对换等变形)不计算,则有5,472,730,538个组合。数独终盘的组合数量都如此惊人,那么数独题目数量就更加不计其数了,因为每个数独终盘又可以根据提示数的多寡,制作出无数道合格的数独题目。

标准数独:
目前(截止2011年)发现的最少提示数9×9标准数独为17个提示数,截止2011年11月24日16:14,共发现了非等价17提示数谜题49151题,此数量仍在缓慢上升中,如果你先发现了17提示数的题目,可以上传至“17格数独验证”网站,当然你也可以在这里下载这49151题。
Gary McGuire的团队在2009年设计了新的算法,利用Deadly Pattern的思路,花费710万小时CPU时间后,于2012年1月1日提出了9×9标准数独不存在16提示唯一解的证明,继而说明最少需要17个提示数。并将他们的论文以及源代码更新在2009年的页面上。


看见e派用了自定义函数,用标准lisp函数也不是很麻烦,关键是求解数独的算法。

  1. ;; sudoku-str->row 字符串转行表,传入参数 str
  2. ;;(setq str "000000980020010000004057010000000506019060340203000000080420100000070030076000000")
  3. (defun sudoku-str->row(str)(DivLst (mapcar 'atoi (mapcar 'chr (vl-string->list str))) 9))


  4. ;; sudoku-row->col 行表转列表,传入参数 lst
  5. ;;(setq lst (sudoku-str->row str))
  6. (defun sudoku-row->col (lst / NL S N)
  7. (while (car lst)
  8.   (foreach x lst (setq S (cons (car x) S) N (cons (cdr x) N)))
  9.   (setq lst (reverse N) NL (cons (reverse S) NL) S nil N nil)
  10. )(reverse NL)
  11. )


  12. ;; sudoku-row->9gg 行表转宫表,传入参数 lst
  13. ;;(setq lst (sudoku-str->row str))
  14. (defun sudoku-row->9gg(lst / SN)
  15. (foreach x (sudoku-row->col (mapcar '(lambda (x)(DivLst x 3)) lst))
  16. (setq SN (cons (DivLst (apply 'append x) 9) SN)))
  17. (apply 'append (reverse SN))
  18. )
  19.   

  20. ;;定长分割表函数
  21. (defun DivLst(lst n / a b)
  22. (while lst
  23.   (setq b nil i 0)
  24.   (while (< i n) (setq b (cons (car lst) b) lst (cdr lst) i (1+ i)))
  25.   (setq a (cons (reverse b) a))
  26. )(reverse a)
  27. )




回复 支持 1 反对 0

使用道具 举报

发表于 2018-3-30 13:10:26 | 显示全部楼层
本帖最后由 yxp 于 2018-4-1 14:47 编辑

回溯法
一、在第一个格子填上1,检查行、列和九宫格是否合法。如果合法,跳入下一个格子,从1开始测试;如果不合法,则继续测试2、 3......
二、如果下一个格子1-9都测试完了,全部不合法,说明上一个格子填错了,则回溯到上一步,继续测试2、 3......
所有格子测试的数字只有1-9,所以不存在死循环的问题。


  1. ;;*******************************************************
  2. ;;          LISP 数独求解:回溯法(穷举法)
  3. ;;*******************************************************
  4. ;;回溯法主程序
  5. (defun c:sudoku2( / str m x sulsp xSudo)
  6. (setq str "000000980020010000004057010000000506019060340203000000080420100000070030076000000")
  7. ;(setq str "800000000003600000070090200050007000000045700000100030001000068008500010090000400")
  8. (setq m 0 x 1 sulsp (str->row str) xSudo nil)
  9. (sudoku-print (DivLst sulsp 9)) ;;显示题目
  10. (setq *s1* (getvar "date")) ;;计时开始
  11. (while (< m 81)                           ;;测试数独的81个格子,m 为格子指针值为 0-80
  12.         (setq hl9g (Cal99 m (updateLst xSudo sulsp)))
  13.         (if (zerop (nth m sulsp))(progn       ;;如果m格子里数据为0,则进入循环
  14.                 (while (and (<= x 9)(apply 'or (mapcar '(lambda(y)(member x y))hl9g)))(setq x (1+ x)));;合法性
  15.                 (if (> x 9)                       ;;当x>9时,显然该回溯了
  16.                         (while (>= x 9)
  17.                                 (setq x (car xSudo) xSudo (cdr xSudo) ;;数据回溯遇到 9 则继续回溯
  18.                                         x (1+ x) m (1- m)) (entdel(entlast))
  19.                                 (while (> (nth m sulsp) 0)(setq m (1- m))) ;;指针回溯稍微麻烦,遇到原始数据则需要继续
  20.                         )(setq xSudo (cons x xSudo) LJ (dxftxt (itoa x) m) x 1 m (1+ m))   ;;x 归位,m 跳入下一格
  21.                 ))(setq m (1+ m) x 1)   ;;如果下一个格子有数据,即 m 格子数据不为 0,则跳入下一格, x 复位
  22.         )
  23. ) (princ (strcat "\n数独回溯法求解耗时 " (rtos (* 86400.0 (- (getvar "date") *s1*)) 2 4) " 秒"))
  24. (princ)
  25. )

  26. ;;行、列、九宫计算
  27. ;;返回一维表数字 L 中第 n 个数字所在的行、列和九宫数的组合
  28. (defun Cal99(n L / mn mr mc ms k Lr Lc Ls)
  29. (setq k 0 Lr nil Lc nil Ls nil
  30.         mn (Calxm n)
  31.         mr (car mn)
  32.         mc (cadr mn)
  33.         ms (caddr mn))
  34. (foreach x L (setq kk (Calxm k) k (1+ k))
  35.         (if (= (car kk) mr)(setq Lr (cons x Lr)))    ;;行
  36.         (if (= (cadr kk) mc)(setq Lc (cons x Lc)))   ;;列
  37.         (if (= (caddr kk) ms)(setq Ls (cons x Ls)))) ;;宫
  38. (list Lr Lc Ls)
  39. )

  40. ;;返回第 m 个数字在九宫格里的行、列、宫号,均为 0-8
  41. (defun Calxm(m / r c s)
  42. (setq r (/ m 9) c (- m (* r 9)) s (+ (* 3 (/ r 3))(/ c 3)))
  43. (list r c s)
  44. )

  45. ;;将计算出的数独 L1 与原始数独 L2 合并
  46. (defun updateLst(L1 L2)
  47. (setq xL (reverse L1) NL '())
  48. (foreach x L2 (if (and (= x 0)(car xL))
  49.         (setq NL (cons (car xL) NL) xL (cdr xL))
  50.         (setq NL (cons x NL))))
  51. (reverse NL)
  52. )

  53. ;;字符串到一维数据表
  54. (defun str->row(str)(mapcar 'atoi (mapcar 'chr (vl-string->list str))))


  55. ;;定长分割表函数
  56. (defun DivLst(lst n / a b)
  57. (while lst
  58.         (setq b nil i 0)
  59.         (while (< i n) (setq b (cons (car lst) b) lst (cdr lst) i (1+ i)))
  60.         (setq a (cons (reverse b) a))
  61. )(reverse a)
  62. )


  63. ;;*******************************************************
  64. ;;           数独在CAD中绘制
  65. ;;*******************************************************
  66. ;;在CAD中输出九宫格 (sudoku-print lst)
  67. ;;(setq lst (sudoku-str->row str))
  68. (defun sudoku-print(lst / dx dy pt0 en)
  69. (setvar "cmdecho" 0)
  70. (setvar "peditaccept" 0)
  71. (setq pt0 (getpoint "\n指定点:"))
  72. (or pt0 (setq pt0 '(0 0 0)))
  73. (setq dx 0 dy 0 zx0 (car pt0) zy0 (cadr pt0))
  74. (command "line" pt0 (polar pt0 0 18) "")
  75. (setq en (entlast))
  76. (command "-array" en "" "r" 10 1 -2)
  77. (line-WPL (line-WPL en))
  78. (command "line" pt0 (polar pt0 (r2a -90) 18) "")
  79. (setq en (entlast))
  80. (command "-array" en "" "r" 1 10 2)
  81. (line-WPL (line-WPL en))
  82. (command "zoom" (polar pt0 (r2a 135) 8) (polar pt0 (r2a -45) 34))
  83. (foreach y lst
  84.         (foreach x y
  85.                 (if (/= x 0)(command "text" "j" "mc"
  86.                         (list (+ (car pt0) (* 2 dx) 1)(- (cadr pt0) (* 2 dy) 1)) "1" "0" (itoa x)))
  87.                 (setq dx (+ dx 1)))
  88.         (setq dy (+ dy 1) dx 0)
  89. )(princ)
  90. )

  91. ;;角度到弧度的转换
  92. (defun R2A(R)(* pi (/ R 180.0)))

  93. (defun line-WPL(en)
  94. (setq en (entnext (entnext (entnext en))))
  95. (command "pedit" en "y" "w" "0.15" "") en
  96. )
  97. ;;显示求解出的数字
  98. (defun dxftxt(str n / a b)
  99. (setq a (/ n 9) b (- n (* a 9))
  100.         pt (list (+ 1 zx0 (* b 2))(- zy0 (* a 2) 1) 0))
  101. (entmake (list '(0 . "TEXT")(cons 1 str)(cons 72 1)(cons 10 pt)(cons 11 pt)
  102.         (cons 40 1)(cons 62 1)(cons 71 0)(cons 73 2)))
  103. (command "zoom" (polar (list zx0 zy0) (r2a 135) 8)(polar (list zx0 zy0) (r2a -45) 34))
  104. )


 楼主| 发表于 2018-3-26 22:39:39 | 显示全部楼层
先来看看效果:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2018-3-26 22:46:35 | 显示全部楼层
  1. ;; suduku-str->row 字符串转行表
  2. ;; (suduku-str->row "000000980020010000004057010000000506019060340203000000080420100000070030076000000")
  3. (defun suduku-str->row (str) (if (= (strlen str) 81) (xyp-List-Div (xyp-Str2List str) 9)))

  4. ;; suduku-row->col 行表转列表
  5. (defun suduku-row->col (lst-row) (xyp-List-Reverse lst-row))

  6. ;; suduku-row->9gg 行表转宫表
  7. (defun suduku-row->9gg (lst-row / lst-9gg)
  8.   (setq        lst-9gg        (mapcar '(lambda (x) (xyp-List-Div x 3)) lst-row)
  9.         lst-9gg        (xyp-List-Div lst-9gg 3)
  10.         lst-9gg        (mapcar 'xyp-List-Reverse lst-9gg)
  11.         lst-9gg        (apply 'append lst-9gg)
  12.         lst-9gg        (mapcar '(lambda (x) (apply 'append x)) lst-9gg)
  13.   )
  14. )

  15. ;; suduku-9gg->row 宫表转行表
  16. (defun suduku-9gg->row (lst / lst1 x y)
  17.   (setq        lst1 (xyp-List-Div lst 3)
  18.         lst1 (mapcar '(lambda (x)
  19.                         (xyp-List-Reverse (mapcar '(lambda (y) (xyp-List-Div y 3)) x))
  20.                       )
  21.                      lst1
  22.              )
  23.         lst1 (apply 'append lst1)
  24.         lst1 (mapcar '(lambda (x) (apply 'append x)) lst1)
  25.   )
  26. )
发表于 2018-3-27 08:02:33 | 显示全部楼层
不错,不错,玩出了花样
发表于 2018-3-27 16:58:27 | 显示全部楼层
厉害、非常厉害!
发表于 2018-3-27 17:00:45 | 显示全部楼层
请问算法是什么?以前vba做过的是笨方法,一个个去试,试到合格为止。
发表于 2018-3-27 20:11:19 来自手机 | 显示全部楼层
关键是算法
发表于 2018-3-28 07:38:44 | 显示全部楼层
好玩法,代码用的什么编译器啊,我的发上来不是这样的格式,还要调格式的

点评

yxp
发帖的时候点 代码 ,会弹出一个小窗口,然后选择 lisp,将代码粘贴进小窗口  发表于 2018-3-29 21:12
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-11-25 21:49 , Processed in 0.203501 second(s), 29 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表