【问题标题】:Finding nth permutation for parallelized C/C++查找并行化 C/C++ 的第 n 个排列
【发布时间】:2016-03-03 05:27:08
【问题描述】:

我目前正在寻找一种方法,通过使用函数来按字典顺序查找数组的第 n 个排列。我有一个使用 C++ 库中的 next_permutation 编写的顺序代码(其余代码是普通的旧 C),但由于 next_permutation 的工作方式,它不仅效率低下,而且在代码的并行版本。我正在使用 Open MPI,因此每个进程都必须使用next_permutation,此时数学变得非常混乱。此外,next_permutation 每次最多计算 n 次,因此每个进程都必须进行比必要更多的计算。我听说过使用阶乘来找到第 n 个排列,但我一直无法找到有关其使用的任何信息。 C/C++ 库中是否有提供因子的函数,或者我能找到一个很好的资源来做到这一点?有没有更好的方法来找到特定数组的第 n 个排列?

例如:

数组[3] = {1, 2, 3}

factoradicFunc(3) --> 2, 1, 3

factoradicFunc(4) --> 2, 3, 1

等等

【问题讨论】:

    标签: c++ c algorithm parallel-processing mpi


    【解决方案1】:

    我已经在别处发布了以下示例,但让我在这里重复一遍:

    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <errno.h>
    
    typedef char           element_t;
    typedef unsigned long  permutation_t;
    
    static permutation_t factorial(const permutation_t n)
    {
        permutation_t i, result = 1;
        for (i = 2; i <= n; i++) {
            const permutation_t  newresult = result * i;
            if ((permutation_t)(newresult / i) != result)
                return 0;
            result = newresult;
        }
        return result;
    }
    
    int permutation(element_t *const        buffer,
                    const element_t *const  digits,
                    const size_t            length,
                    permutation_t           index)
    {
        permutation_t  scale;
        size_t         i, d;
    
        if (!buffer || !digits || length < 1)
            return errno = EINVAL;
    
        scale = factorial(length);
        if (!scale)
            return errno = EMSGSIZE;
        if (index >= scale)
            return errno = ENOENT;
    
        /* Copy original ordered set to mutable buffer */     
        memmove(buffer, digits, length * sizeof (element_t));
    
        for (i = 0; i < length - 1; i++) {
            scale /= (permutation_t)(length - i);
            d = index / scale;
            index %= scale;
            if (d > 0) {
                const element_t c = buffer[i + d];
                memmove(buffer + i + 1, buffer + i, d * sizeof (element_t));
                buffer[i] = c;
            }
        }
    
        return 0;
    }
    

    factorial() 函数只是对factorial 的缓慢但谨慎的实现。从13开始! > 232,21! > 264,还有 35! > 2128,对于 32、64 或 128 位无符号整数 permutation_t 类型只有极少数可能的结果,最好简单地使用数组查找来代替阶乘(如rcgldr already mentioned)。

    permutation(buffer, digits, length, index) 函数接受一个长度为length 的目标buffer,并用digits 的第index'th 排列填充它。 (digits 是只读的,不可变的。)

    这不是最快的实现,但它会计算O(length) 时间复杂度的任何排列(忽略memmove() 操作;O(length²) 如果考虑memmove())。这是次优的,因为它使用 memmove() 对目标 buffer 中的项目重新排序,并且每个元素需要两个除法(以及一个具有相同除数的模数)。

    考虑到最大实际长度限制(12、20 或 34 个元素,取决于 permutation_t 类型的大小),使用 memmove() 不是问题(因为数据在一个或最多几个缓存行)。

    这是线程安全的,只要只有一个线程同时对同一个目标buffer进行操作;多个线程从同一源digits 缓冲区生成不同的目标buffers 是线程安全的。

    【讨论】:

      【解决方案2】:

      您可以创建一个阶乘表。如果使用 64 位无符号整数,最大值为 20! = 2432902008176640000,或者对于 32 位无符号整数,最大值为 12! = 479001600,所以表很小。

      数学部分的先前线程:

      https://math.stackexchange.com/questions/60742/finding-the-n-th-lexicographic-permutation-of-a-string

      【讨论】:

        猜你喜欢
        • 2012-01-07
        • 1970-01-01
        • 2013-04-07
        • 1970-01-01
        • 1970-01-01
        • 2017-06-29
        • 1970-01-01
        • 1970-01-01
        • 2014-02-04
        相关资源
        最近更新 更多