【问题标题】:Speed up permutation [closed]加快排列[关闭]
【发布时间】:2020-10-08 14:00:41
【问题描述】:

我有下一个任务。

给定整数 n (1 <= n <= 1000000) 和 k (1 <= k <= n)。

需要找到整数1, 2, 3, ..., n的任何排列p,使得排列中每两个连续整数之间的绝对差为>= k,即对于所有i的排列p需要abs(p[i] - p[i + 1]) >= k .

如果给定的nk 不存在这样的排列,则输出Impossible

原始在线任务是波斯语,这就是我不提供链接的原因。

我已经实现了下一个代码。但是解决上述任务非常慢。如何提高它的速度?

from itertools import permutations

n,k=input('').split(' ')

n=int(n);k=int(k)

def check(n,k):
    n=list(n)
    N=n[:]
    result=[]
    b=n.pop(0)
    while n:
        if abs(b-n[0]) >= k:
            result.append(True)
        b=n.pop(0)
    if len(result)+1 == len(N) and all(result):
        return True
    else:
        return False

def solver(n,k):
    return (i
        for i in (permutations(range(1,n+1)))
        if check(i,k)
    )
try:
    aaa=next(solver(n,k))
    for i in aaa:
        print(i,end=' ')
except :
    print("Impossible")

【问题讨论】:

  • 我不得不从代码中删除大量空白以保持可读性。请检查这是否在逻辑上仍然与您的代码匹配。
  • 您能否说明您的速度目标是什么?该代码中存在 巨大 性能错误,例如破坏性地迭代列表,复制整个列表以保留其长度,并检查整个列表,即使它已经被拒绝。到目前为止,您是否尝试过任何优化?你真的想优化排列,还是只是求解器?
  • 其实,这是一道数学题。在这个程序中,有一个从 1 到 n 的数字列表,我必须对这个列表进行排序,使每行中的每两个数字至少有 (k) 个差异。例如,如果我有这样的列表 [1,2,3,4,5] =》这个程序必须给我这样的列表 [1,3,5,2,4] 。您可以看到 1 和 3 有 2 个单位的差异。 (abs(1-3)=2) and abs(3-5)=2 and abs(5-2)=3 (至少k=2) and abs(2-4)=2。因为这个问题在数学上对我来说很难,所以我选择排列。
  • @mohamadmahdi 我想我已经解决了你在my answer 中处理大n 的任务。阅读整个答案文本。
  • @mohamadmahdi 数学问题总是通过耗尽其数学属性来最快地解决。我的问题是您是否关心当前的方法没有穷尽这些属性来简化方法,或者是否还有一种完全不同的方法也在范围内。

标签: python performance permutation


【解决方案1】:

因为最简单的优化是通过使用非常流行的NumPy库来改进check(...)函数,只需安装一次NumPy,然后通过python -m pip install numpy命令行使用下一个代码。

Try it online!

def check(n, k):
    import numpy as np
    return np.abs(np.diff(n)).min() >= k

但是,如果您的任务的解决方案应该非常非常快,那么我实施了完整的下一个新解决方案,对于大型 n(100 万甚至更多)来说应该非常快。此外,我的下一个解决方案不使用任何额外的库,例如 numpy

主要解决函数有solve2(n, k)它返回正确答案的列表(正确性由assert仔细检查),或者答案不存在它返回None。这个求解函数的使用示例是test2() 函数,例如它为所有小的nk 组合创建答案。如果求解函数返回None,测试函数在打印前将其转换为字符串Impossible

您必须按照您的案例所需的方式实现自己的test2() 变体,例如像在原始源代码中那样从用户那里获取输入nk,我刚刚为test2()如何使用我的代码的示例。

Try it online!

def solve2(n, k):
    if k <= 1 or n <= 1:
        p = list(range(n))
    elif n < 2 * k - 1:
        p = None
    elif 2 * k - 1 <= n <= 2 * k + 1:
        p = [None] * n
        cnt, klo = 0, -1
        if n == 2 * k + 1:
            p[0], p[1], p[2], cnt, klo = 0, k, 2 * k, 3, 0
        for i in range(k - 1, klo, -1):
            p[cnt] = i
            cnt += 1
            if cnt >= n:
                break
            p[cnt] = i + k
            cnt += 1
    else:
        if 2 * (k + 1) <= n <= 3 * (k + 1):
            kst = k + 1
        else:
            kst = k
        p = [None] * n
        cnt = 0
        for i in range(kst):
            for j in range(i, n, kst):
                p[cnt] = j
                cnt += 1

    if p is not None:
        assert len(p) == n, (len(p), n)
        p = [(e + 1) for e in p]
        assert all(abs(f - s) >= k for f, s in zip(p[:-1], p[1:]))

    return p

def test2():
    for n in range(1, 16):
        for k in range(1, n + 1):
            answer = solve2(n, k)
            answer = answer if answer is not None else 'Impossible'
            print(n, k, answer, end = '  |  ', flush = True)

test2()

【讨论】:

  • 感谢您的帮助。但我想加快速度,(n)可以是 1000000,这可能需要很多时间
  • @mohamadmahdi 然后是有趣的组合/数学任务。 A需要发明一些快速的公式来解决它。肯定生成所有排列会很慢,生成排列在 n=10 以下时才足够快。
  • @mohamadmahdi 您能否提供您任务的确切全文?或者也许是描述的链接?我想知道nk 的确切限制(最小值和最大值)。它还需要是任何数字(就像您在问题中所说的那样)还是特别是 permutation 的数字 1..n?它还需要在字典上是第一个排列还是任何随机合适的排列?甚至根本不需要置换,只需要一个简短的答案PossibleImpossible
  • @mohamadmahdi 如果您提供指向原始问题/任务所在的网站页面的链接,那就太好了。有趣的任务,很高兴阅读原始的整页文本。
  • @mohamadmahdi 顺便说一句,我上面的解决方案是否正确解决了您的在线比赛问题?真的是线上比赛的问题吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多