
본 글에서는 java에서 순열을 작성하는 방법을 알아봅니다.
1. 책장을 정리중이다. 책을 어떤 순서로 꽂을까?
2. 놀러가는 3일동안 각 날짜별로 어떤 옷을 입을까?
반복문 사용 → 반복문이 몇 번 중첩되는지 알며, 그 중첩의 깊이가 깊지 않을 때
재귀 사용 → 반복문의 중첩 깊이가 동적일 때
// 원소를 뽑을 수열
int[] nums = {1, 2, 3, 4, 5, 6, 7};
// 동일한 원소를 뽑지 않기 위해 중복 체크
boolean[] check = new boolean[nums.length];
// nums에서 5개를 뽑아 순열을 구성할 배열
int[] permutation = new int[5];
for(int i = 0; i < nums.length; ++i) {
check[i] = true;
permutation[0] = nums[i];
for(int j = 0; j < nums.length; ++j) {
if (check[j]) continue;
check[j] = true;
permutation[1] = nums[j];
for(int k = 0; k < nums.length; ++k) {
if (check[k]) continue;
check[k] = true;
permutation[2] = nums[k];
for(int l = 0; l < nums.length; ++l) {
if (check[l]) continue;
check[l] = true;
permutation[3] = nums[l];
for(int m = 0; m < nums.length; ++m) {
if (check[m]) continue;
permutation[4] = nums[m];
System.out.println(Arrays.toString(permutation));
}
check[l] = false;
}
check[k] = false;
}
check[j] = false;
}
check[i] = false;
}
import java.io.*;
import java.util.*;
class Main {
static List<Integer> input = new ArrayList<>(); // 입력받은 수의 집합
static int n, r;
static int count;
static int[] data; // 순열을 구성할 데이터
static boolean[] check;
// index번째 원소를 뽑아 순열을 구성한다.
static void permutation(int index) {
// index가 r에 다다르면 원하는 행위를 한다.
if (index == r) {
// 순열 출력
count++;
System.out.println("#" + count + " " + Arrays.toString(data));
return;
}
// i 번째 원소를 뽑는다.
for (int i = 0; i < n; ++i) {
// 이미 뽑힌 원소는 뽑지 않는다.
if (check[i]) continue;
check[i] = true;
data[index] = input.get(i);
permutation(index + 1);
// 다음 원소 뽑을 준비
check[i] = false;
}
}
public static void main(String[] args) throws Exception {
System.out.println("nPr 출력 프로그램");
// 수열 입력
System.out.print("수열 입력: ");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
while (st.hasMoreTokens()) {
input.add(Integer.parseInt(st.nextToken()));
}
// r 입력 및 변수 초기화
System.out.print("r 입력: ");
r = Integer.parseInt(br.readLine());
n = input.size();
check = new boolean[n];
data = new int[r];
permutation(0);
}
}
순열의 기본적인 형태를 잘 작성할 수 있게 되었다면, 몇가지 변형을 해보며 다양한 상황에 응용 가능하도록 연습해보면 좋겠죠?
위 코드에서 boolean[] check를 통해 중복체크했던 부분을 java에서 성능 최적화를 위해 자주 사용되는 기법인 비트마스킹으로 변형해보겠습니다.
(참고: 순열 코드에서 중복체크를 비트마스킹으로 하는 것 만으로 큰 성능차이는 발생하지 않습니다. 오히려 가독성 저하 및 디버깅 난이도 상승을 불러올 수 있으므로 상황에 맞게 사용해야합니다.)
import java.io.*;
import java.util.*;
class Main {
static List<Integer> input = new ArrayList<>(); // 입력받은 수의 집합
static int n, r;
static int count;
static int[] data; // 순열을 구성할 데이터
// index번째 원소를 뽑아 순열을 구성한다.
static void permutation(int index, int mask) {
// index가 r에 다다르면 원하는 행위를 한다.
if (index == r) {
// 순열 출력
count++;
System.out.println("#" + count + " " + Arrays.toString(data));
return;
}
// i 번째 원소를 뽑는다.
for (int i = 0; i < n; ++i) {
// 이미 뽑힌 원소는 뽑지 않는다.
if ((mask & 1 << i) != 0) continue;
data[index] = input.get(i);
permutation(index + 1, mask | 1 << i);
}
}
public static void main(String[] args) throws Exception {
System.out.println("nPr 출력 프로그램");
// 수열 입력
System.out.print("수열 입력: ");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
while (st.hasMoreTokens()) {
input.add(Integer.parseInt(st.nextToken()));
}
// r 입력 및 변수 초기화
System.out.print("r 입력: ");
r = Integer.parseInt(br.readLine());
n = input.size();
data = new int[r];
permutation(0, 0);
}
}
from itertools import permutations
nums = [1, 2, 3, 4, 5]
# permutations(nums, 3) : nums 리스트에서 3개를 뽑아 순열을 구성
for p in permutations(nums, 3):
print(p)