如何排序排列,使每个排列中至少有1个元素恰好相差1

时间:2013-12-18 16:46:35

标签: python algorithm sorting generator permutation

这个post提供了一些很好的python代码来查找一个总和到某个数字S的集合中的所有排列。我想消除输出中的不连续性,以便输出行中没有一个元素与任何相邻行的差异超过1。

以下是生成我想要订购/排序的输出的代码:

def f(n,s):
    if n == 1:
        yield (s,)
    else:
        for i in xrange(s + 1):
            for j in f(n - 1,s - i):
                yield (i,) + j

L = list(f(3,5))

for i in L:
    print i

输出:

(0, 0, 5)
(0, 1, 4)
(0, 2, 3)
(0, 3, 2)
(0, 4, 1)
(0, 5, 0)
(1, 0, 4) <-Bad, 0 transitioned to 4 from one row to the next
(1, 1, 3)
(1, 2, 2)
(1, 3, 1) 
(1, 4, 0)
(2, 0, 3) <-Bad, 4 transitioned to 0 from one row to the next
(2, 1, 2)
(2, 2, 1)
(2, 3, 0)
(3, 0, 2)
(3, 1, 1)
(3, 2, 0)
(4, 0, 1) <-Bad, 2 transitioned to 0 from one row to the next
(4, 1, 0)
(5, 0, 0)
Desired Output:
(0, 0, 5)
(0, 1, 4)
(0, 2, 3)
(0, 3, 2)
(0, 4, 1)
(0, 5, 0)
(1, 4, 0)
(1, 3, 1) 
(1, 2, 2)
(1, 1, 3)
(1, 0, 4)
(2, 0, 3)
(2, 1, 2)
(2, 2, 1)
(2, 3, 0)
(3, 2, 0)
(3, 1, 1)
(3, 0, 2)
(4, 0, 1)
(4, 1, 0)
(5, 0, 0)

有人能提出一些能以这种方式订购输出的代码吗?

2 个答案:

答案 0 :(得分:1)

这是我能提出的最简单的天真(“词法排序”)解决方案,它保留了转换平滑性并生成所有排列:

def g(n, s, direction=1):
    if n == 1:
        yield (s,)
    else:
        if direction > 0:
            r = xrange(s + 1)
        else:
            r = xrange(s, -1, -1)
        if s % 2:
            direction = -direction
        for i in r:
            for j in g(n - 1, s - i, direction):
                yield (i,) + j
            direction = -direction

不幸的是,对于s的奇数值,它不是根据需要以(0,) * (n - 1) + (s,)开头,而是(0, s) + (0,) * (n - 2)。 (所以g(5, 7)不是以(0, 0, 0, 0, 7)开头,而是以(0, 7, 0, 0, 0)开头。)我假设必须有一些相对简单的调整才能解决这个问题,但目前我还在逃避。如果我正确地阅读了这个问题,那么平滑性和完整性是唯一真正关键的要求。

如果您只限于n <= 3,那么您可以通过摆脱

来获得所需的排序
        if s % 2:
            direction = -direction

但不知何故,这似乎是一个严酷的限制。

答案 1 :(得分:0)

虽然可能不是最优雅的解决方案,但这可行:

def f(n, s):
    def g(n,s):
        if n == 1:
            yield (s,)
        else:
            for i in xrange(s + 1):
                for j in g(n - 1,s - i):
                    yield (i,) + j

    L = list(g(3, 5))

    D = []

    i = 1

    while i != len(L):
        for j in xrange(len(L[i])):
            if abs(L[i][j] - L[i-1][j]) > 1:
                D.append(L.pop(i))
                i -= 1
                break
        for d in D:
            ins = True
            for j in xrange(len(L[-1])):
                if abs(L[-1][j] - d[j]) > 1:
                    ins = False
                    break
            if ins:
                L.append(d)
                D.remove(d)
        i += 1

    return L

for i in f(3, 5):
    print i

打印:

(0, 0, 5)
(0, 1, 4)
(0, 2, 3)
(0, 3, 2)
(0, 4, 1)
(0, 5, 0)
(1, 4, 0)
(2, 3, 0)
(3, 2, 0)
(4, 1, 0)
(5, 0, 0)
(4, 0, 1)
(3, 0, 2)
(2, 0, 3)
(1, 0, 4)
(1, 1, 3)
(2, 1, 2)
(3, 1, 1)
(2, 2, 1)
(1, 2, 2)
(1, 3, 1)

它基本上定义g内的f,作为创建permations的生成器。然后它通过它的列表并检查每个元素,如果一个子列表中的元素与之前的元素之间的差异(abs部分)(未解释得很好,我知道......但是你得到它)大于1,如果删除该列表,则将其附加到D并将索引减少1(这就是我使用while而不是for)的原因。

修改 每次检查一个元素后,它会经过D并查看是否有任何内容适合L,如果适合,请附加它。

然后返回已过滤的列表。