JavaScript의 순열?


138

다음을 수행하는 함수를 작성하려고합니다.

  • 정수 배열을 인수로 사용합니다 (예 : [1,2,3,4])
  • [1,2,3,4]의 모든 가능한 순열의 배열을 만듭니다. 각 순열의 길이는 4입니다.

아래 함수 (온라인에서 찾았습니다)는 문자열을 인수로 사용하고 해당 문자열의 모든 순열을 반환 하여이 작업을 수행합니다

정수 배열에서 작동하도록 수정하는 방법을 알 수 없었습니다. (이것은 정수 에서와는 달리 문자열에서 일부 메소드가 다르게 작동하는 방식과 관련이 있다고 생각하지만 확실하지 않습니다. ..)

var permArr = [], usedChars = [];
function permute(input) {
  var i, ch, chars = input.split("");
  for (i = 0; i < chars.length; i++) {
    ch = chars.splice(i, 1);
    usedChars.push(ch);
    if (chars.length == 0)
      permArr[permArr.length] = usedChars.join("");
    permute(chars.join(""));
    chars.splice(i, 0, ch);
    usedChars.pop();
  }
  return permArr
};

참고 : 나는의 함수 반환 배열 만들기 위해 찾고 있어요 정수 , 하지 의 배열 문자열을 .

JavaScript로 솔루션을 사용해야합니다. 파이썬 에서이 작업을 수행하는 방법을 이미 알아 냈습니다.

답변:


106

알다시피, 코드는 순열을 수행하기 전에 실제로 문자를 배열로 분할하므로 결합 및 분할 작업을 제거하면됩니다.

var permArr = [],
  usedChars = [];

function permute(input) {
  var i, ch;
  for (i = 0; i < input.length; i++) {
    ch = input.splice(i, 1)[0];
    usedChars.push(ch);
    if (input.length == 0) {
      permArr.push(usedChars.slice());
    }
    permute(input);
    input.splice(i, 0, ch);
    usedChars.pop();
  }
  return permArr
};


document.write(JSON.stringify(permute([5, 3, 7, 1])));


@SiGanteng. 귀하의 기능을 사용하려고 시도하는 중에 이상한 일이 발생했습니다. 나는 "목록 조작 기능"이 모두있는 .js에 보관한다. permute ([1,2,3]) 및 이후 permute ([4,5,6])와 함께 사용하면 나중에 출력에 여전히 결과가 표시됩니다 (첫 번째 출력). 어떻게 고칠 수 있습니까? 많은 감사합니다!
500


15
당신의 기능, 나쁜 형태로 글로벌에 액세스!
Shmiddty

123

조금 늦었지만 여기에 약간 더 우아한 버전을 추가하고 싶습니다. 모든 배열이 될 수 있습니다 ...

function permutator(inputArr) {
  var results = [];

  function permute(arr, memo) {
    var cur, memo = memo || [];

    for (var i = 0; i < arr.length; i++) {
      cur = arr.splice(i, 1);
      if (arr.length === 0) {
        results.push(memo.concat(cur));
      }
      permute(arr.slice(), memo.concat(cur));
      arr.splice(i, 0, cur[0]);
    }

    return results;
  }

  return permute(inputArr);
}

ES6 (2015) 버전 추가 또한 원래 입력 배열을 변경하지 않습니다. Chrome 콘솔에서 작동합니다 ...

const permutator = (inputArr) => {
  let result = [];

  const permute = (arr, m = []) => {
    if (arr.length === 0) {
      result.push(m)
    } else {
      for (let i = 0; i < arr.length; i++) {
        let curr = arr.slice();
        let next = curr.splice(i, 1);
        permute(curr.slice(), m.concat(next))
     }
   }
 }

 permute(inputArr)

 return result;
}

그래서...

permutator(['c','a','t']);

수확량 ...

[ [ 'c', 'a', 't' ],
  [ 'c', 't', 'a' ],
  [ 'a', 'c', 't' ],
  [ 'a', 't', 'c' ],
  [ 't', 'c', 'a' ],
  [ 't', 'a', 'c' ] ]

과...

permutator([1,2,3]);

수확량 ...

[ [ 1, 2, 3 ],
  [ 1, 3, 2 ],
  [ 2, 1, 3 ],
  [ 2, 3, 1 ],
  [ 3, 1, 2 ],
  [ 3, 2, 1 ] ]

1
(가능성이 순열 다루고있는 고려 상당히처럼) 당신이 편리 계승 기능이있는 경우에 외부 범위 초기화를 변경하여 속도를 높일 수 var results = new Array(factorial(inputArr.length)), length=0후 교체 results.push(…)와 함께results[length++]=…
Cyoce

1
라인 var cur, memo = memo || [];은 무엇을 합니까?
Ricevind

2
@ user2965967 cur 및 memo를 선언하고, false가 아닌 경우 (정의되지 않은 경우 포함) 빈 배열이 될 경우를 제외하고 memo를 memo의 값으로 초기화합니다. 다시 말해, 함수 매개 변수에 기본값을 제공하는 것은 이상적인 방법이 아닙니다.
Mr. Lavalamp

이것은 원래 배열을 수정합니다.
Shmiddty

2
slice()permute(curr.slice(), m.concat(next))정말 필요한?
Yoav

82

다음의 매우 효율적인 알고리즘은 Heap의 방법 을 사용 하여 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(permute([1, 2, 3]));

O (N)에서 공간 복잡성이 있는 생성기 로 구현 된 동일한 알고리즘 :

성능 비교

다음 benchmark.js 테스트 스위트에 구현을 자유롭게 추가하십시오 .

Chrome 48의 런타임 결과 :


1
고정 된 n = 2에 대한 결과를 제공하기 위해이 코드를 어떻게 변경할 수 있습니까? 예를 들어, A, B, C의 세 글자가 있다고 가정 해 봅시다. 우리는 그 세트에서 두 글자를 배열 할 수있는 방법을 몇 개나 물어볼 수 있습니다. 각 가능한 배열은 순열의 예입니다. 가능한 순열의 전체 목록은 AB, AC, BA, BC, CA 및 CB입니다.
a4xrbj1

1
@ a4xrbj1 예를 들어이 질문의 코드 샘플을보십시오 : stackoverflow.com/questions/37892738/…- 또는이 (힙) 방법을 수정하는 것에 대해 구체적으로 질문하고 있습니까?
le_m

@le_m yes, 구체적으로이 (Heap 's) 방법을 사용하여 너무 빠름
a4xrbj1

@ a4xrbj1 I 고정 길이 N의 모든 조합을 계산하는 것 (예를 들면 AB, AC, BC, N = 2) 상기 소정의 링크와 유사한 전략을 사용하여 (참조 stackoverflow.com/questions/127704/... ), 그리고, 조합마다 힙의 방법을 사용하여 모든 순열을 계산합니다. n = 2와 같은 특별한 경우는 물론 최적화 할 수 있습니다.
le_m

1
생성기 버전이 제대로 작동 yield permutation.slice()하지 않으므로 슬라이스하지 않으면 마지막 순열 만 계산해야합니다.
Beldar

41
var inputArray = [1, 2, 3];

var result = inputArray.reduce(function permute(res, item, key, arr) {
    return res.concat(arr.length > 1 && arr.slice(0, key).concat(arr.slice(key + 1)).reduce(permute, []).map(function(perm) { return [item].concat(perm); }) || item);
}, []);


alert(JSON.stringify(result));

10
와우, 간결함과 문서 부족에도 불구하고 이것이 가장 우아한 대답이라고 생각합니다. 이 알고리즘에 대한 설명은 다음과 같습니다. 배열의 모든 항목 (reduce)에 대해 다른 모든 항목을 선택하고 재귀 적으로 (recursively)이 항목에 연결합니다.
aaron

이 솔루션을 여기에서 시도했습니다 : codewars.com/kata/reviews/5254ca2719453dcc0b000280/groups/… 원래 골프 코드를 읽을 수있는 코드로 풀었 지만 본질적으로 동일합니다. 그것에 대한 문제는 그것이 복제물을 생성한다는 것 .filter(uniq)입니다. 결과에 추가 작업을 수행해야했습니다 .
Andrey Mikhaylov-lolmaus

1
개념에 혀짤배기 평행이 [1,2,3].length == 3 && "foo" || "bar"또는 [1,2].length == 3 && "foo" || "bar"내 오! 있습니다! (or (and (= 3 2) (print "hello!")) (print "goodbye"))
Dmitry

@ lolmaus-AndreyMikhaylov 중복을 제거하는 방법 가능한 경우 답변을 업데이트하십시오
Pardeep Jain

@PardeepJain 위의 솔루션에 대한 링크를 제공했습니다.
Andrey Mikhaylov-lolmaus

21

SiGanteng답변 을 개선 했습니다 .

이제 호출 할 수 permute있기 때문에, 한 번 이상 permArr하고 usedChars때마다 지워집니다.

function permute(input) {
    var permArr = [],
        usedChars = [];
    return (function main() {
        for (var i = 0; i < input.length; i++) {
            var ch = input.splice(i, 1)[0];
            usedChars.push(ch);
            if (input.length == 0) {
                permArr.push(usedChars.slice());
            }
            main();
            input.splice(i, 0, ch);
            usedChars.pop();
        }
        return permArr;
    })();
}


10

다음 함수는 모든 유형의 배열을 순열하고 찾은 각 순열에서 지정된 콜백 함수를 호출합니다.

/*
  Permutate the elements in the specified array by swapping them
  in-place and calling the specified callback function on the array
  for each permutation.

  Return the number of permutations.

  If array is undefined, null or empty, return 0.

  NOTE: when permutation succeeds, the array should be in the original state
  on exit!
*/
  function permutate(array, callback) {
    // Do the actual permuation work on array[], starting at index
    function p(array, index, callback) {
      // Swap elements i1 and i2 in array a[]
      function swap(a, i1, i2) {
        var t = a[i1];
        a[i1] = a[i2];
        a[i2] = t;
      }

      if (index == array.length - 1) {
        callback(array);
        return 1;
      } else {
        var count = p(array, index + 1, callback);
        for (var i = index + 1; i < array.length; i++) {
          swap(array, i, index);
          count += p(array, index + 1, callback);
          swap(array, i, index);
        }
        return count;
      }
    }

    if (!array || array.length == 0) {
      return 0;
    }
    return p(array, 0, callback);
  }

다음과 같이 호출하면

  // Empty array to hold results
  var result = [];
  // Permutate [1, 2, 3], pushing every permutation onto result[]
  permutate([1, 2, 3], function (a) {
    // Create a copy of a[] and add that to result[]
    result.push(a.slice(0));
  });
  // Show result[]
  document.write(result);

나는 그것이 당신이 원하는 것을 정확하게 할 것이라고 생각합니다-배열 result의 순열로 불리는 배열을 채 웁니다 [1, 2, 3]. 결과는 다음과 같습니다.

[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,2,1],[3,1,2]]

JSFiddle에서 약간 더 명확한 코드 : http://jsfiddle.net/MgmMg/6/


10

이 질문에 대한 대부분의 답변은 배열에서 항목을 연속으로 삽입하거나 삭제하거나 배열을 반복적으로 복사하는 것과 같은 비싼 작업을 사용합니다.

대신 이것이 전형적인 역 추적 솔루션입니다.

function permute(arr) {
  var results = [],
      l = arr.length,
      used = Array(l), // Array of bools. Keeps track of used items
      data = Array(l); // Stores items of the current permutation
  (function backtracking(pos) {
    if(pos == l) return results.push(data.slice());
    for(var i=0; i<l; ++i) if(!used[i]) { // Iterate unused items
      used[i] = true;      // Mark item as used
      data[pos] = arr[i];  // Assign item at the current position
      backtracking(pos+1); // Recursive call
      used[i] = false;     // Mark item as not used
    }
  })(0);
  return results;
}
permute([1,2,3,4]); // [  [1,2,3,4], [1,2,4,3], /* ... , */ [4,3,2,1]  ]

결과 배열이 크므로 모든 데이터를 동시에 할당하는 대신 결과를 하나씩 반복하는 것이 좋습니다. ES6에서는 생성기를 사용하여 수행 할 수 있습니다.

function permute(arr) {
  var l = arr.length,
      used = Array(l),
      data = Array(l);
  return function* backtracking(pos) {
    if(pos == l) yield data.slice();
    else for(var i=0; i<l; ++i) if(!used[i]) {
      used[i] = true;
      data[pos] = arr[i];
      yield* backtracking(pos+1);
      used[i] = false;
    }
  }(0);
}
var p = permute([1,2,3,4]);
p.next(); // {value: [1,2,3,4], done: false}
p.next(); // {value: [1,2,4,3], done: false}
// ...
p.next(); // {value: [4,3,2,1], done: false}
p.next(); // {value: undefined, done: true}

6

이것은 흥미로운 작업이며 여기 내 공헌입니다. 매우 간단하고 빠릅니다. 관심이 있으시면 제게 참아주세요.

이 일을 빨리하고 싶다면 역동적 인 프로그래밍을해야합니다. 재귀 접근 방식을 잊어야한다는 것을 의미합니다. 그건 확실합니다...

OK 의 힙 방법을 사용하는 le_m의 코드 가 지금까지 가장 빠른 것 같습니다. 글쎄, 내 알고리즘의 이름을 얻지 못했지만, 이미 구현되었는지 아닌지는 모르겠지만 매우 간단하고 빠릅니다. 모든 동적 프로그래밍 방식과 마찬가지로 가장 간단한 문제부터 시작하여 최종 결과를 얻습니다.

의 우리가 배열을 가지고 있다고 가정하면 a = [1,2,3]우리가 시작됩니다

r = [[1]]; // result
t = [];    // interim result

그런 다음이 세 단계를 따르십시오.

  1. r(결과) 배열 의 각 항목 에 대해 입력 배열의 다음 항목을 추가합니다.
  2. 우리는 각 항목의 길이를 여러 번 회전시키고 각 인스턴스를 중간 결과 배열에 저장 t합니다. (0 회전으로 시간을 낭비하지 않는 첫 번째 것을 제외하고 잘)
  3. r중간 배열 의 모든 항목을 마치면 t다음 수준의 결과를 유지해야하므로 r = t; t = [];입력 배열의 길이가 될 때까지 계속 수행합니다 a.

다음은 우리의 단계입니다.

r array   | push next item to |  get length many rotations
          |  each sub array   |       of each subarray
-----------------------------------------------------------
[[1]]     |     [[1,2]]       |     [[1,2],[2,1]]
----------|-------------------|----------------------------
[[1,2],   |     [[1,2,3],     |     [[1,2,3],[2,3,1],[3,1,2],
 [2,1]]   |      [2,1,3]]     |      [2,1,3],[1,3,2],[3,2,1]]
----------|-------------------|----------------------------
previous t|                   |
-----------------------------------------------------------

여기 코드가 있습니다

function perm(a){
  var r = [[a[0]]],
      t = [],
      s = [];
  if (a.length <= 1) return a;
  for (var i = 1, la = a.length; i < la; i++){
    for (var j = 0, lr = r.length; j < lr; j++){
      r[j].push(a[i]);
      t.push(r[j]);
      for(var k = 1, lrj = r[j].length; k < lrj; k++){
        for (var l = 0; l < lrj; l++) s[l] = r[j][(k+l)%lrj];
        t[t.length] = s;
        s = [];
      }
    }
    r = t;
    t = [];
  }
  return r;
}

var arr = [0,1,2,4,5];
console.log("The length of the permutation is:",perm(arr).length);
console.time("Permutation test");
for (var z = 0; z < 2000; z++) perm(arr);
console.timeEnd("Permutation test");

다중 테스트에서 25 ~ 35ms에서 2000 번 120 번의 [0,1,2,3,4] 순열을 해결하는 것으로 나타났습니다.


1
다른 길이 / 워밍업 반복 등을 위해 FF / Ubuntu의 힙 방법보다 실제로 빠르게, 때로는 더 빠르며, 때로는 느리게 실행되는 것처럼 보입니다. 다른 엔진에 대한 결과를 보려면 jsperf가 필요합니다.
le_m

1
@le_m OK 우분투 및 AMD CPU에서 @JSBen 테스트를 수행 했습니다 .ChromerotatePerm (위의 것)이 1.2보다 빠릅니다. FF를 사용하면 일관성이 없습니다. 여러 번의 테스트 후 때로는 heapPerm2 배 더 빠르며 일부 rotatePerm는 1.1 배 빠릅니다. Opera 또는 Epiphany와 같은 다른 웹 키트 브라우저를 사용하면 rotatePerm일관되게 1.1 배 더 빠릅니다. 그러나 Edge를 사용 heapPerm하면 매번 1.2 배 빠릅니다.
Redu

1
좋은! 적어도 FF / Ubuntu에서 힙 방법의 성능은 주로 어레이 복사 성능에 달려있는 것 같습니다. jsben.ch/#/x7mYh-FF 와 작은 입력 배열의 경우 슬라이싱과 푸시를 비교하기 위해 벤치 마크를 수정했습니다. 더 빠른 것 같습니다
le_m

2
힙 방법이 성능면에서 우월 할 수 있다면 좋을 것입니다. 그건 그렇고, 당신의 방법은 힙의 방법에 대한 참조로 사용한 동일한 1977 년 논문에서 Langdon의 알고리즘 (16 페이지)과 동일한 출력을 생성합니다 : homepage.math.uiowa.edu/~goodman/22m150.dir/2007/…
le_m

2
@le_m 방금 확인했는데 같은 것 같습니다. 그가 구현 한 것처럼 회전하는 것 같습니다. 단지 40 년의 지연. 내 대답에서 언급했듯이 실제로 매우 간단한 방법입니다. 빠른 회전이 가능한 경우에만 선택해야합니다. 현재 Haskell에 있으며 목록 (배열이라고 가정)을 무한대로 순환 시키는 내장 방법이 있습니다 (게으른 평가는 무한 반복 문제 없음). 그러나 Haskell은 이미 표준 permutations기능을 가지고 있습니다 :)
Redu

6

Haskell에서 영감을 얻은 일부 버전 :

perms [] = [[]]
perms xs = [ x:ps | x <- xs , ps <- perms ( xs\\[x] ) ]

function perms(xs) {
  if (!xs.length) return [[]];
  return xs.flatMap((xi, i) => {
    // get permutations of xs without its i-th item, then prepend xi to each
    return perms([...xs.slice(0,i), ...xs.slice(i+1)]).map(xsi => [xi, ...xsi]);
  });
}
document.write(JSON.stringify(perms([1,2,3])));


5

외장형 어레이 또는 추가 기능이 필요없는 답변

function permutator (arr) {
  var permutations = [];
  if (arr.length === 1) {
    return [ arr ];
  }

  for (var i = 0; i <  arr.length; i++) { 
    var subPerms = permutator(arr.slice(0, i).concat(arr.slice(i + 1)));
    for (var j = 0; j < subPerms.length; j++) {
      subPerms[j].unshift(arr[i]);
      permutations.push(subPerms[j]);
    }
  }
  return permutations;
}

당신은 그것을 밖으로 칵테일을 만들 수 있습니까? stackoverflow.com/questions/53555563/…
Techdive

5

요즘 가장 빠르고, 가장 (복원) 효과적이고 가장 우아한 버전 (2020)

function getArrayMutations(arr, perms = [], len = arr.length) {
  if (len === 1) perms.push(arr.slice(0))

  for (let i = 0; i < len; i++) {
    getArrayMutations(arr, perms, len - 1)

    len % 2 // parity dependent adjacent elements swap
      ? [arr[0], arr[len - 1]] = [arr[len - 1], arr[0]]
      : [arr[i], arr[len - 1]] = [arr[len - 1], arr[i]]
  }

  return perms
}

const arrayToMutate = [1, 2, 3, 4, 5, 6, 7, 8, 9]

const startTime = performance.now()
const arrayOfMutations = getArrayMutations(arrayToMutate)
const stopTime = performance.now()
const duration = (stopTime - startTime) / 1000

console.log(`${arrayOfMutations.length.toLocaleString('en-US')} permutations found in ${duration.toLocaleString('en-US')}s`)


안녕하세요, 무슨 len % 2 // parity dependent adjacent elements swap의미 인지 , 왜 사용되는지 설명해 주 시겠습니까?
Pramesh Bajracharya

내 코드는 "힙 알고리즘"을 사용하여 배열 순열을 생성합니다. 따라서 내 코드가 어떻게 작동하는지 알고 싶다면 힙 알고리즘에 대한 다음 설명을 읽으십시오. en.m.wikipedia.org/wiki/Heap%27s_algorithm
Vladislav Ladicky

결과를 인쇄하려고 했습니까? 배열 요소가 10보다 큰 경우 최대 값을 제어하는 ​​방법은 무엇입니까?
Marvix

4

멋진 해결책이 있습니다.

const rotations = ([l, ...ls], right=[]) =>
  l ? [[l, ...ls, ...right], ...rotations(ls, [...right, l])] : []

const permutations = ([x, ...xs]) =>
  x ? permutations(xs).flatMap((p) => rotations([x, ...p])) : [[]]
  
console.log(permutations("cat"))


2

또 다른 "더 재귀적인"솔루션이 있습니다.

function perms(input) {
  var data = input.slice();
  var permutations = [];
  var n = data.length;

  if (n === 0) {
    return [
      []
    ];
  } else {
    var first = data.shift();
    var words = perms(data);
    words.forEach(function(word) {
      for (var i = 0; i < n; ++i) {
        var tmp = word.slice();
        tmp.splice(i, 0, first)
        permutations.push(tmp);
      }
    });
  }

  return permutations;
}

var str = 'ABC';
var chars = str.split('');
var result = perms(chars).map(function(p) {
  return p.join('');
});

console.log(result);

산출:

[ 'ABC', 'BAC', 'BCA', 'ACB', 'CAB', 'CBA' ]

당신은 그것에 대한 조합을 만들 수 있습니까? stackoverflow.com/questions/53555563/…
Techdive

2
   function perm(xs) {
       return xs.length === 0 ? [[]] : perm(xs.slice(1)).reduce(function (acc, ys) {
        for (var i = 0; i < xs.length; i++) {
          acc.push([].concat(ys.slice(0, i), xs[0], ys.slice(i)));
        }
        return acc;
      }, []);
    }

다음과 같이 테스트하십시오.

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

2

대부분의 다른 답변은 이러한 유형의 문제에 대한 완벽한 솔루션 인 새로운 자바 스크립트 생성기 함수를 사용하지 않습니다. 메모리에서 한 번에 하나의 순열 만 필요합니다. 또한 각 인덱스를 인덱싱하고 특정 순열로 바로 이동할 수 있으며 다른 컬렉션을 순열시키는 데 사용할 수 있으므로 인덱스 범위의 순열을 생성하는 것을 선호합니다.

// ES6 generator version of python itertools [permutations and combinations]
const range = function*(l) { for (let i = 0; i < l; i+=1) yield i; }
const isEmpty = arr => arr.length === 0;

const permutations = function*(a) {
    const r = arguments[1] || [];
    if (isEmpty(a)) yield r;
    for (let i of range(a.length)) {
        const aa = [...a];
        const rr = [...r, ...aa.splice(i, 1)];
        yield* permutations(aa, rr);
    }
}
console.log('permutations of ABC');
console.log(JSON.stringify([...permutations([...'ABC'])]));

const combinations = function*(a, count) {
    const r = arguments[2] || [];
    if (count) {
        count = count - 1;
        for (let i of range(a.length - count)) {
            const aa = a.slice(i);
            const rr = [...r, ...aa.splice(0, 1)];
            yield* combinations(aa, count, rr);
        }
    } else {
        yield r;
    }
}
console.log('combinations of 2 of ABC');
console.log(JSON.stringify([...combinations([...'ABC'], 2)]));



const permutator = function() {
    const range = function*(args) {
        let {begin = 0, count} = args;
        for (let i = begin; count; count--, i+=1) {
            yield i;
        }
    }
    const factorial = fact => fact ? fact * factorial(fact - 1) : 1;

    return {
        perm: function(n, permutationId) {
            const indexCount = factorial(n);
            permutationId = ((permutationId%indexCount)+indexCount)%indexCount;

            let permutation = [0];
            for (const choiceCount of range({begin: 2, count: n-1})) {
                const choice = permutationId % choiceCount;
                const lastIndex = permutation.length;

                permutation.push(choice);
                permutation = permutation.map((cv, i, orig) => 
                    (cv < choice || i == lastIndex) ? cv : cv + 1
                );

                permutationId = Math.floor(permutationId / choiceCount);
            }
            return permutation.reverse();
        },
        perms: function*(n) {
            for (let i of range({count: factorial(n)})) {
                yield this.perm(n, i);
            }
        }
    };
}();

console.log('indexing type permutator');
let i = 0;
for (let elem of permutator.perms(3)) {
  console.log(`${i}: ${elem}`);
  i+=1;
}
console.log();
console.log(`3: ${permutator.perm(3,3)}`);


2
#!/usr/bin/env node
"use strict";

function perm(arr) {
    if(arr.length<2) return [arr];
    var res = [];
    arr.forEach(function(x, i) {
        perm(arr.slice(0,i).concat(arr.slice(i+1))).forEach(function(a) {
            res.push([x].concat(a));
        });
    });
    return res;
}

console.log(perm([1,2,3,4]));

2

여기 제가 만든 것이 있습니다 ...

const permute = (ar) =>
  ar.length === 1 ? ar : ar.reduce( (ac,_,i) =>
    {permute([...ar.slice(0,i),...ar.slice(i+1)]).map(v=>ac.push([].concat(ar[i],v))); return ac;},[]);

그리고 여기에 다시 있지만 덜 간결하게 쓰여 있습니다! ...

function permute(inputArray) {
  if (inputArray.length === 1) return inputArray;
  return inputArray.reduce( function(accumulator,_,index){
    permute([...inputArray.slice(0,index),...inputArray.slice(index+1)])
      .map(value=>accumulator.push([].concat(inputArray[index],value)));
    return accumulator;
  },[]);
}

작동 방식 : 배열이 하나 이상의 요소 인 경우 각 요소를 단계별로 실행하고 나머지 요소를 인수로 사용하여 재귀 호출로 배열합니다. 원래 배열을 변경하지 않습니다.


2

flatMap을 사용한 기능적 답변 :

const getPermutationsFor = (arr, permutation = []) =>
  arr.length === 0
    ? [permutation]
    : arr.flatMap((item, i, arr) =>
        getPermutationsFor(
          arr.filter((_,j) => j !== i),
          [...permutation, item]
        )
      );

1

"use strict";
function getPermutations(arrP) {
    var results = [];
    var arr = arrP;
    arr.unshift(null);
    var length = arr.length;

    while (arr[0] === null) {

        results.push(arr.slice(1).join(''));

        let less = null;
        let lessIndex = null;

        for (let i = length - 1; i > 0; i--) {
            if(arr[i - 1] < arr[i]){
                less = arr[i - 1];
                lessIndex = i - 1;
                break;
            }
        }

        for (let i = length - 1; i > lessIndex; i--) {
            if(arr[i] > less){
                arr[lessIndex] = arr[i];
                arr[i] = less;
                break;
            }
        }

        for(let i = lessIndex + 1; i<length; i++){
           for(let j = i + 1; j < length; j++){
               if(arr[i] > arr[j] ){
                   arr[i] = arr[i] + arr[j];
                   arr[j] = arr[i] - arr[j];
                   arr[i] = arr[i] - arr[j];
               }
           }
        }
    }

    return results;
}

var res = getPermutations([1,2,3,4,5]);
var out = document.getElementById('myTxtArr');
res.forEach(function(i){ out.value+=i+', '});
textarea{
   height:500px;
  width:500px;
}
<textarea id='myTxtArr'></textarea>

사전 순으로 정렬 된 순열을 출력합니다. 숫자로만 작동합니다. 다른 경우에는 34 행에서 스왑 방법을 변경해야합니다.


1

@crl의 Haskell 스타일 솔루션과 비슷하지만 다음과 reduce같이 작업합니다 .

function permutations( base ) {
  if (base.length == 0) return [[]]
  return permutations( base.slice(1) ).reduce( function(acc,perm) {
    return acc.concat( base.map( function(e,pos) {
      var new_perm = perm.slice()
      new_perm.splice(pos,0,base[0])
      return new_perm
    }))
  },[])    
}

1

이것은 map / reduce에 대한 매우 유용한 사용 사례입니다.

function permutations(arr) {
    return (arr.length === 1) ? arr :
    arr.reduce((acc, cv, index) => {
        let remaining = [...arr];
        remaining.splice(index, 1);
        return acc.concat(permutations(remaining).map(a => [].concat(cv,a)));
    }, []);
}
  • 먼저 기본 사례를 처리하고 그 안에 항목 만있는 경우 배열을 반환합니다.
  • 다른 모든 경우
    • 빈 배열을 만듭니다
    • 입력 배열을 반복
    • 현재 값의 배열과 나머지 배열의 모든 순열을 추가하십시오. [].concat(cv,a)

1

다음은 최소 ES6 버전입니다. 평평하고 기능이없는 평평한 것은 Lodash에서 가져올 수 있습니다.

const flatten = xs =>
    xs.reduce((cum, next) => [...cum, ...next], []);

const without = (xs, x) =>
    xs.filter(y => y !== x);

const permutations = xs =>
    flatten(xs.map(x =>
        xs.length < 2
            ? [xs]
            : permutations(without(xs, x)).map(perm => [x, ...perm])
    ));

결과:

permutations([1,2,3])
// [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

1
perm = x => x[0] ?  x.reduce((a, n) => (perm(x.filter(m => m!=n)).forEach(y => a.push([n,...y])), a), []): [[]]

2
설명을 추가해주세요.
Mehdi Bounya

3
이 답변 문제를 해결할 수는 있지만 어떻게 또는 왜 그렇게하는지에 대한 설명은 포함하지 않습니다.
samlev

1

const permutations = array => {
  let permut = [];
  helperFunction(0, array, permut);
  return permut;
};

const helperFunction = (i, array, permut) => {
  if (i === array.length - 1) {
    permut.push(array.slice());
  } else {
    for (let j = i; j < array.length; j++) {
      swapElements(i, j, array);
      helperFunction(i + 1, array, permut);
      swapElements(i, j, array);
    }
  }
};

function swapElements(a, b, array) {
  let temp = array[a];
  array[a] = array[b];
  array[b] = temp;
}

console.log(permutations([1, 2, 3]));


1

많이 늦다. 이것이 누군가를 돕는 경우에도 여전히 그렇습니다.

function permute(arr) {
  if (arr.length == 1) return arr

  let res = arr.map((d, i) => permute([...arr.slice(0, i),...arr.slice(i + 1)])
                              .map(v => [d,v].join(''))).flat()

  return res
}

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


1

간결하면서도 읽기 쉽고 순수하게 기능적인 프로그래밍을 시도하는 버전을 만드는 데 어려움을 겪었습니다.

function stringPermutations ([...input]) {
  if (input.length === 1) return input;

  return input
    .map((thisChar, index) => {
      const remainingChars = [...input.slice(0, index), ...input.slice(index + 1)];
      return stringPermutations(remainingChars)
        .map(remainder => thisChar + remainder);
    })
    .reduce((acc, cur) => [...acc, ...cur]);
}

인수 형식화는 입력 문자열을 배열로 변환합니다. 그것이 너무 마법 적인지 확실하지 않습니다 .. 야생에서 그것을 보았는지 확실하지 않습니다. 실제 가독성 input = [...input]을 위해 함수의 첫 번째 줄을 대신 사용했을 것입니다 .


1

이것은 재귀 적 인 것을 제외하고 힙 알고리즘 (@le_m과 유사)의 구현입니다.

function permute_kingzee(arr,n=arr.length,out=[]) {
    if(n == 1) {
        return out.push(arr.slice());
    } else {
        for(let i=0; i<n; i++) {
            permute_kingzee(arr,n-1, out);
            let j = ( n % 2 == 0 ) ? i : 0;
            let t = arr[n-1];
            arr[n-1] = arr[j];
            arr[j] = t;
        }
        return out;
    }
}

훨씬 더 빠른 것 같습니다 : https://jsfiddle.net/3brqzaLe/


1

사이트에 대한 첫 번째 기여. 또한 내가 한 테스트에 따르면이 코드는이 날짜 이전에 여기에 언급 된 다른 모든 방법보다 빠르게 실행되지만 값이 적 으면 최소화되지만 너무 많이 추가하면 시간이 기하 급수적으로 증가합니다.

function permutations(arr) {
    var finalArr = [];
    function iterator(arrayTaken, tree) {
        var temp;
        for (var i = 0; i < tree; i++) {
            temp = arrayTaken.slice();
            temp.splice(tree - 1 - i, 0, temp.splice(tree - 1, 1)[0]);
            if (tree >= arr.length) {
                finalArr.push(temp);
            } else {
                iterator(temp, tree + 1);
            }
        }
    }
    iterator(arr, 1);
    return finalArr;
};

성능 비교 stackoverflow.com/a/37580979/1647737을 추가했습니다 -자유롭게 업데이트하십시오.
le_m

0

JavaScript로 배열을 치환하는 방법을 보여주는 게시물 을 작성했습니다 . 이를 수행하는 코드는 다음과 같습니다.

var count=0;
function permute(pre,cur){ 
    var len=cur.length;
    for(var i=0;i<len;i++){
        var p=clone(pre);
        var c=clone(cur);
        p.push(cur[i]);
        remove(c,cur[i]);
        if(len>1){
            permute(p,c);
        }else{
            print(p);
            count++;
        }
    }
}
function print(arr){
    var len=arr.length;
    for(var i=0;i<len;i++){
        document.write(arr[i]+" ");
    }
    document.write("<br />");
}
function remove(arr,item){
    if(contains(arr,item)){
        var len=arr.length;
        for(var i = len-1; i >= 0; i--){ // STEP 1
            if(arr[i] == item){             // STEP 2
                arr.splice(i,1);              // STEP 3
            }
        }
    }
}
function contains(arr,value){
    for(var i=0;i<arr.length;i++){
        if(arr[i]==value){
            return true;
        }
    }
    return false;
}
function clone(arr){
    var a=new Array();
    var len=arr.length;
    for(var i=0;i<len;i++){
        a.push(arr[i]);
    }
    return a;
}

그냥 전화

permute ([], [1,2,3,4])

작동합니다. 작동 방식에 대한 자세한 내용은 해당 게시물의 설명을 참조하십시오.


0
function nPr(xs, r) {
    if (!r) return [];
    return xs.reduce(function(memo, cur, i) {
        var others  = xs.slice(0,i).concat(xs.slice(i+1)),
            perms   = nPr(others, r-1),
            newElms = !perms.length ? [[cur]] :
                      perms.map(function(perm) { return [cur].concat(perm) });
        return memo.concat(newElms);
    }, []);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.