Latin square计算求解

学号sha256(哈希):2a3979ec090637531da9c78f3a1e562a1ef858ec2caec000bf4e0f943124a699
拉丁正交矩阵,已用于国科大算法实验作业,原创烂码,只是个人纪录,别喷我。
模式为先生成,后回溯对比所以复杂度较高。期末时间紧迫没有时间深入探索,先这样,后面再来降低计算复杂度。
#实验
import sys  # 导入sys模块
sys.setrecursionlimit(30000)  # 将默认的递归深度修改为30000
import copy
ndimen=4#计算维度
a=b=list(range(1,ndimen+1))
import numpy as np
import time
#start = time.time()
start = time.perf_counter()
lnegth=ndimen

test=[]
listqpl = []
#下面为全排列函数
def quanpl(n,begin):#n是需要排序的列表,begin是本次需要排列的头指针(下标),也可以看作是前面已固定的元素个数
    """
    以三阶为例[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2]]
    :param n: 需要排列的阶数
    :param begin: 回溯使用的位置标记
    :return: 全排列列表
    """
    end=len(n)#记录元素个数
    i=0
    if begin==end: #递归的结束条件,全部都已固定,可以输出
        listqpl.append(copy.copy(n))#全局最坑的点,需要浅拷贝,n的父类对象,子对象改变可以被拷贝出去
        return listqpl
    else:
        i=begin #指向本次需要排列的第一个位置(本轮需要固定的位置)
    for num in range(begin,end):#num遍历本次需要排列的序列中的每一个数,
        n[num],n[i]=n[i],n[num]#依次把这些数放在固定的位置上,方法是交换
        quanpl(n,begin+1) #不看那个已经固定的数,进行递归
        n[num],n[i]=n[i],n[num] #回溯,还原上一布操作,回到上一步,再进行下一次for循环

def jiechenghanshu (x):#求N!
    a=1
    for i in range(1, x + 1):
        a = a * i
    return a


quanpl(a,0)
def liebiaofenge(testlist):
    """
    以三阶为例[[[1, 2, 3], [1, 3, 2]], [[2, 1, 3], [2, 3, 1]], [[3, 2, 1], [3, 1, 2]]]

    :param testlist: 为了下面的行全组合,以首元素进行分割
    :return: 返回首元素相同的分割矩阵
    """
    dim = len(testlist[0])#探索组合矩阵维度
    linesample=[]
    cou=jiechenghanshu(dim-1)#分割长度,因为首元素固定,后面全排列,所以有dim-1!个首元素相同
    """
    也可以用下面函数,可能速率会慢
    from functools import reduce
    #使用lambda,匿名函数,迭代
    num = reduce(lambda x,y:x*y,range(1,dim))
    """
    duan=int(len(testlist)/cou)#分割段数,首元素固定,后面全排列
    for i in range(duan):#按照段数分割为首元素相同的行
        linesample.append(testlist[cou*i:cou*(i+1)])
    return linesample#返回分割的元素
#liebiaofenge(listqpl)


coutlit=[]
#
linesample=liebiaofenge(listqpl)
def zuhefunction(testlit,cout,num):#矩阵组合函数,输入(全组合列表),cout为计数器
    """
    采用先分割成单个拉丁矩阵,然后开始一一对比,此为拉丁方阵搜索器
    :param testlit: 需要组合的全排列列表
    :param cout: 组合过程中,已组合的行数
    :param num: 回溯使用的临时矩阵,用来存储组合的列表
    :return: 返回全组合的拉丁方阵,不强调行排列,只强调列全排列组合
    """
    if cout==len(testlit)  :#将最底层的递归传入列表
        if len(num)==len(num[0]):
            coutlit.append(copy.copy(num))
        num =[]
        #print(coutlit)
        return coutlit
    for i in testlit[cout]:#以分相同首元素列表元素为循环次数
        #linecopare()
        if num == []:
            num.append(i)  # 将每一层的列表传入
        else:
            width = len(num)
            linesnum = len(i)
            tap=0
            for k in range(1, linesnum):  # 首列元素相同
                for m in range(width):
                        if i[k]== num[m][k]:
                            tap=1
                            break
                if tap == 1:  # 中断循环
                    break
            if tap==0:
                num.append(i)
        zuhefunction(testlit,cout+1,num)#递归函数
        if i in num:
            num.remove(i)  # 控制变量,参与组合的直接删除
lastladin=[]

def pipei(tex1,tex2,lasttem):
    if tex2 == [] and tex1==[] and len(lasttem)==lnegth*lnegth:#判断是否匹配完成,完成则返回拉丁正交矩阵
        lastladin.append(copy.copy(lasttem))
        lasttem=[]
        return lastladin
    for i in range(len(tex1)):#第一个矩阵按顺序循环提取
        tempos=copy.copy(tex1[i])
        tex1.remove(tex1[i])
        for j in range(len(tex2)):#第二个矩阵按顺序与前一个匹配
            sum=0
            for k in range(len(tex2[j])):
                if [tempos[k],tex2[j][k]] not in lasttem:#判断是否已经存在
                    sum+=1
                else:
                    break
            if sum==len(tex2[j]):
                for m in range(len((tex2[j]))):
                    lasttem.append([tempos[m], tex2[j][m]])
                temnum=tex2[j]
                tex2.remove(tex2[j])
                pipei(tex1,tex2,lasttem)#递归
                tex2.insert(j,temnum)#第二个矩阵回溯
            lasttem = []#第一个矩阵的一行与下游矩阵匹配跑完,代表以此行为开头的拉丁矩阵搜索结束,临时匹配矩阵置空
        tex1.insert(i,tempos)#第一个矩阵回溯
        #


def sousuolading(coutlit):#循环对比,使用全排列对比N!+N-1!+....1
    """
    下面是打破重复计算的过程,把相同的比较矩阵只比较对角线相同的矩阵,其余矩阵均是/
    对角线矩阵经过列变换得来的
    :param coutlit:需要搜索的拉丁方阵集合
    :return:搜索的正交拉丁方阵
    """
    freecopy=copy.deepcopy(coutlit)
    for i in range(len(coutlit)):
        for j in freecopy[i+1:len(coutlit)]:
            pipei(coutlit[i], j, [])
        sum = 0
        coutnum=0
        for m in coutlit[i]:
            if m[coutnum] == 1:  # 只重复和对角为1的矩阵自身对比
                sum += 1
            else:
                break#中断程序,防止不必要重复计算,
            coutnum += 1
        if sum==lnegth:
            pipei(coutlit[i], freecopy[i], [])#此处即使相同的矩阵也需要使用深拷贝,如果使用相同coutlit[i],则可能会同时改变,无法出现结果
    return lastladin




if __name__=="__main__":
    quanpl(a, 0)
    liebiaofenge(listqpl)
    zuhefunction(linesample,0,[])
    sousuolading(coutlit)
    #print(lastladin)
    print(len(lastladin))
    #print(np.array(lastladin).reshape(len(lastladin),ndimen,2*ndimen))
    delta = time.perf_counter() - start
    print("程序运行的时间是:{}秒".format(delta))

你可能感兴趣的:(python)