요소를 반복하지 않고 한 쌍의 세트에서 조합 생성


28

한 쌍의 세트가 있습니다. 각 쌍은 x, y가 범위의 정수에 속하도록 (x, y) 형식 [0,n)입니다.

따라서 n이 4이면 다음 쌍이 있습니다.

(0,1) (0,2) (0,3)
(1,2) (1,3) 
(2,3) 

이미 쌍이 있습니다. 이제 n/2정수를 반복하지 않도록 쌍을 사용하여 조합을 만들어야합니다 (즉, 각 정수는 적어도 마지막 조합에서 한 번 이상 나타납니다). 다음은 더 나은 이해를위한 올바른 조합과 잘못된 조합의 예입니다.

 1. (0,1)(1,2) [Invalid as 3 does not occur anywhere]
 2. (0,2)(1,3) [Correct]
 3. (1,3)(0,2) [Same as 2]

누군가가 쌍을 갖게되면 가능한 모든 조합을 생성하는 방법을 제안 할 수 있습니까?


아마도 2D 배열을 사용하여 쌍을 나타냅니다. 유효한 조합은 각 행과 열에 정확히 1 개의 선택된 셀이 포함되도록 n 개의 배열 셀 선택에 해당합니다.
Joe

4
입력이 모든 쌍 의 집합이라고 말하고 있습니까? 그렇다면 입력이 단순히 이라고 말하면 됩니다. n
rgrig

2
되어 에도 항상? 그렇지 않다면, "정수 중 어느 것도 반복되지 않는다"는 말과 "각 정수는 최종 조합에서 적어도 한 번 나타난다"는 모순이다. n
Dmytro Korduban

1
@rgrig와 같은 문제 : 입력이 모두 정렬되지 않은 쌍입니까 아니면 가능한 쌍의 임의 세트입니까? 그것이 모두 쌍이라면 입력이 이라고 말할 수 있으며 목록을 제공 할 필요가 없습니다. n
Kaveh

1
초기 쌍 세트로 정의 된 점 에서 그래프의 모든 완벽한 일치 를 생성하는 데 관심이 있습니다. 또한 그 그래프를 그 지점의 완전한 그래프로 생각합니다. 당신이 그것을 언급한다면 당신의 질문은 더 명확해질 것입니다. 거기 등 matchings. ( n - 1 ) ! ! : = 1 × 3 × 5 × × ( n - 1 )n(n1)!!:=1×3×5××(n1)
마크 반 리웬

답변:


14

직접적인 방법 중 하나는 각 호출에서 다음을 수행하는 재귀 프로 시저입니다. 프로 시저에 대한 입력은 이미 선택된 쌍의 목록과 모든 쌍의 목록입니다.

  1. 입력 목록에서 아직 다루지 않은 가장 작은 숫자를 계산하십시오. 첫 번째 호출의 경우 페어가 선택되지 않았으므로 0이됩니다.
  2. 모든 숫자가 적용되는 경우 올바른 조합을 갖고 인쇄 한 후 이전 단계로 돌아갑니다. 그렇지 않으면 발견되지 않은 가장 작은 숫자가 목표입니다.
  3. 대상 번호를 포함하는 방법을 찾기 위해 쌍을 검색하십시오. 없는 경우 이전 수준의 재귀로 돌아갑니다.
  4. 대상 번호를 처리 할 수있는 방법이있는 경우 첫 번째 방법을 선택하고 방금 선택한 쌍을 선택한 쌍 목록에 추가하여 전체 프로 시저를 재귀 적으로 다시 호출하십시오.
  5. 다시 돌아 오면 이전에 선택한 쌍과 겹치지 않고 대상 번호를 한 쌍으로 덮는 다음 방법을 찾으십시오 . 하나를 찾으면 선택하고 다음 절차를 재귀 적으로 호출하십시오.
  6. 대상 번호를 더 이상 덮을 방법이 없을 때까지 4 단계와 5 단계를 계속하십시오. 전체 쌍 목록을 살펴보십시오. 더 이상 올바른 선택이 없으면 이전 수준의 재귀로 돌아갑니다.

이 알고리즘을 시각화하는 방법은 경로가 겹치지 않는 쌍의 트리 인 트리를 사용하는 것입니다. 트리의 첫 번째 레벨에는 0을 포함하는 모든 쌍이 포함됩니다. 위의 예에서 트리는

           뿌리
             |
     ----------------
     | | |
   (0,1) (0,2) (0,3)
     | | |
   (2,3) (1,3) (1,2)

이 예에서 트리를 통과하는 모든 경로는 실제로 올바른 모음을 제공하지만, 예를 들어 (1,2) 쌍을 제외하면 가장 오른쪽 경로에는 하나의 노드 만 있고 3 단계의 검색에 해당합니다.

이 유형의 검색 알고리즘은 특정 유형의 모든 객체를 열거하는 많은 유사한 문제에 대해 개발 될 수 있습니다.


아마도 OP 는 질문이 말한 것처럼 단지 쌍이 아니라 모든 쌍이 입력에 있음을 의미한다고 제안했습니다 . 이 경우 더 이상 허용되는 쌍을 확인할 필요가 없으므로 알고리즘이 훨씬 더 쉽습니다. 모든 쌍의 세트를 생성 할 필요조차 없습니다. 다음 의사 코드는 OP가 요청한 것을 수행합니다. 여기서 은 입력 번호이고, "list"는 빈 목록으로 시작하고 "covered"는 0으로 초기화 된 길이 의 배열입니다. 좀 더 효율적으로 만들 수는 있지만 바로 나의 목표는 아닙니다.nnn

sub cover {
  i = 0;
  while ( (i < n) && (covered[i] == 1 )) {
   i++;
  }
  if ( i == n ) { print list; return;}
  covered[i] = 1;
  for ( j = 0; j < n; j++ ) {
    if ( covered[j] == 0 ) {
      covered[j] = 1;
      push list, [i,j];
      cover();
      pop list;
      covered[j] = 0;
    }
  }
  covered[i] = 0;
}

이것은 작동해야하지만 가장 효율적인 방법은 아닙니다.
Joe

2
결국 요점은 어떻게 든 그 나무의 경로를 열거하는 것입니다. 입력 목록의 쌍 수가 가능한 쌍 수보다 훨씬 작은 경우, 특히 이런 유형의 알고리즘은 특히 일부 해시 테이블을 사용하여 각 단계에서 이미 적용된 숫자를 기억하는 데 도움이되는 경우에 매우 효율적입니다. 일정한 시간에 쿼리 할 수 ​​있습니다.
Carl Mummert

목록에서 포인터를 사용하면 Knuth의 댄싱 링크 를 살펴볼 가치가 있습니다. 재귀 호출 양식을 반환하고 목록의 이전 상태를 복원해야 할 때.
uli

10

반복적으로 해결할 수 있습니다. 범위에 대한 모든 솔루션 이 있다고 가정합니다 . 그러면 에서 솔루션 를 쉽게 구성 할 수 있습니다 . 크기는 매우 빠르게 커지 므로 모든 세트를 메모리에 보관하는 대신 생성기를 작성하는 것이 좋습니다. 아래 Python 예제를 참조하십시오. [ 0 , N ) S N + 2 S , N , NSn[0,n)Sn+2Snn

def pairs(n):
    if (n%2==1 or n<2):
        print("no solution")
        return
    if (n==2):
        yield(  [[0,1]]  )
    else:
        Sn_2 = pairs(n-2) 
        for s in Sn_2:
            yield( s + [[n-2,n-1]] )
            for i in range(n/2-1):
                sn = list(s)
                sn.remove(s[i])
                yield( sn + [ [s[i][0], n-2] , [s[i][1], n-1] ] )
                yield( sn + [ [s[i][1], n-2] , [s[i][0], n-1] ] )

당신은 전화하여 모든 쌍을 나열 할 수 있습니다

for x in pairs(6):
   print(x)

6

업데이트 : 이전 답변은 OP가 요구하지 않은 이분 그래프를 처리했습니다. 관련 정보로 지금 남겨두고 있습니다. 그러나 더 적절한 정보는 이분자가 아닌 그래프의 완벽한 일치와 관련이 있습니다.

이와 관련 하여 Propp의 진행 상황 에 대한 훌륭한 설문 조사가 있습니다 (1999 년까지). 이 기사의 일부 아이디어와 관련 링크가 유용 할 수 있습니다. TL; DR은-까다 롭습니다 :)

--- 옛날 답변 시작

당신이 요구하는 것은 이분 그래프에서 가능한 모든 완벽한 일치를 열거한다는 것입니다. 이를 수행하기위한 여러 가지 알고리즘이 있으며, 특히 최신 알고리즘 중 하나는 ISAAC 2001 에서 온 것입니다 .

기본 개념은 네트워크 흐름을 사용하여 하나의 완벽한 일치를 찾은 다음 교번 사이클을 사용하여이를 반복해서 수정하는 것입니다 (자세한 내용은 네트워크 흐름에 대한 알고리즘 교과서 장 참조).


이분 그래프는 주어진 레이블 [0, n]을 가진 두 세트로 구성되며, 만약 (i! = j) 경우에만 모서리 (i, j)가 있습니다
Joe

나는 당신이 쌍을 위해 노드를 넣을 필요가 없다고 생각합니다. 우리는 그것들을 가장자리로 취급 할 수 있습니다. 다시 말해 우리는 개의 정점 에 대한 완전한 그래프를 가지고 있으며 모든 정점 커버링을 생성하려고합니다. 그래서 문제는 의 영원을 계산하는 것 같습니다 . K nnKn
Kaveh

2
퍼머넌트가 답을 계산합니다. 그러나 OP는 그들을 열거하고 싶어
Suresh

그래프 구조로 인해 모두 동형이되므로 순열 적용에 대해 생각하는 것이 좋습니다.
Kaveh

4

선택한 모든 쌍은 더 이상 선택할 수없는 두 개의 행을 제거합니다. 이 아이디어는 스칼라에서 재귀 알고리즘을 설정하는 데 사용할 수 있습니다.

def combine(pairs : Seq[(Int,Int)]) : Seq[Seq[(Int, Int)]] = pairs match {
  case Seq() => Seq()
  case Seq(p) => Seq(Seq(p))
  case _ => {
    val combinations = pairs map { case (a,b) => {
      val others = combine(pairs filter { case (c,d) =>
        a != c && a != d && b != c && b != d
      })

      others map { s => ((a,b) +: s) }
    }}

    combinations.flatten map { _.sorted } distinct
  }
}

이것은보다 효율적인 방식으로 표현 될 수 있습니다. 특히 조합에 대해 전체 행을 고려하지 않아도된다는 아이디어는에 대한 호출에서 사용되지 않습니다 filter.


이것은 모든 숫자를 포함하지는 않지만 원래 시퀀스에 확장 할 수있는 쌍이 없기 때문에 확장 할 수없는 조합을 반환하지 않습니까? 그렇다면 해당 조합을 필터링해야합니다.
Carl Mummert

OP는 중복없이 충분히 큰 세트가있는 경우에만 모든 숫자를 얻을 것을 제안했습니다. 의 설정에서는 모든 입력으로 쌍 (정렬)을 마찬가지이며,이 내 알고리즘 (의심) 올바른 설정하는 것이다. n2N
Raphael

스칼라를 읽을 수 없지만 이것이 내가 우려하는 문제입니다. 입력에 및 한 쌍만 있다고 가정합니다 . 그런 다음 전체 세트를 커버 할 수 없으므로 출력이 전혀 없어야합니다. 이 경우이 알고리즘이 여전히 무언가를 반환합니까? n = 4(0,1)n=4
Carl Mummert

예. 그러나 내가 말했듯이, 내 대답은 OP가 제안하는 시나리오, 즉 임의의 입력이 아닌 것을 처리합니다.
Raphael

원래 질문을 읽으면서 임의의 쌍 세트에 관한 것이지만 OP는 모든 쌍이 가능하다고 말하지 않습니다. 그러나 나는 OP가 그것에 대해 더 명확 할 수 있다는 데 동의합니다.
Carl Mummert

4

이 질문에 대해 이미 많은 사랑스러운 답변이 있지만 기본적이고 일반적인 트릭을 지적하는 것이 좋을 것이라고 생각합니다.

결합 할 요소의 전체 순서를 가질 수있는 경우 고유 한 조합을 생성하는 것이 훨씬 쉽습니다 . 이런 식으로 정렬 조합 만 허용하면 고유성이 보장됩니다. 정렬 된 조합을 생성하는 것은 어렵지 않습니다. 일반적인 무차별 대입 열거 검색 만 수행하지만 각 단계에서 각 단계에서 이미 선택한 것보다 큰 요소 만 선택합니다.

이 특정 문제의 추가적인 합병증은 길이 n / 2 (최대 길이)의 조합 만 얻으려는 욕구입니다. 우리가 좋은 분류 전략을 결정한다면 이것은 어려운 일이 아닙니다. 예를 들어 Carl Mummet의 답변에서 지적했듯이 사전 편찬을 고려하면 (질문의 다이어그램에서 하향식, 왼쪽-오른쪽) 항상 첫 번째 숫자가 다음 요소가되도록 다음 요소를 취하는 전략을 도출합니다. 여전히 사용되지 않은 가장 작은 숫자입니다.

다른 길이의 시퀀스를 생성하려는 경우에도이 전략을 확장 할 수 있습니다. 첫 번째 숫자가 사용 가능한 가장 작은 요소가 아닌 다음 요소를 선택할 때마다 하나 이상의 요소 행이 정렬 된 하위 시퀀스에 나타나지 않도록 배정되므로 사전 치환의 최대 길이는 그에 따라 줄어 듭니다.


3

이것이 당신이 요구하는 것인지 확실하지 않지만 내가 이해하는 것처럼, 당신은 모든 정렬되지 않은 쌍을 가지고 있고 모든 쌍의 목록을 세고 싶습니다. 집합 여기서 은 짝수입니다. 우리는 다음과 같이 생각할 수 있습니다 에지 코팅 의 에 전체 그래프 정점.(n2)[n]={1,,n}[n]nKnn

또한 각 숫자 가 목록에 한 번만 나타나는 것으로 가정 합니다. 어떤 경우에, 우리는 완벽하게 일치하는 덮개만을보고 있습니다. 그래프에서 일치하는 수 는 인접 행렬영구성 과 같습니다 . 따라서 을 계산해야합니다 .[n]Perm(Kn)

영구는 인 것으로 알려져 있지만, 이는 일반적인 경우입니다. 들어 있다 같은 목록. #P-complete Knn!2n2

이 모든 것을 생성하는 가장 쉬운 방법은 하나의 완벽한 일치를 수정 한 다음 순열을 적용하는 것입니다.[n]

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.