在不产生所有可能组合的情况下获得组合偏移量

时间:2018-01-27 17:20:03

标签: php math

我试图从6个字符组合与任何字符a-z,0-9的所有可能组合中获得特定组合。有几亿种可能的组合,我可以通过递归生成组合时找到偏移量,但我更倾向于使用不需要存储所有先前组合的算法来达到给定的偏移量。我不是一个数学家,我认为这是一个很好的数学家。任何建议将不胜感激......谢谢

我发现了这个问题:Find the index of a specific combination without generating all ncr combinations

..但我不擅长C#,也不确定我是否可以正确地将其翻译成PHP。

1 个答案:

答案 0 :(得分:2)

我不是一个PHP家伙,但希望没有高级魔法的JavaScript代码可能是我们的共同点。所以首先是一些代码:

const defaultChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

function indexToPermutation(index, targetLength, chars) {
    if (arguments.length < 3)
        chars = defaultChars;
    const charsLen = chars.length;
    let combinationChars = [];
    for (let i = 0; i < targetLength; i++) {
        let ch = chars[index % charsLen];
        combinationChars.push(ch);
        index = Math.floor(index / charsLen); //integer division
    }
    return combinationChars.reverse().join("")
}

function permutationToIndex(combination, chars) {
    if (arguments.length < 2)
        chars = defaultChars;
    const charsLen = chars.length;
    let index = 0;
    let combinationChars = combination.split("");
    for (let i = 0; i < combination.length; i++) {
        let digit = chars.indexOf(combinationChars[i]);
        index = index * charsLen + digit;
    }
    return index;
}


function indexToCombination(index, targetLength, chars) {
    if (arguments.length < 3)
        chars = defaultChars;

    let base = chars.length - targetLength + 1;
    let combinationIndices = [];
    for (let i = 0; i < targetLength; i++) {
        let digit = index % base;
        combinationIndices.push(digit);
        index = Math.floor(index / base); //integer division
        base++;
    }
    let combinationChars = [];
    for (let i = targetLength - 1; i >= 0; i--) {
        let ch = chars[combinationIndices[i]];
        combinationChars.push(ch);
        // here it is important that chars is only local copy rather than global variable
        chars = chars.slice(0, combinationIndices[i]) + chars.slice(combinationIndices[i] + 1);  // effectively chars.removeAt(combinationIndices[i])
    }

    return combinationChars.join("")
}

function combinationToIndex(combination, chars) {
    if (arguments.length < 2)
        chars = defaultChars;
    const charLength = chars.length; // full length before removing!
    let combinationChars = combination.split("");
    let digits = [];
    for (let i = 0; i < combination.length; i++) {
        let digit = chars.indexOf(combinationChars[i]);
        // here it is important that chars is only local copy rather than global variable
        chars = chars.slice(0, digit) + chars.slice(digit + 1); // effectively chars.removeAt(digit)
        digits.push(digit);
    }
    let index = 0;
    let base = charLength;
    for (let i = 0; i < combination.length; i++) {
        index = index * base + digits[i];
        base--;
    }
    return index;
}

以下是一些结果

indexToPermutation(0, 6) => "000000"
indexToPermutation(1, 6) => "000001"
indexToPermutation(123, 6) => "00003F"
permutationToIndex("00003F") => 123
permutationToIndex("000123") => 1371

indexToCombination(0,6) => "012345"
indexToCombination(1,6) => "012346"
indexToCombination(123,6) => "01237Z"
combinationToIndex("01237Z") => 123
combinationToIndex("543210") => 199331519

显然,如果您的数字和字母顺序不同,您可以通过明确传递chars(最后一个参数)或更改defaultChars来更改它。

背后的一些数学

对于排列,您可能会看到一个字符串作为用36 - 基础数字系统(36 = 10位+ 26个字母)写的数字,类似于十六进制的工作原理。所以转换索引&lt; =&gt;换句实际上是转换到该数字系统的简单工作。

对于组合的想法很相似,但数字系统就是我所说的&#34;变量基数&#34;数字系统,其基数从36更改为36-n+1(其中n是目标组合长度)。一个更熟悉的&#34;变量基数&#34;数字系统是时钟。如果要将毫秒转换为时间,则首先除以1000毫秒,然后除以60秒,然后除以60分钟,然后除以24小时。这里唯一真正的技巧部分是&#34;数字&#34;允许每个位置取决于先前位置已经使用了哪些数字(所设数字的大小始终相同)。这个棘手的部分是chars参数被修改为在combinationToIndexindexToCombination中每次迭代后删除一个字符的原因。