有个运营妹纸心算找不到数独答案,心一横干脆写代码实现

0、代码写很差,心痛吖

家里新购了一个数独,周四午餐后消食便拿起来玩,半小时过去了,一小时过去了,一直没成功……超级不服气的,这道难题想逼我是吗?干脆直接写python代码破解好了!半小时后代码还没写好,我被本身蠢哭了。在写代码这件事上,我显然手生得很呢。值得欣慰的是,我起码知道生物脑没法完成的,电脑能够代劳,只不过这代码依然须要生物脑来完成。python

趁着端午小长假有点时间,就来琢磨下这件事好了。算法

一、不擅长写代码,总擅长需求分析吧?

计算目标:找到数独的答案。是找一些答案,仍是找出全部答案呢?后者难度高,且包含前者,我选择后者。框架

数独规则:在下述六角形棋盘的12个位置中分别放入数字1至12,使得图中每条连线4个位置加和为26。dom

shudu

二、生物脑和电脑在演算方法上的迥异

个人生物脑是如未尝试解出答案的呢?函数

从棋盘取下全部数字,先随意选择4个数字放入某条线使之加和为26,而后再选择3个数字放入与此线有交点的另一条线使之加和为26,而后尝试第3条线……结果是,每次尝试到第三、4条线时开始吃力,偶尔能知足5条线但最后一条线没法知足条件。优化

我将如何用代码来指挥电脑演算呢?用代码复原生物脑的思路,显然是很是蠢的解法。大家知道我是如何知道本身蠢的吗?由于上述思路拆解出来就是:spa

  1. 指定前3个位置数值,计算得出第4个数,使得一条线==26
  2. 选择剩余5条线中已获得赋值的位置最多的那条线,计算得出使该条线加和为26的数值
  3. 循环以上,尝试使全部线的加和都知足26;若是不知足,则从新指令最初3个位置的数值

我彻底没法快速用代码表达出以上思路,基于此不难自我判断上述算法是愚蠢的。code

通过一些摸索后,我把碳基问题抽象为下述硅基问题:cdn

  1. 棋盘12个位置的不一样取值,构成了一个列表。
  2. 天然数1至12有多少种排列组合的方式,就有多少个上述列表。
  3. 对于每一个列表,计算特定位置的加和(6条线)是否知足条件,是通用的,能定义为函数。
  4. 穷举天然数1至12全部的排列组合方式。

3. 多说无益,秀出代码

3.1 版本1.0:能找到部分答案,但找不到全部答案

import math
import random

def areyouwin(point_list):
    if point_list[0] + point_list[6] + point_list[11] + point_list[4] != 26 :
        return False
    elif point_list[0] + point_list[7] + point_list[8] + point_list[2] != 26 :
        return False
    elif point_list[1] + point_list[7] + point_list[6] + point_list[5] != 26 :
        return False
    elif point_list[1] + point_list[9] + point_list[8] + point_list[3] != 26 :
        return False
    elif point_list[4] + point_list[9] + point_list[10] + point_list[2] != 26 :
        return False
    elif point_list[3] + point_list[10] + point_list[11] + point_list[5] != 26 :
        return False
    else:
        return True

point_list = [1,2,3,4,5,6,7,8,9,10,11,12]
k = 0
while k < 10 :#此处k表示找出10个答案,想要更多答案就改k的值便可
    random.shuffle(point_list)#新知识点:随机打乱列表
    if areyouwin(point_list):
        print(point_list,k+1)
        k = k + 1
复制代码

3.2 版本2.0:本身动手,穷举全部排列组合

初步搜索,我没有找到如何实现输出列表项所有的排列组合。鉴于很是清楚本身对排列组合的代码实现接近于0经验,因而故意想要本身写代码实现。blog

计算目标的大框架已经实现,如今只缺排列组合。那我聚焦于此:

子目标:输出天然数1至12的全部排列组合

演算方法是怎样的呢?

  1. 位置1的取值范围为1至12,先取值1,完成下列全部演算后,再取值2,完成下列全部演算,再取值3……直至完成取值范围内的全部数值
  2. 位置2的取值范围为除了位置1当前取值以外的11个数,先取值其中一个数值,完成下列全部演算后,再取值另一个数值,完成下列全部演算后,再取值第3个数值……直至完成取值范围内的全部数值
  3. 位置3的取值范围为除了位置1和位置2当前取值以外的10个数,先取值其中一个数值,完成下列全部演算后,再取值另一个数值,完成下列全部演算后,再取值第3个数值……直至完成取值范围内的全部数值
  4. 位置4的取值范围为除了位置1和位置2当前取值以外的9个数,……
  5. ……
  6. 位置12的取值范围为除了位置1至11当前取值以外的11个数,此为惟一取值。输出位置1至位置12的当前取值

这个演算方法可行吗?代码将如何写?彷佛有些困难。那我我先试着简化问题:输出天然数1至3的全部排列组合。并写出代码来验证结果是否正确。

point_list = [1,2,3]

#列表A赋值给列表B时须要用切片的方式,不然列表B改变时会引起列表A改变!
point_1_list = point_list[:]
for i in point_1_list:#实现位置1的循环
    point_2_list = point_1_list[:]
    point_2_list.remove(i) #位置2的取值范围是位置1的取值范围移除位置1的当前值
    #print(point_2_list)
    for j in point_2_list:
        #print(j,"j的值")
        point_3_list = point_2_list[:]
        point_3_list.remove(j)#位置3的取值范围是位置2的取值范围移除位置1的当前值
        k = point_3_list[0]#位置3的取值范围只剩1个数值
        print(i,j,k)#打印排列组合结果
复制代码

在完成上述代码的过程当中,刚开始列表赋值并无采用切片的方式,致使全部列表均发生改变。无他,仍是反映我对列表的操做不熟。

先不论代码是否简洁优雅,至少它在功能上实现了。那么如今,先试着用这个思路实现天然数1至12的所有排列组合,并计算得出数独26的解法共有多少个!

下面就是不简洁不优雅版本的数独全部答案的代码:

def areyouwin(point_list):
    if point_list[0] + point_list[6] + point_list[11] + point_list[4] != 26 :
        return False
    elif point_list[0] + point_list[7] + point_list[8] + point_list[2] != 26 :
        return False
    elif point_list[1] + point_list[7] + point_list[6] + point_list[5] != 26 :
        return False
    elif point_list[1] + point_list[9] + point_list[8] + point_list[3] != 26 :
        return False
    elif point_list[4] + point_list[9] + point_list[10] + point_list[2] != 26 :
        return False
    elif point_list[3] + point_list[10] + point_list[11] + point_list[5] != 26 :
        return False
    else:
        return True

point_list = [1,2,3,4,5,6,7,8,9,10,11,12]

#列表A赋值给列表B时须要用切片的方式,不然列表B改变时会引起列表A改变!
point_1_list = point_list[:]
for p_1 in point_1_list:#实现位置1的循环
    point_2_list = point_1_list[:]
    point_2_list.remove(p_1) #位置2的取值范围是位置1的取值范围移除位置1的当前值
    for p_2 in point_2_list:
        point_3_list = point_2_list[:]
        point_3_list.remove(p_2)#位置3的取值范围是位置2的取值范围移除位置1的当前值
        for p_3 in point_3_list:
            point_4_list = point_3_list[:]
            point_4_list.remove(p_3)
            for p_4 in point_4_list:
                point_5_list = point_4_list[:]
                point_5_list.remove(p_4)
                for p_5 in point_5_list:
                    point_6_list = point_5_list[:]
                    point_6_list.remove(p_5)
                    for p_6 in point_6_list:
                        point_7_list = point_6_list[:]
                        point_7_list.remove(p_6)
                        for p_7 in point_7_list:
                            point_8_list = point_7_list[:]
                            point_8_list.remove(p_7)
                            for p_8 in point_8_list:
                                point_9_list = point_8_list[:]
                                point_9_list.remove(p_8)
                                for p_9 in point_9_list:
                                    point_10_list = point_9_list[:]
                                    point_10_list.remove(p_9)
                                    for p_10 in point_10_list:
                                        point_11_list = point_10_list[:]
                                        point_11_list.remove(p_10)
                                        for p_11 in point_11_list:
                                            point_12_list = point_11_list[:]
                                            point_12_list.remove(p_11)
                                            p_12=point_12_list[0]
                                            one_list=[p_1, p_2, p_3, p_4, p_5, p_6, p_7, p_8, p_9, p_10, p_11, p_12]

                                            if areyouwin(one_list):
                                                print(one_list)
复制代码

3.3 还有什么遗憾?

上述代码for循环嵌套代码部分,不够简洁优雅。如何优化,是我接下来须要再琢磨的。笨办法也是办法,终于把数独的答案所有演算出来了,终于能安心睡个好觉了。

这就完了吗?固然不!这个碳基转硅基的解决方式中,有个内核的问题是,棋盘是个对称的六角星型,因而碳基世界的正确答案,其实只需硅基代码演算出答案的1/6。

以正确答案[1,2,3,6,11,7,5,12,10,8,4,9]为例,棋盘每转动60°就产生一个新答案: [2,3,6,11,7,1,12,10,8,4,9,5] [3,6,11,7,1,2,10,8,4,9,5,12] [6,11,7,1,2,3,8,4,9,5,12,10] [11,7,1,2,3,6,4,9,5,12,10,8] [7,1,2,3,6,11,9,5,12,10,8,4]

咿??如何优化代码,能撇掉那5/6臃肿的答案呢?——难度超纲,我仍是暂时放下吧。

一句话小结

代码写得好很差,手熟与否很关键呀!而手熟并不是简单的常常写就够了,而是要专门分解出哪些基础功或知识点薄弱,并重点突破之。此乃大咖们常曰的“刻意练习”。

相关文章
相关标签/搜索