【问题标题】:Permutations without recursive function call没有递归函数调用的排列
【发布时间】:2016-03-04 23:29:10
【问题描述】:

要求:生成集合所有可能组合的算法,不重复,或递归调用函数返回结果。

Permutations in JavaScript? 提供的大部分(如果不是全部)答案会从循环或其他函数中递归调用函数以返回结果。

循环内递归函数调用示例

function p(a, b, res) {
  var b = b || [], res = res || [], len = a.length;
  if (!len) 
    res.push(b)
  else 
    for (var i = 0; i < len 
         // recursive call to `p` here
       ; p(a.slice(0, i).concat(a.slice(i + 1, len)), b.concat(a[i]), res)
       , i++
    );
  return res
}

p(["a", "b", "c"]);

当前问题尝试在线性过程中创建给定的排列,依赖于先前的排列。

例如,给定一个数组

var arr = ["a", "b", "c"];

确定可能排列的总数

for (var len = 1, i = k = arr.length; len < i ; k *= len++);

k 应该返回 6arr ["a", "b", "c"] 的可能排列总数

在为 set 确定单个排列的总数后,可以使用 Array.prototype.slice()Array.prototype.concat()Array.prototype.reverse() 创建和填充包含所有六个排列的结果数组

var res = new Array(new Array(k));

res[0] = arr;

res[1] = res[0].slice(0,1).concat(res[0].slice(-2).reverse());

res[2] = res[1].slice(-1).concat(res[1].slice(0,2));

res[3] = res[2].slice(0,1).concat(res[2].slice(-2).reverse());

res[4] = res[3].slice(-2).concat(res[3].slice(0,1));

res[5] = res[4].slice(0,1).concat(res[4].slice(-2).reverse());

尝试根据图表上显示的模式重现结果,An Ordered Lexicographic Permutation Algorithm 基于在 C++ 中的实用算法Calculating Permutations and Job Interview Questions 上发表的算法。

如果输入集是 ,则似乎有一个可以扩展的模式,例如

["a", "b", "c", "d", "e"]

预计会有 120 个排列。

尝试仅依赖先前排列填充数组的示例

// returns duplicate entries at `j`
var arr = ["a", "b", "c", "d", "e"], j = [];
var i = k = arr.length;
arr.forEach(function(a, b, array) {
 if (b > 1) {
  k *= b;
  if (b === i -1) {
    for (var q = 0;j.length < k;q++) {
      if (q === 0) {
       j[q] = array;
      } else {
       j[q] = !(q % i) 
              ? array.slice(q % i).reverse().concat(array.slice(0, q % i)) 
              : array.slice(q % i).concat(array.slice(0, q % i));
      }
    }
  }
 }
})

但是还不能对.slice().concat().reverse() 以上js 的参数进行必要的调整,以从一种排列过渡到另一种排列;而仅使用res 中的前一个数组条目来确定当前排列,而不使用递归。

注意到偶数、奇数的调用平衡,并尝试使用模数 % 运算符和输入数组 .length 来调用 .reverse() 或不在 ["a", "b", "c", "d", "e"] 数组中调用,但没有产生没有重复条目的结果。

预期的结果是上述模式可以减少到在整个过程中连续调用两行,直到所有排列完成,res 填充;拨打.reverse()各一个,不拨打.reverse();例如,res[0] 填充后

// odd , how to adjust `.slice()` , `.concat()` parameters 
// for array of unknown `n` `.length` ?
res[i] = res[i - 1].slice(0,1).concat(res[i - 1].slice(-2).reverse());
// even    
res[i] = res[1 - 1].slice(-1).concat(res[i - 1].slice(0,2));

问题:需要对上述模式进行哪些调整,特别是参数或索引,通过.slice().concat() 生成给定集合的所有可能排列,而不使用对当前处理函数的递归调用?

var arr = ["a", "b", "c"];

for (var len = 1, i = k = arr.length; len < i; k *= len++);

var res = new Array(new Array(k));

res[0] = arr;

res[1] = res[0].slice(0, 1).concat(res[0].slice(-2).reverse());

res[2] = res[1].slice(-1).concat(res[1].slice(0, 2));

res[3] = res[2].slice(0, 1).concat(res[2].slice(-2).reverse());

res[4] = res[3].slice(-2).concat(res[3].slice(0, 1));

res[5] = res[4].slice(0, 1).concat(res[4].slice(-2).reverse());

console.log(res);

编辑、更新

已经找到了一种利用上述模式的过程,使用单个 for 循环以字典顺序为输入最多返回 .length 4 的排列。对于 .length5 的数组,不会返回预期结果。

该模式基于“计算排列和求职面试问题”中的第二张图表[0]

不希望使用.splice().sort() 来返回结果,尽管在此处使用时尝试遵守每列的最后一个“旋转” 要求。变量r 应该引用下一个排列的第一个元素的index,它确实如此。

如果.splice().sort() 的使用遵循图表中的模式,则可以包括它们的使用;虽然在下面的js,他们实际上没有。

不完全确定下面js 的问题只是if (i % (total / len) === reset) 之后的声明,尽管这部分需要花费最多的时间;但仍未返回预期结果。

具体来说,现在参考图表,在旋转时,例如2 到索引01 到索引2。试图通过使用 r 来实现这一点,这是一个负索引,从右到左遍历以检索应该位于相邻“列”的 index0 的下一个项目。

在下一列,2 将放置在 index 23 将放置在 index 0。这是部分,就目前为止能够掌握或调试的情况而言,是发生错误的区域。

同样,返回 [1,2,3,4] 的预期结果,但不是 [1,2,3,4,5] 的预期结果

var arr = [1, 2, 3, 4];
for (var l = 1, j = total = arr.length; l < j ; total *= l++);
for (var i = 1
     , reset = 0
     , idx = 0
     , r = 0
     , len = arr.length
     , res = [arr]
     ; i < total; i++) {
  // previous permutation
  var prev = res[i - 1];
  // if we are at permutation `6` here, or, completion of all 
  // permutations beginning with `1`;
  // setting next "column", place `2` at `index` 0;
  // following all permutations beginning with `2`, place `3` at
  // `index` `0`; with same process for `3` to `4`
  if (i % (total / len) === reset) {
    r = --r % -(len);
    var next = prev.slice(r);
    if (r === -1) {
      // first implementation used for setting item at index `-1`
      // to `index` 0
      // would prefer to use single process for all "rotations",
      // instead of splitting into `if` , `else`, though not there, yet
      res[i] = [next[0]].concat(prev.slice(0, 1), prev.slice(1, len - 1)
               .reverse());
    } else {
      // workaround for "rotation" at from `index` `r` to `index` `0`
      // the chart does not actually use the previous permutation here,
      // but rather, the first permutation of that particular "column";
      // here, using `r` `,i`, `len`, would be 
      // `res[i - (i - 1) % (total / len)]`
      var curr = prev.slice();
      // this may be useful, to retrieve `r`, 
      // `prev` without item at `r` `index`
      curr.splice(prev.indexOf(next[0]), 1);
      // this is not optiomal
      curr.sort(function(a, b) {
        return arr.indexOf(a) > arr.indexOf(b)
      });
      // place `next[0]` at `index` `0`
      // place remainder of sorted array at `index` `1` - n
      curr.splice(0, 0, next[0])
      res[i] = curr
    }
    idx = reset;
  } else {
    if (i % 2) {
      // odd
      res[i] = prev.slice(0, len - 2).concat(prev.slice(-2)
              .reverse())
    } else {
      //  even
      --idx
      res[i] = prev.slice(0, len - (len - 1))
               .concat(prev.slice(idx), prev.slice(1, len + (idx)))
    }
  }
}
// try with `arr` : `[1,2,3,4,5]` to return `res` that is not correct;
// how can above `js` be adjusted to return correct results for `[1,2,3,4,5]` ?
console.log(res, res.length)

资源:

Generating Permutation with Javascript

(Countdown) QuickPerm Head Lexicography: (Formally Example_03 ~ Palindromes)

Generating all Permutations [non-recursive] (尝试从C++ 移植到javascript jsfiddle http://jsfiddle.net/tvvvjf3p/

Calculating Permutation without Recursion - Part 2

permutations of a string using iteration

iterative-permutation

Permutations by swapping

Evaluation of permutation algorithms

Permutation algorithm without recursion? Java

Non-recursive algorithm for full permutation with repetitive elements?

String permutations in Java (non-recursive)

Generating permutations lazily

How to generate all permutations of a list in Python

Can all permutations of a set or string be generated in O(n log n) time?

Finding the nth lexicographic permutation of ‘0123456789’

Combinations and Permutations

【问题讨论】:

标签: javascript algorithm permutation


【解决方案1】:

Here is an answer 来自@le_m。可能会有帮助。

以下非常高效的算法使用Heap's method 生成运行时复杂度为 O(N!) 的 N 个元素的所有排列:

function permute(permutation) {
  var length = permutation.length,
      result = [permutation.slice()],
      c = new Array(length).fill(0),
      i = 1, k, p;

  while (i < length) {
    if (c[i] < i) {
      k = i % 2 && c[i];
      p = permutation[i];
      permutation[i] = permutation[k];
      permutation[k] = p;
      ++c[i];
      i = 1;
      result.push(permutation.slice());
    } else {
      c[i] = 0;
      ++i;
    }
  }
  return result;
}

console.log(JSON.stringify(permute([1, 2, 3, 4])));

【讨论】:

    【解决方案2】:

    一个相当简单的没有递归的 C++ 代码。

    #include <vector>
    #include <algorithm>
    #include <iterator>
    #include <iostream>
    #include <string>
    
    // Integer data
    void print_all_permutations(std::vector<int> &data) {
        std::stable_sort(std::begin(data), std::end(data));
        do {
            std::copy(data.begin(), data.end(), std::ostream_iterator<int>(std::cout, " ")), std::cout << '\n';
        } while (std::next_permutation(std::begin(data), std::end(data)));
    }
    
    // Character data (string)
    void print_all_permutations(std::string &data) {
        std::stable_sort(std::begin(data), std::end(data));
        do {
            std::copy(data.begin(), data.end(), std::ostream_iterator<char>(std::cout, " ")), std::cout << '\n';
        } while (std::next_permutation(std::begin(data), std::end(data)));
    }
    
    int main()
    {
        std::vector<int> v({1,2,3,4});
        print_all_permutations(v);
    
        std::string s("abcd");
        print_all_permutations(s);
    
        return 0;
    }
    

    我们可以在线性时间内找到序列的下一个排列。

    【讨论】:

    • 代码是否只处理字符串输入?代码中是否有任何部分无法使用 Emscripten 转换为 JavaScript?
    • 其实就是处理整数数据的代码。同样,同样的算法也可以用于字符串。
    • 预期的输入是什么?
    • 添加了整数和字符(字符串)的示例代码以及运行代码的主函数。
    【解决方案3】:

    我认为post 应该对您有所帮助。该算法应该很容易转换为 JavaScript(我认为它已经超过 70% 与 JavaScript 兼容)。

    slicereverse 如果您追求效率,则不适合使用。帖子中描述的算法遵循 next_permutation 函数的最有效实现,甚至集成在某些编程语言中(例如 C++)

    编辑

    当我再次对算法进行迭代时,我认为您可以删除变量的类型,并且应该很好地使用 JavaScript。

    编辑

    JavaScript 版本:

    function nextPermutation(array) {
        // Find non-increasing suffix
        var i = array.length - 1;
        while (i > 0 && array[i - 1] >= array[i])
            i--;
        if (i <= 0)
            return false;
    
        // Find successor to pivot
        var j = array.length - 1;
        while (array[j] <= array[i - 1])
            j--;
        var temp = array[i - 1];
        array[i - 1] = array[j];
        array[j] = temp;
    
        // Reverse suffix
        j = array.length - 1;
        while (i < j) {
            temp = array[i];
            array[i] = array[j];
            array[j] = temp;
            i++;
            j--;
        }
        return true;
    }
    

    【讨论】:

      【解决方案4】:

      这是一个计算字符串第 n 次排列的简单解决方案:

      function string_nth_permutation(str, n) {
          var len = str.length, i, f, res;
      
          for (f = i = 1; i <= len; i++)
              f *= i;
      
          if (n >= 0 && n < f) {
              for (res = ""; len > 0; len--) {
                  f /= len;
                  i = Math.floor(n / f);
                  n %= f;
                  res += str.charAt(i);
                  str = str.substring(0, i) + str.substring(i + 1);
              }
          }
          return res;
      }
      

      算法遵循以下简单步骤:

      • 首先计算f = len!,有factorial(len) 一组len 不同元素的总排列。
      • 作为第一个元素,将排列数除以(len-1)! 并选择结果偏移处的元素。有(len-1)! 不同的排列,它们将任何给定元素作为它们的第一个元素。
      • 从集合中移除所选元素,并使用除法的剩余部分作为排列数以继续进行。
      • 与其余部分一起执行这些步骤,其长度减一。

      这个算法非常简单,并且有一些有趣的特性:

      • 它直接计算第 n 个排列。
      • 如果集合是有序的,则排列按字典顺序生成。
      • 即使集合元素不能相互比较,它也能工作,例如对象、数组、函数...
      • 排列数0 是按给定顺序排列的集合。
      • 排列数factorial(a.length)-1是最后一个:集合a倒序排列。
      • 超出此范围的排列返回未定义。

      它可以很容易地转换为处理存储为数组的集合:

      function array_nth_permutation(a, n) {
          var b = a.slice();  // copy of the set
          var len = a.length; // length of the set
          var res;            // return value, undefined
          var i, f;
      
          // compute f = factorial(len)
          for (f = i = 1; i <= len; i++)
              f *= i;
      
          // if the permutation number is within range
          if (n >= 0 && n < f) {
              // start with the empty set, loop for len elements
              for (res = []; len > 0; len--) {
                  // determine the next element:
                  // there are f/len subsets for each possible element,
                  f /= len;
                  // a simple division gives the leading element index
                  i = Math.floor(n / f);
                  // alternately: i = (n - n % f) / f;
                  res.push(b.splice(i, 1)[0]);
                  // reduce n for the remaining subset:
                  // compute the remainder of the above division
                  n %= f;
                  // extract the i-th element from b and push it at the end of res
              }
          }
          // return the permutated set or undefined if n is out of range
          return res;
      }
      

      澄清:

      • f 首先计算为 factorial(len)
      • 对于每一步,f 除以 len,得到前一个阶乘。
      • n 除以 f 的新值得出具有相同初始元素的 len 插槽中的插槽号。 Javascript 没有整数除法,我们可以使用(n / f) ... 0) 将除法的结果转换为整数部分,但它限制了 12 个元素的集合。 Math.floor(n / f) 允许最多 18 个元素的集合。我们也可以使用(n - n % f) / f,也可能更高效。
      • n 必须减少到此槽内的排列数,即除法 n / f 的余数。

      我们可以在第二个循环中以不同的方式使用i,存储除法余数,避免Math.floor() 和额外的% 运算符。这是此循环的替代方案,它可能甚至可读性较差:

              // start with the empty set, loop for len elements
              for (res = []; len > 0; len--) {
                  i = n % (f /= len);
                  res.push(b.splice((n - i) / f, 1)[0]);
                  n = i;
              }
      

      【讨论】:

      • @chqlie 如果可能,可以在第二个 for 循环中包含 cmets,描述每行发生的过程?
      • 可以描述i = (n / (f /= len)) &gt;&gt; 0n -= f * i;的数学、程序化操作吗?
      • 确实如此。 x &gt;&gt;= 0 如果浮点数在]-2147483649 .. 2147483648[ 范围内,则将其转换为其整数部分。 x &gt;&gt;&gt;= 0,它的无符号等价物,首先将x 转换为UInt32,因此适用于x[0 .. 4294967296[ 范围内。然而,在您的情况下,我们应该能够处理超过 12 个元素的集合,从而超出 32 位整数的容量。使用 Math.floor() 最多可以设置 18 个元素。
      • @guest271314: (n - n % f) / f 计算整数除法,无需围绕它调用Math.floor。它执行一个额外的模运算,但避免了对Math.floor 的昂贵查找操作、函数调用开销和实际的floor 浮点运算。所有这些都可以优化,但可能仍然比(n - n % f) / f 慢。
      • @guest271314:如果您使用(n - n % f) / f,您需要事先在单独的语句中计算f /= len
      【解决方案5】:

      我敢再补充一个答案,旨在回答你关于sliceconcatreverse的问题。

      答案是(几乎)有可能,但不会很有效。您在算法中所做的如下:

      • 在排列数组中找到第一个反转,从右到左(反转在这种情况下定义为 ij 其中i j 和 perm[i] > perm[j],索引给定从左到右)
      • 放置较大的反转数
      • 以相反的顺序连接处理后的数字,这将与排序顺序相同,因为没有观察到倒置。
      • 连接倒数的第二个数字(仍然按照前面的数字排序,因为没有观察到倒数)

      这主要是我的第一个答案所做的,但以更优化的方式。

      示例

      考虑排列 9,10, 11, 8, 7, 6, 5, 4 ,3,2,1 从右到左的第一个反转是 10, 11。 实际上,下一个排列是: 9,11,1,2,3,4,5,6,7,8,9,10=9concat(11)concat(rev(8,7,6,5,4,3,2,1))concat (10)

      源代码 在这里,我包含了我所设想的源代码:

      var nextPermutation = function(arr) {
        for (var i = arr.length - 2; i >= 0; i--) {
           if (arr[i] < arr[i + 1]) {
              return arr.slice(0, i).concat([arr[i + 1]]).concat(arr.slice(i + 2).reverse()).concat([arr[i]]);
           }
        }
        // return again the first permutation if calling next permutation on last.
        return arr.reverse();
      }
      
      console.log(nextPermutation([9, 10, 11, 8, 7, 6, 5, 4, 3, 2, 1]));
      console.log(nextPermutation([6, 5, 4, 3, 2, 1]));
      console.log(nextPermutation([1, 2, 3, 4, 5, 6]));
      

      该代码适用于 jsfiddle here

      【讨论】:

      • 是的,尝试使用 .slice().reverse().concat() 和单个 for 循环。试图将 OP 的递归解决方案转换为迭代解决方案,但还不能。您最初的回答实际上要求进一步询问;使用类似的模式创建了一个工作版本。同样,我不愿在此处发布它,因为它实际上并没有解决 OP 中描述的模式。
      • 那么你是在追求 fpr 迭代还是递归?
      • 迭代,非递归。 "考虑排列 9,10, 11, 8, 7, 6, 5, 4 ,3,2,1 从右到左的第一个反转是 10, 11。实际上下一个排列是:9, 11,1,2,3,4,5,6,7,8,9,10=9concat(11)concat(rev(8,7,6,5,4,3,2,1))concat(10 )" 可以通过修改长格式方法(在第六次排列后不返回预期结果)或.forEach() 方法来实现吗?创建了一个 for ,其中包括 input[i] &lt; input[i + 1] ,但迄今为止无法在 Question 尝试使用偶数、奇数方法
      • 试图在for循环中减少到单行或两行;不完整的初始尝试不会返回预期结果jsfiddle.net/ya31o4km/2。注意到问题的长格式模式,其中 -1 应用于 res[2] = res[1].slice(-1).concat(res[1].slice(0,2)); ; jsfiddle 的ni 应该能够用于在六次迭代中以.slice() 递减?在这里缺少一些东西,观察模式时,为什么发布问题
      • @guest271314 我已经添加了我的代码,还没有机会尝试它
      【解决方案6】:

      这可能是另一个解决方案,灵感来自Steinhaus-Johnson-Trotter algorithm

      function p(input) {
        var i, j, k, temp, base, current, outputs = [[input[0]]];
        for (i = 1; i < input.length; i++) {
          current = [];
          for (j = 0; j < outputs.length; j++) {
            base = outputs[j];
            for (k = 0; k <= base.length; k++) {
              temp = base.slice();
              temp.splice(k, 0, input[i]);
              current.push(temp);
            }
          }
          outputs = current;
        }
        return outputs;
      }
      
      // call
      
      var outputs = p(["a", "b", "c", "d"]);
      for (var i = 0; i < outputs.length; i++) {
        document.write(JSON.stringify(outputs[i]) + "<br />");
      }

      【讨论】:

        【解决方案7】:

        创建排列的一种方法是在迄今为止的所有结果中的所有元素之间的所有空间中添加每个元素。这可以在不使用循环和队列的递归的情况下完成。

        JavaScript 代码:

        function ps(a){
          var res = [[]];
        
          for (var i=0; i<a.length; i++){
            while(res[res.length-1].length == i){
              var l = res.pop();
              for (var j=0; j<=l.length; j++){
                var copy = l.slice();
                copy.splice(j,0,a[i]);
                res.unshift(copy);
              }
            }
          }
          return res;
        }
        
        console.log(JSON.stringify(ps(['a','b','c','d'])));
        

        【讨论】:

        • 解决方案可以进行哪些调整以按字典顺序返回结果?
        • @guest271314 我不确定是否可以调整此算法以按字典顺序输出排列。但是,有一个已知的“下一个字典排列”算法:en.wikipedia.org/wiki/…
        • 能够在结果上使用.sort() 转换为字典顺序,但会在循环中尝试其他方法
        • return res.sort() 似乎实现了字典顺序。这会返回意外结果的任何情况?也许.sort() 也可以用于返回排列?当在数组中播种具有.length 的总可能排列的数组时,其中每个内部数组都是原始输入数组?
        • sort() 函数将尝试按字典(或用户定义)顺序对您提供的输入进行排序。我看不出它如何用于返回排列 - 如果你给 sort() 两个具有相同元素的数组以不同的顺序,sort() 将返回两个相同的数组 - 都以相同的方式排序。
        【解决方案8】:

        这是我自己想出的一种方法的sn-p,但自然也能找到它described elsewhere

        generatePermutations = function(arr) {
          if (arr.length < 2) {
            return arr.slice();
          }
          var factorial = [1];
          for (var i = 1; i <= arr.length; i++) {
            factorial.push(factorial[factorial.length - 1] * i);
          }
        
          var allPerms = [];
          for (var permNumber = 0; permNumber < factorial[factorial.length - 1]; permNumber++) {
            var unused = arr.slice();
            var nextPerm = [];
            while (unused.length) {
              var nextIndex = Math.floor((permNumber % factorial[unused.length]) / factorial[unused.length - 1]);
              nextPerm.push(unused[nextIndex]);
              unused.splice(nextIndex, 1);
            }
            allPerms.push(nextPerm);
          }
          return allPerms;
        };
        Enter comma-separated string (e.g. a,b,c):
        <br/>
        <input id="arrInput" type="text" />
        <br/>
        <button onclick="perms.innerHTML = generatePermutations(arrInput.value.split(',')).join('<br/>')">
          Generate permutations
        </button>
        <br/>
        <div id="perms"></div>

        说明

        由于给定数组arrfactorial(arr.length) 排列,因此0factorial(arr.length)-1 之间的每个数字都对特定排列进行编码。要取消编码排列数,请重复从arr 中删除元素,直到没有元素为止。要删除的元素的确切索引由公式(permNumber % factorial(arr.length)) / factorial(arr.length-1) 给出。可以使用其他公式来确定要删除的索引,只要它保留数字和排列之间的一对一映射即可。

        示例

        以下是如何为数组 (a,b,c,d) 生成所有排列:

        #    Perm      1st El        2nd El      3rd El    4th El
        0    abcd   (a,b,c,d)[0]   (b,c,d)[0]   (c,d)[0]   (d)[0]
        1    abdc   (a,b,c,d)[0]   (b,c,d)[0]   (c,d)[1]   (c)[0]
        2    acbd   (a,b,c,d)[0]   (b,c,d)[1]   (b,d)[0]   (d)[0]
        3    acdb   (a,b,c,d)[0]   (b,c,d)[1]   (b,d)[1]   (b)[0]
        4    adbc   (a,b,c,d)[0]   (b,c,d)[2]   (b,c)[0]   (c)[0]
        5    adcb   (a,b,c,d)[0]   (b,c,d)[2]   (b,c)[1]   (b)[0]
        6    bacd   (a,b,c,d)[1]   (a,c,d)[0]   (c,d)[0]   (d)[0]
        7    badc   (a,b,c,d)[1]   (a,c,d)[0]   (c,d)[1]   (c)[0]
        8    bcad   (a,b,c,d)[1]   (a,c,d)[1]   (a,d)[0]   (d)[0]
        9    bcda   (a,b,c,d)[1]   (a,c,d)[1]   (a,d)[1]   (a)[0]
        10   bdac   (a,b,c,d)[1]   (a,c,d)[2]   (a,c)[0]   (c)[0]
        11   bdca   (a,b,c,d)[1]   (a,c,d)[2]   (a,c)[1]   (a)[0]
        12   cabd   (a,b,c,d)[2]   (a,b,d)[0]   (b,d)[0]   (d)[0]
        13   cadb   (a,b,c,d)[2]   (a,b,d)[0]   (b,d)[1]   (b)[0]
        14   cbad   (a,b,c,d)[2]   (a,b,d)[1]   (a,d)[0]   (d)[0]
        15   cbda   (a,b,c,d)[2]   (a,b,d)[1]   (a,d)[1]   (a)[0]
        16   cdab   (a,b,c,d)[2]   (a,b,d)[2]   (a,b)[0]   (b)[0]
        17   cdba   (a,b,c,d)[2]   (a,b,d)[2]   (a,b)[1]   (a)[0]
        18   dabc   (a,b,c,d)[3]   (a,b,c)[0]   (b,c)[0]   (c)[0]
        19   dacb   (a,b,c,d)[3]   (a,b,c)[0]   (b,c)[1]   (b)[0]
        20   dbac   (a,b,c,d)[3]   (a,b,c)[1]   (a,c)[0]   (c)[0]
        21   dbca   (a,b,c,d)[3]   (a,b,c)[1]   (a,c)[1]   (a)[0]
        22   dcab   (a,b,c,d)[3]   (a,b,c)[2]   (a,b)[0]   (b)[0]
        23   dcba   (a,b,c,d)[3]   (a,b,c)[2]   (a,b)[1]   (a)[0]
        

        请注意,每个排列 # 的形式为:

        (firstElIndex * 3!) + (secondElIndex * 2!) + (thirdElIndex * 1!) + (fourthElIndex * 0!)
        

        这基本上是解释中给出的公式的逆过程。

        【讨论】:

          猜你喜欢
          • 2011-09-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-10-03
          • 2011-07-21
          • 1970-01-01
          • 2021-08-01
          • 1970-01-01
          相关资源
          最近更新 更多