#coding=utf-8
#
#关于这个问题的精彩讨论参见这里
#  http://groups.google.com/group/python-cn/browse_thread/thread/4d9eda8e422a6cf8
#
#猜数字游戏8步以内的求解程序的一部分
#用来生成四位不重复数字(0-9)的所有组合,比如8765, 9876, 0123

#用的是最直接的方法,4重for循环:
#by realfun
def init_set():
    ret = []
    for i in range(0, 10):
        for j in range(0, 10):
            for k in range(0, 10):
                for l in range(0, 10):
                    if i != j and i != k and i != l and j != k and j != l and k != l:
                        ret.append((i, j, k, l))
    return ret

#改进的4重for,减少判断语句被调用的次数
#by Leo Jay
def init_set2():
    ret = []
    for i in range(0, 10):
        for j in range(0, 10):
            if i == j: continue
            for k in range(0, 10):
                if j == k: continue
                for l in range(0, 10):
                    if k == l: continue
                    if i != k and i != l and j != l:
                        ret.append((i, j, k, l))
    return ret

#引入set,消除所有的判断语句
#by realfun
def init_set3():
    s1 = set(range(0, 10))
    ret = []
    for i in s1:
        s2 = s1.copy()
        s2.remove(i)
        for j in s2:
            s3 = s2.copy()
            s3.remove(j)
            for k in s3:
                s4 = s3.copy()
                s4.remove(k)
                for l in s4:
                    ret.append((i, j, k, l))
    return ret

#by Nick Cen
def perm(items, n=None):
    if n is None:
        n = len(items)
    for i in range(len(items)):
        v = items[i:i+1]
        if n == 1:
            yield v
        else:
            rest = items[:i] + items[i+1:]
            for p in perm(rest, n-1):
                yield v + p

#by Nick Cen
#递归的方法消除判断语句
def init_set4():
    ret = []
    for i in perm(range(0,10),4):
        ret.append(i)
    return ret

#展开版,采用init_set3生成,已知程序里面最快的
#灭掉了所有的判断语句,同时用展开的方法灭掉了set,目前已知最快的
#应该不能更快了,因为这个版本已经没有多余的操作了
#by Leo Jay
def init_set5():
    ret = []
    for i in xrange(0, 10):
       for j in xrange(i+1, 10):
           for k in xrange(j+1, 10):
               for l in xrange(k+1, 10):
                   ret.append((i, j, k, l))
                   ret.append((i, j, l, k))
                   ret.append((i, k, j, l))
                   ret.append((i, k, l, j))
                   ret.append((i, l, j, k))
                   ret.append((i, l, k, j))
                   ret.append((j, i, k, l))
                   ret.append((j, i, l, k))
                   ret.append((j, k, i, l))
                   ret.append((j, k, l, i))
                   ret.append((j, l, i, k))
                   ret.append((j, l, k, i))
                   ret.append((k, i, j, l))
                   ret.append((k, i, l, j))
                   ret.append((k, j, i, l))
                   ret.append((k, j, l, i))
                   ret.append((k, l, i, j))
                   ret.append((k, l, j, i))
                   ret.append((l, i, j, k))
                   ret.append((l, i, k, j))
                   ret.append((l, j, i, k))
                   ret.append((l, j, k, i))
                   ret.append((l, k, i, j))
                   ret.append((l, k, j, i))
    return ret

#limodou: 得到一个灵感,可以写一个程序生成的程序,用来生成上面的代码。
#Leo Jay: 嘿嘿,实际上这段代码就是自动生成的
def gen_fun5():
    def init_set3():
       s1 = set(range(0, 4))
       ret = []
       for i in s1:
           s2 = s1.copy()
           s2.remove(i)
           for j in s2:
               s3 = s2.copy()
               s3.remove(j)
               for k in s3:
                   s4 = s3.copy()
                   s4.remove(k)
                   for l in s4:

                       ret.append((i, j, k, l))
       return ret

    c = ['i', 'j', 'k', 'l']
    for i, j, k, l in init_set3():
       print 'ret.append((%s, %s, %s, %s))' % (c[i], c[j], c[k], c[l])
    
#下面的代码用来测试性能
import time

def timing(f, n):
    print f.__name__, n, "times",
    r = range(n)
    t1 = time.clock()
    for i in r:
        f()
    t2 = time.clock()
    print round(t2-t1, 3)

#直接的四重for
timing(init_set, 1000)
#init_set 1000 times 5.624

#四重for里面每层都加入判断,减少不必要的for
timing(init_set2, 1000)
#init_set2 1000 times 4.815

#引入set灭掉所有的判断语句(if语句)
timing(init_set3, 1000)
#init_set3 1000 times 3.189

#用递归灭掉所有的判断语句(递归调用开销大啊)
timing(init_set4, 1000)
#init_set4 1000 times 17.03

#灭掉了所有的判断语句,同时用展开的方法灭掉了set,目前已知最快的
#应该不能更快了,因为这个版本已经没有多余的操作了
timing(init_set5, 1000)
#init_set5 1000 times 2.024