중복없는 조합을 만들어내는 프로그램의 알고리즘....

moonrepeat의 이미지

현재 중복없는 조합을 만들어 내는 프로그램을 만들고 싶은데 어떤 식으로 만들어
야 할지 난감해서 조언 부탁드립니다.

구현하고자 하는 프로그램은 n 이라는 값을 입력받으면 1~n 까지 정수를 이용해
중복이 없는 모든 조합을 만들어 내는 프로그램입니다.

예를 들면 4라는 숫자를 입력하면
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
.
.
.
4 3 2 1

이런식의 조합을 출력하는 프로그램을 만들려고 합니다.

걍 전부 재귀호출로 조합을 만들면서 중복되면 break 하는 식으로
할려고 했는데 속도가 우선시 되는 프로그램이라 조금 효율적인 방법
이 혹시 있을까 해서 질문을 올립니다.

(추가 질문 : 이런 프로그램을 자바로 짜도 될까요?)

jiee의 이미지

#include <algorithm>
#include <vector>
#include <iostream>
 
using namespace std;
 
vector<int> vec;
 
void PrintVec()
{
     for (int i = 0; i < vec.size(); i++)
         cout << vec[i];
     cout << endl;
}
 
int main()
{
     int num; cin >> num;
     for (int n = 1; n <= num; n++) 
         vec.push_back(n);
 
     PrintVec();
     while (next_permutation(vec.begin(), vec.end()))
         PrintVec();
 
     return 0;
}

STL의 알고리즘 쓰면, 이렇게 구현하면 될 것 같습니다.-_-a
그리고, 직접구하는 건 예전에 백트래킹으로 구했던 것 같습니다.
(기억이 가물해서..)

토나오게...

esrevinu의 이미지

Haskell로 짜보다가 포기 :(

인터넷에 찾아보니 이렇게 하면 되네요.
( http://www.polyomino.f2s.com/david/haskell/codeindex.html )

import List
 
permutations n = permutationsOf [1..n]
 
permutationsOf [] = [[]]
permutationsOf xs = [x:xs' | x <- xs, xs' <- permutationsOf (delete x xs)]

"1부터 n까지 수 중에서 하나를 뽑아서 앞에 두고 나머지를 가지고 permutation한 것들을 뒤에 붙인다. 이것을 모든 1부터 n까지의 수에 대해서 한다."라는 알고리즘이네요.

winner의 이미지

만일 순서가 꼭 나열한 방식이 아니어도 상관없다면
Horowitz 와 Sahni 의 Data Structure 에 나옵니다.

물론 Java 로 짜도 상관없죠.

음... 같은 숫자가 없다면 나열하신 순서로 나오는 다음의 code 는 ICPC 준비하면서 작성한 것이긴 한데...
어찌되었든 재귀호출은 필요합니다.
Bug 가 없음을 보장하지는 못합니다.

#define MAX_NUM 10
 
unsigned int level = 0, number, N[MAX_NUM];
 
 
void perm(void)
{
    unsigned int tmp, i;
 
    for (i = level; i < number; ++i) {
        tmp = N[level];
        N[level] = N[i];
        N[i] = tmp;
 
        if (++level < number-1)
            perm();
        else
            출력;
 
        --level;
    }
 
    tmp = N[level];
    for (i = level; i < number-1; ++i)
        N[i] = N[i+1];
    N[number-1] = tmp;
}

N 배열과 number 의 초기화는 빠져있습니다.
number 가 조금만 커져도 출력은 끝이 안 날 수 있으므로 조심하세요.

moonrepeat의 이미지

답변 감사합니다.
완성시키면 다시 답급 달겠습니다.

------------------------

삽질은 계속되어야 한다....... 쭉.........

삽질은 계속되어야 한다....... 쭉.........

ironiris의 이미지

1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
처럼 하나의 숫자로 시작하는 것을 만드는 것은
이미 만드신 알고리즘으로 처리하시구요.
나머진 시간 절약을 위해서 이 데이터를 한칸씩 쉬프트해줍니다.
2 3 4 1
2 4 3 1
3 2 4 1
3 4 2 1
4 2 3 1
4 3 2 1
이렇게...
쉬프트를 n-1번 해주면 되겠네요. ^^;; 속도도 빠르고..

버그가 있으려나요? --;;

esrevinu의 이미지

제대로 못 만든 건지 속도가 안 빨라지네요.

module Main where
 
import List
import IO
 
permutations n = permutationsOf [1..n]
permutations' n = [rotation k y | k <- [0..(n-1)],
                                  y <- (map (1:) (permutationsOf [2..n]))]
permutations'' n = foldl (++) [] (take n perms)
          where
            perms = first:[rotations a | a <- perms]
            first = map (1:) (permutationsOf [2..n])
            rotations a = [rotation 1 y | y <- a]
 
permutationsOf [] = [[]]
permutationsOf xs = [x:xs' | x <- xs, xs' <- permutationsOf (delete x xs)]
 
rotation 0 ys = ys
rotation k (y:ys) = rotation (k-1) (ys ++ [y])
 
main = do
  n <- getLine
  doShowList (permutations'' (read n))
 
doShowList [] = return ()
doShowList (x:xs) = do
                      putStrLn (show x)
                      doShowList xs

세가지 버젼의 permutation 함수가 있어요. 그냥 permutation, rotation 무식하게 적용한 permutation', rotation 머리 쓴다고 어렵게 적용한 permutation'' 입니다.
어떻게 하면 개선할 수 있을지...

--------------

조금 빨라졌네요. permutation' 은 오히려 느려졌지만요.

Prelude Main> length (permutations 10)
3628800
(5.32 secs, 2227581272 bytes)
Prelude Main> length (permutations' 10)
3628800
(5.67 secs, 2372808272 bytes)
Prelude Main> length (permutations'' 10)
3628800
(4.20 secs, 733978852 bytes)
esrevinu의 이미지

module Main where
 
import List
import IO
 
permutations n = permutationsOf [1..n]
permutations' n = [rotation k y | k <- [0..(n-1)],
                                  y <- [1:y | y <- permutationsOf [2..n]]]
permutations'' n = foldr (++) [] (take n perms)
          where
            perms = first:[rotations a | a <- perms]
            first = [1:y | y <- permutationsOf [2..n]]
            rotations a = [rotation 1 y | y <- a]
 
permutations''' n = super_permutations 1 n
 
super_permutations m n
          | m < n = foldr (++) [] (take (n-m+1) perms)
          | m == n = [ [n] ]
          | otherwise = error "m should be smaller than n"
          where
            perms = first:[rotations a | a <- perms]
            first = [m:y | y <- super_permutations (m+1) n]
            rotations a = [rotation 1 y | y <- a]
 
permutationsOf [] = [[]]
permutationsOf xs = [x:xs' | x <- xs, xs' <- permutationsOf (delete x xs)]
 
rotation 0 ys = ys
rotation k (y:ys) = rotation (k-1) (ys ++ [y])
 
main = do
  n <- getLine
  doShowList (permutations''' (read n))
 
doShowList [] = return ()
doShowList (x:xs) = do
                      putStrLn (show x)
                      doShowList xs

하나 아래 단계의 permutation의 rotation을 한 것이 아니라 처음부터 rotation을 해서 permutation을 구하는 permutations'''를 만들었습니다. 더 빨라졌네요.
처음 이 코드를 인터프리터에서 돌렸을 때 permutation, permutation' 함수에서는 시간이 엄청나게 걸리더군요. 그런데 컴파일하고 인터프리터에서 돌리니까 전과 같은 속도가 나네요. binary code를 인터프리터가 읽어 오는 것 같네요. 그런데 permutations'''과 permutations''는 컴파일 전도 컴파일 후보다는 느렸지만 괜찮게 나왔는데 말이죠.

Prelude Main> length (permutations''' 10)
3628800
(2.58 secs, 243525992 bytes)
Prelude Main> length (permutations'' 10)
3628800
(4.36 secs, 428327028 bytes)
Prelude Main> length (permutations' 10)
3628800
(5.44 secs, 2372800716 bytes)
Prelude Main> length (permutations 10)
3628800
(5.10 secs, 2227579804 bytes)
winner의 이미지

esrevinu 씨의 글을 보니 제 code 도 list 를 사용하는 것이 회전시킬 때 좋겠네요.
(그러고 보면 STL 의 list 의 splice 도 회전 algorithm 으로 쓸 수 있겠네요.)

Haskell code 의 algorithm 을 곰곰히 생각해보니 정말 멋진 algorithm 입니다.

계속 배열과 vector 만 가지고 놀았더니 유연한 사고가 안 나오는군요.
뭐랄까 사람이 기본적으로 permutation 하는 방식과 유사하다고 할까요?

배열가지고 특히 추가적인 memory 를 되도록 사용하지 않는 것에만 익숙해진 저에게는 신선한 충격입니다.
Horowitz 와 Sahni 의 책에 나온 permutation algorithm 을 보고 이해하는데 난감하면서
그 미묘한 trick 적인 느낌에 충격을 받았는데 이것은 더한 충격입니다.

저는 list 를 사용하는 좋은 algorithm 이란 것을 찾기가 어려웠었습니다..
오늘 하나 추가입니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.