下面是自己实现的map方法。
def simple_map(proc,items):
result = []
for item in items:
result.append(proc(item))
return result
def scale_list(items,factor):
return simple_map(lambda x:x*factor,items)
print(scale_list([1,2,3,4,5,6],5))
练习2.21 返回每个数的平方构成的表:
def square_list(items):
return simple_map(lambda x:x**2,items)
print(square_list(range(9)))
练习2.29 此题有一点难度。
def make_mobile(left,right):
return (left,right)
def make_branch(length,structure):
return (length,structure)
def left_branch(t):
return getitem(t,0)
def right_branch(t):
return getitem(t,1)
def branch_length(branch):
return getitem(branch,0)
def branch_structure(branch):
return getitem(branch,1)
def total_weight(t):
if type(t)==type(1):
return t
return total_weight(branch_structure(left_branch(t)))+total_weight(branch_structure(right_branch(t)))
def is_balanced(t):
if type(t)==type(1):return True,t
ok1,w1 = is_balanced(branch_structure(left_branch(t)))
ok2,w2 = is_balanced(branch_structure(right_branch(t)))
ok3 = branch_length(left_branch(t))*w1==branch_length(right_branch(t))*w2
return ok1&ok2&ok3,w1+w2
T = ((8,((1,12),(3,4))),(16,((1,6),(3,2))))
print(is_balanced(T))
练习2.32 ,返回一个集合所有子集的集合。
这道题的代码虽短,但是还是需要思考一下的。注意到,当前集合所有元素的子集可以通过除第一个元素以外的剩余集合的所有集合子集设为rest,则rest+rest(first)就是完整的子集集合,rest(first)等价于每一个元素都添加上first。
def restof(s):
return s[1:]
def firstof(s):
return s[0]
def transform(x):
if type(x)==type(1):return (x,)
return x
def subsets(s):
print(s)
if s==():return((),)
first = firstof(s)
rest = subsets(restof(s))
u = rest+tuple(map(lambda x:transform(x)+(first,),rest))
return u
补充 提供一种非递归的思路,虽然相对较慢,但是避免了递归层数的限制。采取BFS遍历状态。
def combinations(s):
n = len(s)
q,conditions=deque(),{():n}
if n==0:return None
q.append(())
size_q = 1
while size_q:
case = q.pop()
size = conditions[case]
if size:
item = s[n-size]
u = case+(item,)
q.append(u)
q.append(case)
conditions[u],conditions[case] = size-1,size-1
size_q+=1
else:
size_q-=1
print(case)
嵌套映射
给定自然数n,找出所有不同序对(j,i)满足1=<j<i<=n,且i+j是素数
这里就体现了嵌套映射的威力,和书上的方法不一样,计算一个数是不是素数的开销较大,因此先计算出2-2n-1的素数,然后计算每一个数的序对,最后将这些序对累积到一起。这里面就使用了两层映射的结合加过滤。代码很少就解决了问题。
def flat_map(proc,seq):
return reduce(lambda x,y:x+y if type(x)==type([]) else [x]+y ,map(proc,seq))#map the second time and accumulate
def prime_sum_pairs(n):
def f(x):
jmax = ((x-1)//2) +1
return [(j,x-j) for j in range(max(1,x-n),jmax)]#map the first time
seq = [x for x in range(2,2*n) if is_prime(x)]
return flat_map(f,seq)
练习2.40 :类似上面的结构:
def unique_pairs(n):
return flat_map(lambda x:[(j,x-j) for j in range(max(1,x-n),((x-1)//2) +1)],range(3,n*2))
补充练习 :生成一个集合的全排列
基本思路是:将第一个元素first插入到剩下元素的全排列的每一个位置。
def permutation(sets):
def rest(s):return s[1::]
def create_subsets(s):
nonlocal first
return [tuple(range(l+1))for index in range(len(s)+1)]
if sets==():return [()]
first = sets[0]
return flat_map(create_subsets,permutation(rest(sets)))
print(permutation((1,2,3,4)))
再给出另一种思路生成全排列,将每一个元素x插入到S−x的全排列的最前面 :
def permutation(sets):
def rest(x):return tuple([item for item in sets if item!=x])
def f(x):return [tuple([x]+list(each)) for each in permutation(rest(x))]
if sets==():return [()]
return flat_map(f,sets)
对比两种思路:虽然简洁其实都不高效。。。具体的分析请看该链接…
2.41
def create_pairs(s,n):
for i in range(1,s+1):
for j in range(i+1,s+1):
k = s-(i+j)
if k <= j:break
print((i,j,k))
2.42 10行代码就解决了8皇后问题,但是这个逻辑相对不太容易看明白,因为程序写的十分紧凑而且效率比较低下。
def queens(size):
def queen_cols(k):
def safe(item):
n = len(item)//2
rows = set([x for (i,x) in enumerate(item) if i%2==0 ])
cols = set([x for (i,x) in enumerate(item) if i%2==1 ])
return len(rows)==n and len(cols)==n
if k==size+1:return [tuple()]
rest = queen_cols(k+1)
return list(filter(safe,flat_map(lambda row:[(row,k)+each for each in rest],range(1,size+1))))
return queen_cols(1)
2.43 我一开始就犯了这个错误,按照书上的写法,每一次内层映射都会去求一次rest_of_queens,相当于 T(k)=N∗[(k−1)!+T(k−1)]=N∗(k−1)!+NT(k−1)...(1) ,
而正常的的消耗是 T(k)=T(k−1)+N∗(k−1)!.(2)
来计算一下 (1) :
T(N)=N!+NT(N−1)=N!+N∗[(N−2)!+T(N−2)]
=N!+N∗(N−2)!+N[N∗(N−3)!+T(N−3)]
=N!+N∗(N−2)!+N2∗(N−3)!+N2[N∗(N−4)!+NT(N−4)]
=...+N3(N−4)!+N3T(N−4)=
N!+N∗(N−2)!+N2∗(N−3)!+.....NN−1≤NN
计算一下 (2) :
T(N)=N!+N∗(N−2)!+N∗(N−3)!..=O(N!)
综上,错误的方式需要大约 NNN!∗T 的时间。
补充练习 ,上面的方法虽然简洁,但是效率太低,算到N=9就很吃力了。下面给出一个高效且同样不复杂的办法:
进行DFS+剪枝,这里主要就是记录下已经占据了的行数,然后直从可用的行数里面进行挑选。
T(N)=N!
def quick_queens(size):
av_rows = list(range(1,size+1))
Final_pos = [()]
def quick_cols(k,case):
nonlocal Final_pos
nonlocal av_rows
if k==size+1:Final_pos.append(case)
t = av_rows[::]
for i in t:
av_rows.remove(i)
quick_cols(k+1,case+(i,k))
av_rows.append(i)
quick_cols(1,())
return Final_pos