获得大量元素的第n个排列

时间:2016-10-07 14:57:04

标签: javascript algorithm permutation largenumber

我想编写一个JavaScript程序,在所有可能的排列中排列一组36个数字。 (每秒10次排列)。有36种方法可以安排这些数字。 是的,我知道我的程序直到地球结束才会结束:)(这就是我要展示的内容)。

它从以下顺序开始:

  1. 排列:0,1,2,3,4,5,6,...,32,33,34,35
  2. 排列:0,1,2,3,4,5,6,...,32,33,35,34
  3. 排列:0,1,2,3,4,5,6,...,32,34,33,35
  4. .....(这里有更多的排列)....

    有没有办法计算5'595'000'000th(自2000年1月1日起以秒为单位的时间)排列而不计算所有以前的排列? 计算以前的那些将永远需要!

    另外,如果我知道5'595'000'000th排列,我需要一种方法来计算下一个排列。 我找到的所有排列算法一次计算所有排列。这不是多种排列的选择。

    这甚至可能还是我注定要失败?

    输入的Thx

2 个答案:

答案 0 :(得分:1)

这是针对给定排列状态的建议,以获得下一个状态。

这是由两条规则完成的。

  1. 如果最正确的项目大于之前的项目,例如

    1 < 2 < 3 < 4 < 5
                ^ ^ ^
    1   2   3   5   4
    

    然后交换这两个项目。

  2. 从右侧搜索左侧和右侧大于一个项目的项目。

    2 < 5 > 3 < 4 > 1
            ^ ^ ^ ^ ^
    _____| |_________ take the right group, get the next higher value (4) of the most left
     left     right   (3) in this group, add it to the left group and sort the right group
                      and append it to the left group.
    2   5   4   1   3
    
  3. function swap(array) {
        var l = array.length,
            i = l - 2,
            temp,
            right;
    
        if (array[l - 2] < array[l - 1]) {
            temp = array[l - 1];
            array[l - 1] = array[l - 2];
            array[l - 2] = temp;
            return;
        }
        while (i > 0) {
            if (array[i - 1] < array[i] && array[i] > array[i + 1]) {
                right = array.splice(i - 1, l - i + 1);
                temp = right[0];
                right.sort(function (a, b) { return a - b; });
                array.push(right.splice(right.indexOf(temp) + 1, 1)[0]);
                array.splice.apply(array, [3, 0].concat(right));
                return;
            }
            i--;
        }
    }
    
    var i = 0,
        data = [1, 2, 3, 4, 5];
    
    while (i++ < 120) {
        swap(data);
        document.getElementById('out').innerHTML += data.join(' ') + '\n';
    }
    <pre id="out"></pre>

答案 1 :(得分:1)

这是两个功能:

  • getPermutationByRank:按排名编号获取排列(数组)。灵感取自this answer;
  • nextPermutation:将给定的排列变为顺序中的下一个排列。灵感取自this answer

这基本上就是你所需要的,但我添加了一个生成器,它使用上述两个函数从某个等级开始生成排列。

最后,格式化函数将给定的排列转换为字符集(10位+ 26个字母= 36个不同的字符)。

该片段计算自2000-01-01以来的当前十进制秒数,并输出相应的排列,每隔十二秒将其更新为下一个排列:

&#13;
&#13;
// See https://stackoverflow.com/a/7919887/5459839
function getPermutationByRank(n, rank) {
    // Sageguard for inaccurate calculations: rank <= 9007199254740991
    if (rank > Number.MAX_SAFE_INTEGER) throw "Too large rank for JavaScript";
    var perm = (function loop(i, fact) {
        // Calculate factorials and subtract from rank from highest to lowest
        return i > n ? [] :
               [loop(i+1, fact * i).concat(Math.floor(rank / fact) % n),
                rank = rank % fact][0];
    })(1, 1);
    // Readjust values to obtain the permutation
    // start from the end and check if preceding values are lower
    for (let k = n - 1; k > 0; --k)
      for (let j = k - 1; j >= 0; --j)
         if (perm[j] <= perm[k])
            perm[k]++;
    return perm;
}

// See https://stackoverflow.com/a/1622539/5459839:
function nextPermutation(perm) {
    // Find longest non-increasing suffix
    var i = perm.length - 2;
    while (i >= 0 && perm[i] >= perm[i + 1])
        i--;
    // Now i is the head index of the suffix
    
    // Are we at the last permutation already?
    if (i < 0) return; // no more permuations
    
    // Let perm[i] be the pivot
    // Find rightmost element that exceeds the pivot
    var j = perm.length - 1;
    while (perm[j] <= perm[i])
        j--;
    // Now the value perm[j] will become the new pivot
    // Assertion: j >= i
    
    // Swap the pivot with j
    [perm[i], perm[j]] = [perm[j], perm[i]];
    
    // Reverse the suffix
    j = perm.length - 1;
    i++;
    while (i < j) {
        [perm[i], perm[j]] = [perm[j], perm[i]];
        i++;
        j--;
    }
    return perm;
}

// Create a generator using above two functions:
function* generatePermutationsFrom(n, rank) {
    var perm = getPermutationByRank(n, rank);

    yield perm;
    while (nextPermutation(perm)) {
        yield perm;
    }
}

// Utility functions for OP specific requirements
function deciSecondsSince(dt) {
    return Math.floor((Date.now() - dt.getTime())/100);
}

function permToString(perm) {
    var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    return perm.map ( v => chars[v] ).join('');
}

var deciSecs = deciSecondsSince(new Date('2000-01-01')); // ~5293000000 
var iter = generatePermutationsFrom(36, deciSecs);

// Output next permuation every decisecond:
document.body.textContent = permToString(iter.next().value);
setInterval(function () {
    document.body.textContent = permToString(iter.next().value);
}, 100);
&#13;
body { font-family: monospace }
&#13;
&#13;
&#13;

免责声明:

  1. 这适用于您询问的排名数字(如5293000000),但如果排名超过JavaScript可提供的精度,则结果将是错误的。实际上,在第一个函数中计算的阶乘只能精确到18!,之后它们会失去精度。只要您的排名在JavaScript支持的范围内(大约16位数),这对算法没有影响。因此,第一个功能有一个安全措施。

  2. setInterval不是一种跟上时间步伐的非常可靠的方法,所以一段时间后,数字迭代可能与第一次迭代后经过的实际deci-seconds数不同步。但这似乎对你的目的并不重要。如果是,请查看this answer以补偿此类​​漂移