编程的乐趣:用Python解算法谜题 课后习题答案

说明:作者已经给了各章习题1的答案,其他的习题答案我来补充下,使用语言为Python3,有问题欢迎留言。

附习题一答案:https://mitpress.mit.edu/books/programming-puzzled
点击左下角 Code for Selected Puzzle Solutions 即可下载。

谜题 1 保持一致

1.10 习题(page11)

  习题 2 修改pleaseConformOnepass,如习题1那样打印更自然一些的命令,并确保程序再输入是空列表时不会崩溃。
  提示:你需要记住区间的起始位置(而不是在第6行打印)。

'''在line-opt.py上修改'''
def pleaseConformOnepass(caps):
    if caps != []:	#判断是否为空
        caps = caps + [caps[0]]
        for i in range(1, len(caps)):
            if caps[i] != caps[i-1]:
                if caps[i] != caps[0]:
                    temp = i	#存储临时变量
                    print('People in positions', i, end='')
                else:
                    if temp == i-1:	#前后比较
                        print(' flip your caps!')
                    else:
                        print(' through', i-1, 'flip your caps!')
    else:
        print("You can't input a null list!")

  难题3 假设在队伍中存在没戴帽子的人。我们用字符’H’代表他们。例如,有这样一组数据:
  caps3 = [‘F’, ‘F’, ‘B’, ‘H’, ‘B’, ‘F’, ‘B’, ‘B’, ‘B’, ‘F’, ‘H’, ‘F’, ‘F’ ]
  我们不想指示没戴帽子的人去转动不存在的帽子,这令人困惑,可能会导致有人试图从队伍前面的人那里偷帽子。因此我们希望跳过所有’H’位置。修改pleaseConform,使它生成正确并且最小数量的命令集合。对于上面的例子,它应当生成:
  People in positions 2 flip your caps!
  People in positions 4 flip your caps!
  People in positions 6 through 8 flip your caps!

	'''在line.py文件pleaseConform函数上修改'''
    for i in range(1, len(caps)):
        if caps[start] != caps[i]:
            # each interval is a tuple with 3 elements (start, end, type)
            if caps[start] == 'H':	#在这边做下判断
                start = i
                continue
            intervals.append((start, i - 1, caps[start]))
            
            if caps[start] == 'F':
                forward += 1
            else:
                backward += 1
            start = i

  习题 4 写一段程序实现简单的游程编码,将给定字符串(如BWWWWWBWWWW)转换为较短的字符串(1B5W1B4W),并且执行游程解码,将压缩过的字符串转换回原始字符串。你只能通过一次字符串遍历来执行压缩和解压缩过程。
  str函数能将数字转换成字符串,例如str(12) = ‘12’。它在编码步骤汇总会很有用。
  int函数能将字符串转换成数字,例如:int(‘12’) = 12。对于任意字符串s,如果s[i]是字母字符,则s[i].isalpha()将返回True,否则返回False。如果s中所有的字符都是字母字符,则s.isalpha()返回True,否则返回False。函数int和isalpha在解码步骤中会很有用。

"""
BWWWWWBWWWW ==> 1B5W1B4W
1B5W1B4W ==> BWWWWWBWWWW   
"""

def encode(strs):	
	'''编码:每两个字符进行比较,如果不同输出前面的内容,
	如果相同,开始累加,直到比较不同而输出为止,如此反复。'''
    strs = strs + ' '
    sum = 1 
    for c in strs[1:]:
        if strs[0] != c:
            print(sum, strs[0], sep='', end='')
            sum = 1
        else:
            sum = sum + 1
        strs = strs[1:]
    print('')


def decode(strs):
	'''解码:利用数字在先字母在后的特点,对处于索引偶数位的数字做记录,
	对处于索引奇数位的字母做*运算'''
    for n,s in enumerate(strs):
        if n % 2 == 0:
            num = int(s)
        else:
            print(s * num, end='')
        n += 1
    print('')    


encode('BWTTTTWWBWQQQWW')
decode('1B1W4T2W1B1W3Q2W')

谜题 2 参加派对的最佳时间

2.4 习题(page20)
  习题 2 有另一种方法, 可以不依赖时间精度来计算参加派对的最佳时间。我们依次选择每位名人的时间区间,并确定有多少其他名人的时间区间包含所选名人的开始时间。我们选择出某位名人,使他的开始时间被最大数量的其他名人时间区间所包含,并将他的开始时间作为参加派对的时间。编写代码实现该算法,并验证它的结果与基于排序算法的结果是否相同。

'''在文件partysmart.py文件结尾处添加'''
def ex2(sched):
	'''双循环,让每一个起始时间和所有时间比较,如果在区间中则记录,最后取人数最多的那个。'''
    count = max_count = 0
    for i in sched:
        for j in sched:
            if i[0] >= j[0] and i[0] < j[1]:
                count += 1
        #print(i[0],count)	#可详细输出所有的信息
        if count > max_count:
            max_count = count
            best_time = i[0]
        count = 0
    print('Best time to attend the party is at', best_time, 'o\'clock :', max_count, 'celebrities will be attending!')


ex2(sched2) 
#sched2 = [(6.0, 8.0), (6.5, 12.0), (6.5, 7.0), (7.0, 8.0), (7.5, 10.0), (8.0, 9.0),(8.0, 10.0), (9.0, 12.0), (9.5, 10.0), (10.0, 11.0), (10.0, 12.0), (11.0, 12.0)]
'''
print:
Best time to attend the party is at 9.5 o'clock : 5 celebrities will be attending!
原程序的结果为:
Best time to attend the party is at 9.5 o'clock : 5 celebrities will be attending!
结果完全相同,不过感觉习题2的算法更好理解和实现。
'''

  难题 3 假设每位名人都有一个权重,取决于你对这位名人的喜爱程度。可以在时间表中将其表示为一个三元组,如(6.0, 8.0, 3)。开始时间是6.0,结束时间是8.0,权重是3。修改代码,找出最大化名人总权重的时间。例如…下面是一个更复杂的例子:
  sched3 = [(6.0, 8.0, 2), (6.5, 12.0, 1), (6.5, 7.0, 2), (7.0, 8.0, 2), (7.5, 10.0, 3), (8.0, 9.0, 2), (8.0, 10.0, 1), (9.0, 12.0, 2), (9.5, 10.0, 4), (10.0, 11.0, 2), (10.0, 12.0, 3), (11.0, 12.0, 7)]
  根据名人的日程安排,你想要在11点参加派对,此时参加派对名人权重之和是13,为最大值。

'''在上面的文件partysmart.py上再追加如下代码'''
sched3 = [(6.0, 8.0, 2), (6.5, 12.0, 1), (6.5, 7.0, 2), (7.0, 8.0, 2), (7.5, 10.0, 3), (8.0, 9.0, 2),
          (8.0, 10.0, 1), (9.0, 12.0, 2), (9.5, 10.0, 4), (10.0, 11.0, 2), (10.0, 12.0, 3), (11.0, 12.0, 7)]
    
def ex3(sched):
    count = max_count = 0
    for i in sched:
        for j in sched:
            if i[0] >= j[0] and i[0] < j[1]:
                count += j[2]	#把之前的+1改为+j[2]即可
        #print(i[0],count)
        if count > max_count:
            max_count = count
            best_time = i[0]
        count = 0
    print('Best time to attend the party is at', best_time, 'o\'clock :', max_count, 'celebrities will be attending!')


ex3(sched3)
'''
print:
Best time to attend the party is at 11.0 o'clock : 13 celebrities will be attending!
'''

谜题 3 拥有(需要一点校准的)读心术

3.6 习题(page32)
  习题 2 修改ComputerAssistant,以防存在两对同花色的牌,则在选择隐藏牌与第一张牌时,选择其中需要编码的数值更小的一对牌。

'''直接在magic-exercise1.py文件上修改函数outputFirstCard,ComputerAssistant并添加了一个函数least_elem'''


def least_elem(numbers, oneTwo):
	'''取出两个卡牌差值最小的两个元素'''
    oneTwo_new = [] 
    for oneTwo_one in oneTwo:   # 拆包,将超过2个的元组全部扩展成2个,用于处理含有3个及以上相同花色的卡牌,题目没有要求处理这种情况,但我也把这种情况算进去了。比如oneTwo的值为[(0, 1, 3)],即三张牌同一花色。
        oneTwo_new += [(i,j) for i in oneTwo_one for j in oneTwo_one if i < j ]   # 如[(0, 1, 3)] ⇒ [(0, 1), (0, 3), (1, 3)]
    oneTwo_least = 52	# 随便赋值一个较大的数
    for oneTwo_one in oneTwo_new:   #遍历所有元组,找出最小值 
        temp = min(((numbers[oneTwo_one[0]] - numbers[oneTwo_one[1]]) % 13), ((numbers[oneTwo_one[1]] - numbers[oneTwo_one[0]]) % 13))
        if temp < oneTwo_least:
            oneTwo_least = temp
            oneTwo_really = oneTwo_one
    return oneTwo_really  #最后取出差值最小的两个元素 

#This procedure figures out which card should be hidden based on the distance
#between the two cards that have the same suit.
#It returns the hidden card, the first exposed card, and the distance
def outputFirstCard(numbers, oneTwo, cards):    #牌的数字,相同花色牌的列表[(0, 1), (2, 3)], 5张牌名
    oneTwo = least_elem(numbers, oneTwo)        #挑出最小的两个元素
    encode = (numbers[oneTwo[0]] - numbers[oneTwo[1]]) % 13
    if encode > 0 and encode <= 6:
        hidden = oneTwo[0]
        other = oneTwo[1]
    else:
        hidden = oneTwo[1]
        other = oneTwo[0]
        encode = (numbers[oneTwo[1]] - numbers[oneTwo[0]]) % 13

##    #The following print statement is just for debugging!
##    print ('Hidden card is:', cards[hidden], 'and need to encode', encode)
    print ('First card is:', cards[other])

    return hidden, other, encode

def ComputerAssistant():
    print ('Cards are character strings as shown below.')
    print ('Ordering is:', deck)
    cards, cind, cardsuits, cnumbers = [], [], [], []
    numsuits = [0, 0, 0, 0]
    number = 0
    while number < 99999:
        number = int(input('Please give random number of at least 6 digits:'))
    #Generate five "random" numbers from the input number
    clist = []
    i = 0
    while len(clist) < 5:
        number = number * (i + 1) // (i + 2)
        n = number % 52
        i += 1
        if not n in clist:
            clist.append(n)

    #print (clist)
    pairsuit = []       #创建一个列表
    for i in range(5):
        n = clist[i]
        cards.append(deck[n])       #五张牌名,为了便于理解代码,下面随便举了个实例
        cind.append(n)
        cardsuits.append(n % 4)     #五张牌的花色    [1, 1, 2, 2, 0]
        cnumbers.append(n // 4)     #五张牌的数字    [1, 3, 5, 2, 9]  两个数组 1_1代表2_D, 3_1代表4_D以此类推 
        numsuits[n % 4] += 1        #[1, 2, 2, 0]   算出红花各两张
        if numsuits[n % 4] > 1:
            pairsuit.append(n % 4)  #将大于1的花色存储在列表中,防止被替换。 此时pairsuit值为[1, 2]
        pairsuit = list(set(pairsuit))  #去重,防止2个以上的花色被多次记录在pairsuit中
##    #Just for debugging
##    print (cards)
    
    cardh = []
    cardh_tuple = []    #记录每个子列表
    #print(pairsuit)
    for pairsuit_one in pairsuit:
        for i in range(5):
            if cardsuits[i] == pairsuit_one:
                cardh_tuple.append(i)           #[0, 1]  [2, 3]
        cardh.append(tuple(cardh_tuple))        #[(0, 1), (2, 3)]
        cardh_tuple = []
    #print(cardh)
    hidden, other, encode = outputFirstCard(cnumbers, cardh, cards)

    remindices = []
    for i in range(5):
        if i != hidden and i != other:
            remindices.append(cind[i])

    sortList(remindices)
    outputNext3Cards(encode, remindices)
    # print(cards[hidden])
    guess = input('What is the hidden card?')
    if guess == cards[hidden]:
        print ('You are a Mind Reader Extraordinaire!')
    else:
        print ('Sorry, not impressed!')

    return
ComputerAssistant()  #最后执行测试
'''
输入777777,会出现两对花色相同的情况,验证无误。
输入999999,会出现有三张同花色的情况,取三者之间差值最小的两个元素作为隐藏牌和第一张牌。
'''

  习题 3 一些魔术师更喜欢按不同的方式对牌排序,将花色置为首要排序因子,而不是按数值排序,如下:
  deck = [‘A_C’, ‘2_C’, ‘3_C’, ‘4_C’, ‘5_C’, ‘6_C’, ‘7_C’, ‘8_C’, ‘9_C’, ‘10_C’, ‘J_C’, ‘Q_C’, ‘K_C’, ‘A_D’, ‘2_D’, ‘3_D’, ‘4_D’, ‘5_D’, ‘6_D’, ‘7_D’, ‘8_D’, ‘9_D’, ‘10_D’, ‘J_D’, ‘Q_D’, ‘K_D’, ‘A_H’, ‘2_H’, ‘3_H’, ‘4_H’, ‘5_H’, ‘6_H’, ‘7_H’, ‘8_H’, ‘9_H’, ‘10_H’, ‘J_H’, ‘Q_H’, ‘K_H’, ‘A_S’, ‘2_S’, ‘3_S’, ‘4_S’, ‘5_S’, ‘6_S’, ‘7_S’, ‘8_S’, ‘9_S’, ‘10_S’, ‘J_S’, ‘Q_S’, ‘K_S’]
  修改ComputerAssitant允许魔术师按如上的顺序排练。注意,按索引提取牌的牌号与花色的计算逻辑需要修改。同样,助手读牌的顺序也需要修改。需要正确处理一些细节,才能正确解出这道谜题的变体。

deck2 = ['A_C', '2_C', '3_C', '4_C', '5_C', '6_C', '7_C', '8_C', '9_C', '10_C', 'J_C', 'Q_C', 'K_C', 
         'A_D', '2_D', '3_D', '4_D', '5_D', '6_D', '7_D', '8_D', '9_D', '10_D', 'J_D', 'Q_D', 'K_D', 
         'A_H', '2_H', '3_H', '4_H', '5_H', '6_H', '7_H', '8_H', '9_H', '10_H', 'J_H', 'Q_H', 'K_H', 
         'A_S', '2_S', '3_S', '4_S', '5_S', '6_S', '7_S', '8_S', '9_S', '10_S', 'J_S', 'Q_S', 'K_S']
'''
将习题2修改后的文件经过如下修改:
'''
	#ComputerAssistant()函数改动部分
	......
    for i in range(5):
        n = clist[i]
        cards.append(deck2[n])       
        cind.append(n)
        cardsuits.append(n // 13)    
        cnumbers.append(n % 13)    
        numsuits[n // 13] += 1       
        if numsuits[n // 13] > 1:
            pairsuit.append(n // 13) 
        pairsuit = list(set(pairsuit))  
        ......
        ''' 再将代码中deck替换为deck2即可'''

  难题 4 编写ComputerAssistant4Cards,允许排练这套4张牌的戏法。你可以选择上文描述的编码策略,利用这样的手段提供信息:(1)每张牌放置于桌面上的隐藏牌的左侧还是右侧;(2)所有3张牌是全部正面朝上还是正面朝下。你也可以设计自己的编码策略,确保对任意抽取的4张牌都能操作这套戏法。

#coding=utf-8

'''
一共有4张牌,除了隐藏牌以外,其他牌如果是正面朝上则代表1-8,背面朝上代表9-16。
我们把和隐藏牌最近的那张牌放在其右边,并将其视为一个整体c,这样和其他两张牌ab一共有6种组合,即
abc acb cab cba bca bac ,然后我们把和隐藏牌最近的那张牌放在其左边,并视为整体d,剩余两张牌只在其左边,
一共有2种组合,即 abd bad,这样8种表示方式就齐了,正反各8种,一共16种,足够表达了。
'''

deck = ['A_C', 'A_D', 'A_H', 'A_S', '2_C', '2_D', '2_H', '2_S', '3_C', '3_D', '3_H', '3_S',
        '4_C', '4_D', '4_H', '4_S', '5_C', '5_D', '5_H', '5_S', '6_C', '6_D', '6_H', '6_S',
        '7_C', '7_D', '7_H', '7_S', '8_C', '8_D', '8_H', '8_S', '9_C', '9_D', '9_H', '9_S',
        '10_C', '10_D', '10_H', '10_S', 'J_C', 'J_D', 'J_H', 'J_S',
        'Q_C', 'Q_D', 'Q_H', 'Q_S', 'K_C', 'K_D', 'K_H', 'K_S']

def shuru_shengcheng_4kapai():
    number = 0                
    print('现在有扑克牌52张,分别为:', deck)
    while number < 99999:
        number = int(input('Please give random number of at least 6 digits:'))
    
    clist = []
    i = 0
    while len(clist) < 4:
        number = number * (i + 1) // (i + 2)
        n = number % 52
        i += 1
        if not n in clist:
            clist.append(n)         #随机选择的卡牌索引 比如[13, 26, 6, 4]
    #clist = [0, 13, 26, 39]        #测试反面
    return clist


def fenlei_shuchu(clist):   
    #选择差值最小的两位
    cmp = [(i,j) for i in clist for j in clist if i != j]
    min_cmp = 52
    for i in cmp:
        min_temp = (i[0] - i[1]) % 52
        if min_temp < min_cmp:
            min_cmp = min_temp
            hide = i[0]         #取出隐藏牌索引
            bind = i[1]         #取出绑定的牌索引
    other = clist[:]            
    other.remove(hide)
    other.remove(bind)          
    big = max(other)            #取出大的牌索引
    small = min(other)          #取出小的牌索引
    return hide, bind, big, small


def shengcheng_pailie(hide, bind, big, small):
    print(hide, bind, big, small)
    if (hide - bind) % 52 <= 8:
        cha = (hide - bind) % 52
        front = True
    else:
        cha = (hide - bind) % 52 - 8 
        front = False
    if cha == 1:
        return (big, small, bind, front, 2) #front代表正面还是反面,后面的数字表示hide的位置
    elif cha == 2:
        return (big, bind, small, front, 1)
    elif cha == 3:
        return (bind, big, small, front, 0)
    elif cha == 4:
        return (small, big, bind, front, 2)
    elif cha == 5:
        return (small, bind, big, front, 1)
    elif cha == 6:
        return (bind, small, big, front, 0)
    elif cha == 7:
        return (big, small, bind, front, 3)
    elif cha == 8:
        return (small, big, bind, front, 3)
        

def ComputerAssistant4Cards():
    clist = shuru_shengcheng_4kapai()
    hide, bind, big, small = fenlei_shuchu(clist)
    res = shengcheng_pailie(hide, bind, big, small)
    if res[-2]:  #正面
        print('助手把牌全都正面排列,且顺序如下:')   
    else:
        print('助手把牌全都反面排列,且顺序如下:')  
    res2 = [deck[res[0]], deck[res[1]], deck[res[2]]]
    res2.insert(res[-1], '???')
    print(res2[0], res2[1], res2[2], res2[3])
    magicin = input('请输入未知的卡牌:')
    if magicin == deck[hide]:
        print('恭喜你!猜的真准!')
    else:
        print('哎呀,演砸了!正确的卡牌应该是:', deck[hide])


ComputerAssistant4Cards()

谜题 4 让皇后保持分离

4.5 习题(page46)
  难题 2 修改EightQueens的代码,在列表中某些位置上已放置过皇后的基础上,寻找解法。你可以使用一维列表localtion作为列表,该参数对应皇后的固定位置的某些列并且非负。例如,localtion = [-1, 4, -1, -1, -1, -1, -1, 0],表示在第2列和第8列放置了两个皇后。你的代码应该生成[2, 4, 1, 7, 5, 3, 6, 0],作为与现有皇后位置相符的解。

'''
在8queens-iterative-exercise1.py文件上修改,考虑到每个都循环8次,已经给的那列就不需要循环了,这可以理解成只循环1次,按照这个思路修改代码如下:
'''
def noConflicts(board, current):
    for i in range(current):
        if (board[i] == board[current]):
            return False
        #We have two diagonals hence need the abs()
        if (current - i == abs(board[current] - board[i])):
            return False
    return True 


#This procedure places 8 Queens on a board so they don't conflict.
#It assumes n = 8 and won't work with other n!
#Additional argument: number of solutions
#Non-default argument needs to be first!
def EightQueens(numsol, location, n=8):
    board = location
    sol = 0
    xunhuan = []
    for b in board:		#将需要循环的次数放置到xunhuan列表中
        if b == -1:
            xunhuan.append(8)
        else:
            xunhuan.append(1)
    for i in range(xunhuan[0]):
        if xunhuan[0] != 1:		#如果循环1,说明已经给了board的值,那么就不需要重新赋值了;如果循环不为1,说明未给确定值,则需要遍历。
            board[0] = i 
        for j in range(xunhuan[1]):
            if xunhuan[1] != 1:
                board[1] = j 
            if not noConflicts(board, 1):
                continue  
            for k in range(xunhuan[2]):
                if xunhuan[2] != 1:
                    board[2] = k
                if not noConflicts(board, 2):
                    continue
                for l in range(xunhuan[3]):
                    if xunhuan[3] != 1:
                        board[3] = l
                    if not noConflicts(board, 3):
                        continue
                    for m in range(xunhuan[4]):
                        if xunhuan[4] != 1:
                            board[4] = m
                        if not noConflicts(board, 4):
                            continue
                        for o in range(xunhuan[5]):
                            if xunhuan[5] != 1:
                                board[5] = o
                            if not noConflicts(board, 5):
                                continue
                            for p in range(xunhuan[6]):
                                if xunhuan[6] != 1:
                                    board[6] = p
                                if not noConflicts(board, 6):
                                    continue
                                for q in range(xunhuan[7]):
                                    if xunhuan[7] != 1:
                                        board[7] = q
                                    if noConflicts(board, 7):
                                        if sol < numsol:
                                            print (board)
                                            sol += 1
                                        else:
                                            return
    return

EightQueens(1, [-1, 4, -1, -1, -1, -1, -1, 0])


  习题 3 使用continue语句减少FourQueens代码中的缩进级别,使两种解法都打印出来。这比你想象的更棘手一点。

'''题目没读懂......'''

谜题 6 寻找假币

6.5 习题(page61)
  难题 2 编写CoinComparisonGeneral,它可以不假设假币更重便能确定哪一枚是假币,就像当前代码所做的那样。这将涉及findFakeGroupAndType,中为resultland2填写另外两种情况,然后为第一段分片(split)调用findFakeGroupAndType,为剩余的分片调用findFakeGroupHeavier或者findFakeGroupLighter。给出的过程findFakeGroup假设假币更重,你需要写一个伙伴过程,让他假设假币更轻并返回合适的硬币组。你的代码也应当处理没有假币的情况。

'''
在weighings.py文件上做了修改。
'''
import sys


def splitCoins(coinsList):
    length = len(coinsList)
    group1 = coinsList[0:length//3]
    group2 = coinsList[length//3:length//3*2]
    group3 = coinsList[length//3*2:length]
    return group1, group2, group3
    

def compare(groupA, groupB):
    if sum(groupA) > sum(groupB):
        result = 'left'
    elif sum(groupB) > sum(groupA):
        result = 'right'
    elif sum(groupB) == sum(groupA): 
        result = 'equal'
    return result

def findFakeGroupAndType(group1, group2, group3):
    result1and2 = compare(group1, group2)
   
    if result1and2 == 'left':
        result1and3 = compare(group1, group3)
        if result1and3 == 'left':
            fakeGroup = group1
            type = 'heavier'
        elif result1and3 == 'equal':
            fakeGroup = group2
            type = 'lighter'
    elif result1and2 == 'right':
        result1and3 = compare(group1, group3)
        if result1and3 == 'right':
            fakeGroup = group1
            type = 'lighter'
        elif result1and3 == 'equal':
            fakeGroup = group2
            type = 'heavier'
    elif result1and2 == 'equal': 
        result1and3 = compare(group1, group3)
        if result1and3 == 'left':
            fakeGroup = group3
            type = 'lighter'
        elif result1and3 == 'right':
            fakeGroup = group3
            type = 'heavier'
        else:
            print('There is no fake coin!')
            sys.exit()
    return fakeGroup, type
    
    
def findFakeGroupHeavier(group1, group2, group3):
    result1and2 = compare(group1, group2)
    if result1and2 == 'left':
        fakeGroup = group1
    elif result1and2 == 'right':
        fakeGroup = group2
    elif result1and2 == 'equal':
        fakeGroup = group3
    return fakeGroup

def findFakeGroupLighter(group1, group2, group3):
    result1and2 = compare(group1, group2) 
    if result1and2 == 'left':
        fakeGroup = group2
    elif result1and2 == 'right':
        fakeGroup = group1
    elif result1and2 == 'equal':
        fakeGroup = group3
    return fakeGroup


def CoinComparisonGeneral(coinsList):
    currList = coinsList[:]
    group1, group2, group3 = splitCoins(currList)
    currList, type = findFakeGroupAndType(group1, group2, group3)
    counter = 2
    while len(currList) > 1:
        group1, group2, group3 = splitCoins(currList)
        if type == 'heavier':
            currList = findFakeGroupHeavier(group1, group2, group3)
        elif type == 'lighter':
            currList = findFakeGroupLighter(group1, group2, group3)
        counter += 1
    fake = currList[0]
    print ('The fake coin is coin', coinsList.index(fake) + 1, '.It\'s', type , 'than others.')
    print ('Number of weighings:', counter)

    
#Pretend that you actually can't see the values in coinsList!
coinsList2 = [10, 10, 10, 10, 10, 10, 11, 10, 10]
coinsList = [10, 10, 10, 10, 10, 10, 10, 10, 10,
             10, 10, 10, 10, 10, 10, 10, 10, 10,
             10, 10, 10, 10, 10, 10, 10, 9, 10]

CoinComparisonGeneral(coinsList2)
CoinComparisonGeneral(coinsList)

  习题 3 假设有两枚假币,他们看起来和其他硬币完全相同但比其他硬币更重。修改CoinComparison,找到其中一枚假币(两个假币中的一个)。注意,如果你有两组硬币,每组都有一枚硬币,天平将会显示这两组硬币重量相等。如果你选择了第三组硬币(不在比较范围中),你将会错过找到这枚假币。在修改过的代码中,不要为正确计算称量次数而费心。

'''
在weighings.py原文件上修改如下。
'''
import sys

def splitCoins(coinsList):
    length = len(coinsList)
    group1 = coinsList[0:length//3]
    group2 = coinsList[length//3:length//3*2]
    group3 = coinsList[length//3*2:length]
    return group1, group2, group3
    

def compare(groupA, groupB):
    if sum(groupA) > sum(groupB):
        result = 'left'
    elif sum(groupB) > sum(groupA):
        result = 'right'
    elif sum(groupB) == sum(groupA): 
        result = 'equal'
    return result


def findFakeGroup(group1, group2, group3):
    result1and2 = compare(group1, group2)  
    if result1and2 == 'left':
        fakeGroup = group1
    elif result1and2 == 'right':
        fakeGroup = group2
    elif result1and2 == 'equal': #Could just be an else
        result1and3 = compare(group1, group3)
        if result1and3 == 'right':
            fakeGroup = group3
        elif result1and3 == 'left':
            fakeGroup = group1
        elif result1and3 == 'equal':
            print('There is no fake coin!')
            sys.exit()
    return fakeGroup
    

def CoinComparison(coinsList):
    currList = coinsList[:]
    while len(currList) > 1:
        group1, group2, group3 = splitCoins(currList)
        currList = findFakeGroup(group1, group2, group3)
    fake = currList[0]      
    print ('The fake coin is coin', coinsList.index(fake) + 1, 'in the original list')
    
#Pretend that you actually can't see the values in coinsList!
coinsList2 = [10, 11, 10, 10, 10, 10, 11, 10, 10]
coinsList = [10, 10, 10, 10, 10, 10, 10, 10, 10,
             10, 10, 10, 11, 11, 10, 10, 10, 10,
             10, 10, 10, 10, 10, 10, 10, 10, 10]

CoinComparison(coinsList2)
CoinComparison(coinsList)

谜题 7 跳到平方根

7.5 习题(page69)
  习题 2 修改过程bsearch,多加一个参数:查找区间小于多少时,执行迭代顺序查找。当前的过程会持续二分搜索直到找到元素。实际上,当区间[lo, hi]的长度hi - lo 小于某特定长度时,不分割区间直接在区间内使用顺序查找反而可能更快。

'''
在bsearch.py文件上修改
'''
NOTFOUND = -1
Ls = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, \
     71, 73, 79, 83, 89, 97]    #len = 25

def lsearch(L, value, pre_index=0):
    for i in range(len(L)):
        if L[i] == value:
            print ("Found at location", i + pre_index)
            return i + pre_index
    print ("Could not find the value", value)
    return NOTFOUND



def bsearch(L, value, lenl=10):
        lo, hi = 0, len(L)
        pre_index = 0
        while lo <= hi:
            if hi - lo < lenl:
                return lsearch(L[lo:hi+1], value, pre_index)        
            mid = (lo + hi) // 2
            mid2 = (hi - lo) // 2
            if L[mid] < value:
                lo = mid + 1
                pre_index += mid2 + 1
            elif value < L[mid]:
                hi = mid - 1
            else:
                print ("Found at location", mid)
                return mid

lsearch(Ls, 89)
bsearch(Ls, 47, 3)
bsearch(Ls, 37, 7)


你可能感兴趣的:(编程的乐趣:用Python解算法谜题 课后习题答案)