[자바스크립트] 집합(초집합, 합집합, 교집합, 차집합, 멱집합)

2023. 5. 17. 13:40

자바스크립트에서는 파이썬의 itertools같이 편리한 내부 라이브러리가 없다.

따라서 모든 집합 연산을 직접 구현해야한다.

다행히 Set 객체는 존재하기 때문에 이 객체를 통해 연산하면 된다.


초집합

Set.prototype.isSuperset = function(subset) {
    for (var elem of subset) {
        if (!this.has(elem)) {
            return false;
        }
    }

    return true;
}

var setA = new Set([1, 2, 3, 4]);
var setB = new Set([2, 3]);
setA.isSuperset(setB);  // true

Set 객체의 prototype에 isSuperset이라는 메소드를 등록한다.

인자로 전달받은 subset의 원소가 Set 객체에 포함되어 있지 않다면 false를 반환한다.

 

 

합집합

Set.prototype.union = function(setB) {
    var union = new Set(this);
    for (var elem of setB) {
        union.add(elem);
    }

    return union;
}

var setA = new Set([1, 2]);
var setB = new Set([2, 3]);
setA.union(setB);  // Set [1, 2, 3]

Set 객체의 prototype에 union이라는 메소드를 등록한다.

원본 Set 객체를 복사하여 union 집합을 만들고, setB의 원소를 union 집합에 add한다.

 

 

교집합

Set.prototype.intersection = function(setB) {
    var intersection = new Set();
    for (var elem of setB) {
        if (this.has(elem)) {
            intersection.add(elem);
        }
    }

    return intersection;
}

var setA = new Set([1, 2, 3, 4]);
var setB = new Set([2, 3]);
setA.intersection(setB);  // Set [1, 2]

Set 객체의 prototype에 intersection이라는 메소드를 등록한다.

intersection이라는 새로운 Set 객체를 만들어 둔다.

기존의 Set 객체에 인자로 전달받은 setB의 원소가 포함되어 있는지 확인하고, 포함되어 있다면 intersection 집합에 그 원소를 add한다.

 

 

차집합

Set.prototype.difference = function(setB) {
    var difference = new Set(this);
    for (var elem of setB) {
        difference.delete(elem);
    }

    return difference;
}

var setA = new Set([1, 2, 3, 4]);
var setB = new Set([1, 4]);
setA.difference(setB);  // Set [2, 3]

Set 객체의 prototype에 difference라는 메소드를 등록한다.

현재 Set 객체를 difference라는 변수로 복사해놓는다.

인자로 전달받은 setB의 원소를 difference 집합에서 delete한다.

 

 

멱집합

function getSubSets(arr) {
    var flag = new Array(arr.length).fill(false);
    var subSets = [];

    var subSet = function DFS(depth) {
        if (depth === arr.length) {
            var element = arr.filter((_, index) => flag[index]);
            subSets.push(element);

            return;
        }

        flag[depth] = true;
        subSet(depth + 1);

        flag[depth] = false;
        subSet(depth + 1);
    }

    subSet(0);
    return subSets;
}

완전 탐색을 이용해서 함수를 구현한다.

집합 [1, 2]를 예시로 tree를 통해 멱집합을 구해보면 아래 그림과 같다.

그림

왼쪽 노드를 선택했을 때, 오른쪽 노드는 선택되지 않는다.

같은 Depth에 있는 노드값들은 모두 동일하기 때문에 한 쪽 노드만 선택되면서 중복된 노드값이 선택되지 않게 된다.

 

코드를 살펴보면

var flag = new Array(arr.length).fill(false);

flag는 선택된 노드를 표시해둘 배열이다. 이 flag 배열의 상태에 따라(true인 인덱스만 사용) 원본 집합에서 부분집합들을 구하게 된다.

 

var subSets = [];

구한 부분집합들을 담아둘 배열이다.

 

var subSet = function DFS(depth) {
        if (depth === arr.length) {
            var element = arr.filter((_, index) => flag[index]);
            subSets.push(element);

            return;
        }

        flag[depth] = true;
        subSet(depth + 1);

        flag[depth] = false;
        subSet(depth + 1);
    }

subset은 메인 로직 함수를 가지고 있는 객체이다.

인자로 전달받은 depth가 원본 집합의 개수와 같게될 때를 탈출 조건으로 갖는다.

이 때는 filter() 메소드를 사용해서 원본 집합 arr의 인덱스 중에 flag[index]가 true인 값만 골라 element에 담고, subSets 배열에 push한다.

 

// 왼쪽 노드 선택
flag[depth] = true;
subSet(depth + 1);

// 왼쪽 노드 선택 안함(오른쪽 노드 선택)
flag[depth] = false;
subSet(depth + 1);

탈출 조건에 도달하지 않았다면 왼쪽 노드를 선택했을 때와, 오른쪽 노드를 선택했을 때를 구분하여 재귀해준다. 재귀함수를 호출할 때는 depth를 1 더해주어야 한다.

 

subSet(0);
return subSets;

구현한 subSet객체에 0을 인자로 전달해서 함수를 호출하고, 구해진 멱집합을 반환한다.

 

파이썬의 itertools 라이브러리에 있는 combinations처럼 사용하고 싶다면 아래와 같이 코드를 수정하면 된다.

function getSubSets(arr, n) {
    if (n === -1) {
        var flag = new Array(arr.length).fill(false);
        var subSets = [];
    
        var subSet = function DFS(depth) {
            if (depth === arr.length) {
                var element = arr.filter((_, index) => flag[index]);
                subSets.push(element);
    
                return;
            }
    
            flag[depth] = true;
            subSet(depth + 1);
    
            flag[depth] = false;
            subSet(depth + 1);
        }
    
        subSet(0);
        return subSets;
    }
    else {
        var flag = new Array(arr.length).fill(false);
        var subSets = [];
    
        var subSet = function DFS(depth) {
            if (depth === arr.length) {
                var element = arr.filter((_, index) => flag[index]);
                subSets.push(element);
    
                return;
            }
    
            flag[depth] = true;
            subSet(depth + 1);
    
            flag[depth] = false;
            subSet(depth + 1);
        }
    
        subSet(0);
        return subSets.filter(arr => arr.length === n);
    }
}

인자 n이 -1이면 단순히 멱집합을 구하고, 그렇지 않다면 부분집합 중 원소의 개수가 n인 부분집합만 반환하는 함수이다.


참고한 글

 

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Set

 

Set - JavaScript | MDN

Set 객체는 자료형에 관계 없이 원시 값과 객체 참조 모두 유일한 값을 저장할 수 있습니다.

developer.mozilla.org

 

https://code-designer.tistory.com/56

 

자바스크립트 모든 부분 집합(멱집합) 구하기

📌 멱집합 PowerSet? 구할 수 있는 모든 부분 집합의 집합 === 멱집합 "모든 부분집합 모음"이라고 쉽게 생각하자. 예를 들어, [1, 2, 3, 4, 5]와 같은 배열의 멱집합을 구한다고 한다면, 말 그대로 "부분

code-designer.tistory.com

'코딩테스트 > 메모' 카테고리의 다른 글

집합  (0) 2022.11.08
진수 변환  (0) 2022.11.08
  (0) 2022.11.08
소인수분해  (0) 2022.10.19
피보나치 수  (0) 2022.10.13

BELATED ARTICLES

more