想象一下,你有一个整数x
,你想知道百位是什么数字。(例如,如果 x=4723 你想要答案 7。)要计算这个,你首先除以 100,扔掉小数部分。(在我们的示例中,剩下 47。)然后除以 10 时找到余数。
现在假设您要查找千位中数字的值。要找到你首先除以 1000,丢弃小数部分,然后在除以 10 时再次找到余数。
在常规十进制编号系统中,每个位置都包含 10 个值之一。您可以观察到,在我们的数字查找练习中,我们首先除以我们关心的那个位置右侧的值的可能组合数(第一个示例中为 10 * 10)。然后我们在除以我们关心的地方的可能值的数量时找到余数。当然,所有地方都有 10 个可能的值,所以我们只需除以 10。
现在,想象一个编号系统,其中每个位置都有不同数量的值。我们最右边的位置可以有两个值,0 或 1。下一个位置可以有三个值,0、1 或 2;等等。在这个系统中,我们这样计算:
0
1
10
11
20
21
100
101
110
111
120
121
200
201
210
211
220
...
这就是 wrang-wrang 所指的“可变基数”。
现在,您可以看到我们如何计算该系统中某个位置的数字。要找到最右边的值,我们不需要先除,而是找到余数模 2,因为该列中的一个数字有 2 个可能的值。为了找到左边的下一列,我们首先除以右边列中数字的可能组合数:只有一列有两个可能的数字,所以我们除以 2。然后我们取余数模 3,因为该列有三个可能的值。继续向左,对于第 3 列,我们除以 6(因为右边的列各有 3 和 2 种可能性,相乘得到 6)然后取余数模 4,因为在这一列中有 4 个可能的值。
我们来看看函数:
function permutation(k, s) {
var int n:= length(s); factorial:= 1;
for j= 2 to n- 1 { // compute (n- 1)!
factorial:= factorial* j;
}
factorial
从 (n-1) 开始!
for j= 1 to n- 1 {
每次我们到达这里,factorial
都等于 (nj)!这在第一次很明显,因为j
=1 并且我们知道我们初始化factorial
为 (n-1)!我们稍后会看到factorial
确实总是 (nj)!
tempj:= (k/ factorial) mod (n+ 1- j);
这里我们除以k
(factorial
等于 (nj)!)并丢弃余数,然后当我们将结果除以 (n+1-j) 时取余数。等一下,我开始的所有喋喋不休开始听起来很熟悉!我们只是使用我们的“可变基数系统”在左起第 n 列中找到“数字”的值!
下一位获取索引之间的元素序列j
并将j + tempj
其向右旋转 - 即每个元素都向上移动一个索引,除了最后一个,它移回起点。重要的是要意识到位置 j 右侧的所有数字都是有序的。我们有效地将其中一个拔出并轻推其余的以保持它们井井有条。我们采摘哪一个取决于tempj
。当tempj
为 0 时,我们选择最小的(实际上不需要做任何微调),当tempj
等于 nj 时,我们选择最大的。
temps:= s[j+ tempj]
for i= j+ tempj to j+ 1 step -1 {
s[i]:= s[i- 1]; // shift the chain right
}
s[j]:= temps;
接下来,(nj)!除以 (nj) 得到 (nj-1)!如果你仔细想想,你应该看到这意味着当我们回到循环的顶部并j
增加一时,factorial
将再次等于 (nj)!
factorial:= factorial/ (n- j);
}
return s;
}
我希望这会有所帮助!