SICP_Python第二章:映射(子集问题,排列问题,N皇后问题)

下面是自己实现的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)))

,xSx :

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[(k1)!+T(k1)]=N(k1)!+NT(k1)...(1) ,

而正常的的消耗是 T(k)=T(k1)+N(k1)!.(2)

来计算一下 (1)

T(N)=N!+NT(N1)=N!+N[(N2)!+T(N2)]
=N!+N(N2)!+N[N(N3)!+T(N3)]
=N!+N(N2)!+N2(N3)!+N2[N(N4)!+NT(N4)]
=...+N3(N4)!+N3T(N4)=
N!+N(N2)!+N2(N3)!+.....NN1NN

计算一下 (2) :

T(N)=N!+N(N2)!+N(N3)!..=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

规律总结,其实N皇后问题的解就是一个全排列而已。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值