我正在使用基本转换算法从大整数生成置换(分成32位字)。
我使用相对标准的算法:
/* N = count,K is permutation index (0..N!-1) A[N] contains 0..N-1 */
i = 0;
while (N > 1) {
swap A[i] and A[i+(k%N)]
k = k / N
N = N - 1
i = i + 1
}
不幸的是,每次迭代的除法和模数加起来,尤其是移动到大整数 - 但是,似乎我可以使用乘法!
/* As before, N is count, K is index, A[N] contains 0..N-1 */
/* Split is arbitrarily 128 (bits), for my current choice of N */
/* "Adjust" is precalculated: (1 << Split)/(N!) */
a = k*Adjust; /* a can be treated as a fixed point fraction */
i = 0;
while (N > 1) {
a = a*N;
index = a >> Split;
a = a & ((1 << Split) - 1); /* actually, just zeroing a register */
swap A[i] and A[i+index]
N = N - 1
i = i + 1
}
这更好,但做大整数乘法仍然很迟钝。
问题1:
有没有办法更快地做到这一点?
EG。既然我知道N *(N-1)小于2 ^ 32,我可以从一个单词中提取这些数字,并合并到“剩余时间”吗? 或者,有没有办法修改一个arithetic解码器,一次拉出一个指标?
问题2:
为了好奇 - 如果我使用乘法将数字转换为基数10而不进行调整,则结果乘以(10 ^位数/ 2 ^移位)。是否有一种棘手的方法来删除使用十进制数字的这个因素?即使有调整因子,这似乎会更快 - 为什么标准库不会使用这个vs divide和mod?
答案 0 :(得分:2)
看到你在谈论像2 ^ 128 /(N!)这样的数字,似乎在你的问题中N将会相当小(根据我的计算,N <35)。 我建议以原始算法为出发点;首先切换循环的方向:
i = 2;
while (i < N) {
swap A[N - 1 - i] and A[N - i + k % i]
k = k / i
i = i + 1
}
现在更改循环以在每次迭代中执行多个排列。我想,无论数字i,分割的速度都是相同的,只要i 将范围2 ... N-1拆分为子范围,以使每个子范围内的数字乘积小于2 ^ 32:
2, 3, 4, ..., 12: product is 479001600
13, 14, ..., 19: product is 253955520
20, 21, ..., 26: product is 3315312000
27, 28, ..., 32: product is 652458240
33, 34, 35: product is 39270
然后,将长数k除以产品而不是除以i。每次迭代将产生余数(小于2 ^ 32)和更小的数k。当你有余数时,你可以使用原始算法在内循环中使用它;现在会更快,因为它不涉及长时间划分 这是一些代码:
static const int rangeCount = 5;
static const int rangeLimit[rangeCount] = {13, 20, 27, 33, 36};
static uint32_t rangeProduct[rangeCount] = {
479001600,
253955520,
3315312000,
652458240,
39270
};
for (int rangeIndex = 0; rangeIndex < rangeCount; ++rangeIndex)
{
// The following two lines involve long division;
// math libraries probably calculate both quotient and remainder
// in one function call
uint32_t rangeRemainder = k % rangeProduct[rangeIndex];
k /= rangeProduct[rangeIndex];
// A range starts where the previous range ended
int rangeStart = (rangeIndex == 0) ? 2 : rangeLimit[rangeIndex - 1];
// Iterate over range
for (int i = rangeStart; i < rangeLimit[rangeIndex] && i < n; ++i)
{
// The following two lines involve a 32-bit division;
// it produces both quotient and remainder in one Pentium instruction
int remainder = rangeRemainder % i;
rangeRemainder /= i;
std::swap(permutation[n - 1 - i], permutation[n - i + remainder]);
}
}
当然,这段代码可以扩展到128位以上 另一个优化可能涉及从范围的乘积中提取2的幂;这可能会通过延长范围来增加一点点加速。不确定这是否值得(可能是N的大值,如N = 1000)。
答案 1 :(得分:-1)
不了解算法,但你使用的算法看起来很简单,所以我真的不知道如何优化算法。
您可以使用其他方法: