n个数取m个进行全排列算法(python)

之前使用c++ 写了一个全排列,

今天写了python版本,实现对实际应用题的解析

# 比如有N个数,选出M个排列,并且满足一定条件,则有多少种可能,或者极限值是多少?
# 比如1~8的数字,排列为一个5位数,能被99整除的最大数为多少?
# 正常来解,需要考虑满足9和11分别整除特性,

# 全局的结果集合,集合中每1条也是一个list
result = []    
mapIndex = {}     # 从字符到索引的反向索引表,用于检查那个已经使用了
callback = None   # 过滤函数的指针,初始为空

############################################################
# 检查所有的标记是否都已经标记用过了,没有使用的返回索引,或者返回-1
def isUsedAll(used):
    for i in range(len(used)):
        if used[i] == 0:
            return i

    return -1

# 设置倒排索引
def setMap(lst):
    global mapIndex

    n = len(lst)
    for i in range(n):
        mapIndex[lst[i]] = i

# 根据值取索引
def getIndexBy(a):
    global mapIndex
    return mapIndex[a]

# 根据当前前缀生成一个列表,设置使用哪些
def markUsed(preFix):
    global  mapIndex

    n = len(preFix)
    used = [0] * len(mapIndex)
    for i in range(n):
        index = getIndexBy(preFix[i])
        used[index] = 1
    return used

#以当前前缀,测试其他没有标记使用过的

#  当数字栈为空时返回,
#  当前标记全使用完了,则输出序列,并退栈
#  当前标记没有使用完,则先选下一个可用的,标记,并对2个栈同时入栈
#  每一轮前缀的深度过程中,前缀部分不变,但是使用标记不停地增加,直到所有的标记都用完
def PermutationOneRoot(lst, stackNum, stackUsed, m):
    global result
    global callback

    # 开始循环
    while (len(stackNum) > 0):
        currentPrefix = stackNum[-1]    # 当前的前缀
        currentUsed = stackUsed[-1]     # 当前前缀情况下,遍历了几个了?
        if (len(currentPrefix) >= m):   # 达到了指定的长度,保存记录,并退栈
            if (callback != None):      # 如果设置了过滤函数
                if callback(currentPrefix):
                    result.append(currentPrefix)
            else:                       # 没有过滤函数直接添加
                result.append(currentPrefix)
            stackUsed.pop()
            stackNum.pop()
            continue                    # 执行下一轮循环

        index = isUsedAll(currentUsed)  # 当前前缀不足M个,则需要寻找下1个填充的的后缀
        if (index == -1):               # 当前前缀遍历结束,退栈
            stackUsed.pop()
            stackNum.pop()        
            continue
        else:
            currentUsed[index] = 1          # 标记使用了新找到的后缀

            tmpPrefix = list(currentPrefix) # 拷贝当前前缀,补1个,生成一个新的前缀,
            tmpPrefix.append(lst[index])    # 
            stackNum.append(tmpPrefix)      # 压栈

            tmpUsed = markUsed(tmpPrefix)   # 根据新前缀标记新的使用标记
            stackUsed.append(tmpUsed)       # 压栈

    return

#end of oneroot


# 输入一组数字,输出结果
def Permutation(lst, m):
    stackNum = []
    stackUsed = []
    if (m > len(lst)):
        m = len(lst)

    setMap(lst)

    # 每个数字分别作为前缀执行一遍:
    n = len(lst)

    for i in range(0, n):  
        # 当前数字为根节点的前缀,压栈,只有1个元素
        currentData = [ lst[i] ]
        stackNum.append(currentData)

        used = [0] * n  # 初始化当前使用标记,压栈
        used[i] = True
        stackUsed.append(used)
        # 以每个数字为根节点,分别执行一次深度遍历排列
        PermutationOneRoot(lst, stackNum, stackUsed, m)

#end of permutation

############################################################
# 准备开始使用:
# 
# 打印结果
def printRet():
    for i in result:
        print(i)

# 转换为数字
def printRetAsNum():
     for tmpList in result:
        sum = 0
        for i in tmpList:   
            sum = sum * 10 + i
        print(sum)

# 这里定义用来扩展逻辑的回调函数
def checkCallBack(tmpList):
    sum = 0
    for i in tmpList:   # 转换为数字
        sum = sum * 10 + i

    if (sum % 99) == 0:
        return True

    return False
# end callback
############################################################
lst = [1, 2, 3, 4, 5, 6, 7, 8]
m = 5

# 入口函数
callback = checkCallBack
Permutation(lst, m)
#printRet()
printRetAsNum()

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值