求并行化C/C++的第n个置换
Finding nth permutation for parallelized C/C++
我目前正在寻找一种方法,通过使用函数来按字典顺序查找数组的第n个排列。我有一个序列代码,它是使用C++库中的next_permutation
编写的(其余代码都是普通的旧C),但由于next_permutation
的工作方式,它不仅效率低下,而且在代码的并行版本中使用极其困难。我使用的是Open MPI,因此每个进程都必须使用next_permutation
,此时数学计算变得非常混乱。此外,next_permutation
每次最多计算n个,因此每个进程都必须进行比所需更多的计算。我听说过使用阶乘来找到第n个排列,但我找不到任何关于它的使用信息。C/C++库中是否有函数可以提供阶乘,或者我能找到一个很好的资源来实现这一点?有没有更好的方法来找到特定数组的第n个排列?
例如:
array[3]={1,2,3}
factoradicFunc(3)-->2,1,3
factoradicFunc(4)-->2,3,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()
函数只是阶乘的一个缓慢但谨慎的实现。从13开始!>232,21!>264和35!>2128,对于32、64或128位无符号整数permutation_t
类型,只有极少数可能的结果,最好简单地使用数组查找阶乘(如rcgldr已经提到的)。
permutation(buffer, digits, length, index)
函数取长度为length
的目标buffer
,并用digits
的第index
’个排列填充它。(digits
是只读的,不可变的。)
这不是最快的实现,但它将计算O(length)
时间复杂度中的任何排列(忽略memmove()
运算;如果考虑memmove()
,则忽略O(length²)
)。它是次优的,因为它使用memmove()
对目标buffer
中的项目进行重新排序,并且每个元素需要两个除法(以及一个具有相同除数的模数)。
考虑到最大实际长度限制(12、20或34个元素,取决于permutation_t
类型的大小),使用memmove()
不是问题(因为数据在一个或最多几个缓存行内)。
这是线程安全的,只要同时只有一个线程在同一目标buffer
上操作即可;从同一源CCD_ 23缓冲区生成不同目标CCD_。
您可以创建一个阶乘表。如果使用64位无符号整数,则最大值为20!=2432902008176640000,或者对于32位无符号整数,最大值为12!=479001600,所以桌子很小。
数学部分的前一个线程:
https://math.stackexchange.com/questions/60742/finding-the-n-th-lexicographic-permutation-of-a-string
- 最高有效数字侧的第N位
- 在更改for循环的第三部分后,未使用for循环结果
- 删除列表中的第n个元素
- 为什么它只打印双链接列表的第一个值,而我的程序却崩溃了
- std::find,返回所有找到的值的替代方法,而不仅仅是存在重复的向量的第一个值
- 如何使代码打印文本文件中的第一行?
- 在链表中的第 n 位插入显示分割错误
- 如何仅读取文本文件中的第一个值
- 查找不在标准中的第一个值::设置<int>最小-最大值
- 数组中所有元素的最新最小元素的第五个位置
- 圣诞节的第N天,C++递归
- 查找连续子数组的第二小和
- 在C++中打印多个矢量的第一个值
- cin语句在循环的第三次迭代中被跳过
- 如何用CMake处理只有头的第三部分
- 如何在对数时间内访问 c++ std::set 中的第 k 个元素?
- Systemc - 将sc_time四舍五入到最接近的第 10 SC_NS
- 指向多维数组的第 n 个元素的指针
- 删除最小堆中的第 i 个索引
- 代码无尽写入文本文件的第一行