程序员的算法趣题Q37: 翻转7段码(1)

发布时间:2022-07-05 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了程序员的算法趣题Q37: 翻转7段码(1)脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

目录

1. 问题描述

2. 解题分析

3. 代码及测试


1. 问题描述

程序员的算法趣题Q37: 翻转7段码(1)

        问题:求把10个数字全部显示出来时,亮灯/灭灯的切换次数最少的显示顺序,以及这个切换次数。

        注:原题从答案来看是不包括第一个数字显示时从全黑到显示该数字所需要的亮灭切换次数的。不过这个不影响解题,但是可能会影响答案。

2. 解题分析

        从暴力搜索(原书中使用“全量搜索”这个词很贴切)着手。

        10个数字的不同排列顺序共有10!=factorial(10)=3628800种,针对每一种排列顺序进行切换次数的统计即可。

        本题解中用numpy array存储各数字显示所需要的灯亮灭表示矩阵,每行表示一个数字,表示使用7段码对应的二进制表示。

        切换次数的计算可以理解为两个二进制序列之间的汉明距离(hamming distance),即两个二进制序列之间的不同的数的个数。汉明距离可以用异或操作实现。

        此外,各数字的二进制表示之间的汉明距离总共只有10*10=100个(连自己与自己之间的距离也算上,虽然不用。考虑到查找实现的便利,(k,j)(j,k)也分开来算,虽然它们是相等的)可以预先计算好,这样可以避免在遍历搜索时的大量的重复计算。

3. 代码及测试

# -*- coding: utf-8 -*-
"""
Created on Wed Sep 22 07:37:19 2021

@author: chenxy
"""
import Sys
import time
import datetime
import math
import random
From   typing import List
# from   queue import Queue
from   collections import deque
import ITertools as it
from   math import sqrt, floor, ceil
import numpy as np


class Solution:
    segs = np.array([[1,1,1,1,1,1,0],
            [0,1,1,0,0,0,0],
            [1,1,0,1,1,0,1],
            [1,1,1,1,0,0,1],
            [0,1,1,0,0,1,1],
            [1,0,1,1,0,1,1],
            [1,0,1,1,1,1,1],
            [1,1,1,0,0,0,0],
            [1,1,1,1,1,1,1],
            [1,1,1,1,0,1,1]])
    distArray  = dict()
    minDistSum = np.sum(segs)
    minOrder   = None
    
    def initDistArray(self):
        for i in range(len(self.segs)):
            for j in range(len(self.segs)):
                self.distArray[tuple([i,j])] = np.sum(self.segs[i] ^ self.segs[j])

    def PRintDistArray(self):
        print(self.distArray)
        
    def minToggle(self):
        for order in it.PErmutations(list(range(10))):
            # print(order)
            distSum = 0 #np.sum(self.segs[0])
            for k in range(9):
                distSum += self.distArray[(order[k],order[k+1])]
            if self.minDistSum > distSum:
                self.minDistSum = distSum
                self.minOrder = order
        return self.minDistSum, self.minOrder
                        
if __name__ == '__main__':        
    
    sln = Solution()
    sln.initDistArray()
    # sln.printDistArray()        
    
    t1 = time.perf_counter()
    minDistSum, minOrder = sln.minToggle()
    tCost = time.perf_counter() - t1      
    print('Number of toggles = {0}, order = {1}, tCost = {2}'.format(minDistSum, minOrder,tCost))  
    
                        
        

运行结果:

Number of toggles = 13, order = (0, 8, 6, 5, 9, 4, 1, 7, 3, 2), tCost =  8.640sec

4. 后记

        运行时间有点长,需要考虑进一步优化。

        这个问题其实可以看作是,具有10个节点的全连接无向图,每条边的权重值代表两个节点所代表的数字的7段码显示的二进制表示之间的汉明距离。因此本题就转化为就该图中任意一条最长无重复路径的权重总和。这其实就是著名(恶名昭彰)的旅行商问题。这是一个NP问题,但是只有10个节点还可以对付。接下来考虑用递归+;memoization的方法来试一试会不会变得更快一些。

        上一篇:Q36: 翻转骰子

        下一篇

        本系列总目录参见:程序员的算法趣题:详细分析和Python全解

脚本宝典总结

以上是脚本宝典为你收集整理的程序员的算法趣题Q37: 翻转7段码(1)全部内容,希望文章能够帮你解决程序员的算法趣题Q37: 翻转7段码(1)所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。