하노이 타워 정렬


21

하노이 타워 스타일 의 정수 목록을 정렬하는 함수 / 서브 루틴을 작성하십시오 .

정수 스택 이 제공됩니다 . 이것이 주요 스택입니다.

또한 두 개의 도우미 스택이 추가로 제공됩니다. 이 도우미 스택에는 고유 속성이 있습니다. 모든 요소는 그 아래의 요소보다 작거나 같은 크기 여야합니다. 메인 스택에는 그러한 제한이 없습니다.

가장 큰 정수를 아래에 배치하여 기본 스택을 정렬해야합니다. 함수 / 서브 루틴은 스택 정렬시 이동 횟수를 반환합니다.
참고 : 메인 스택 을 제자리에 정렬해야하며 다른 스택에 정렬하지 않고 응답을 호출해야합니다. 그러나 어떤 이유로 든 그렇게 할 수없는 경우 가변 스택을 시뮬레이션 할 수 있지만 하노이 타워 정렬이라는 것을 기억하십시오. 페그는 3 개 뿐이며 페그는 1 개만 정렬되지 않을 수 있습니다.

함수 / 서브 루틴은 언제든지 스택을 검사 할 수 있지만, 팝하고 밀기 만하면 이동할 수 있습니다. 한 번의 이동은 한 스택에서 다른 스택으로 푸시되는 팝입니다.

처음 6 개의 자연수의 각 순열에 대해 함수 / 서브 루틴을 테스트하십시오. 다시 말해, 함수 / 서브 루틴을 테스트하십시오 {1},{2},...,{6},{1,1},{1,2},...,{1,6},{2,1},...(이것은 총계 이거나 가능성 이 있어야합니다 (수학을 교정 한 Howard에게 감사합니다)). 최소 횟수만큼 요소를 이동시키는 기능 / 서브 루틴이 이깁니다.61+62+...+6655986


@ JanDvorak 그것은 테스트에 대한 일종의 아이디어였습니다. 프로그래머가 46656 번 함수를 실행해야한다면 왜 출력을 너무 오래 기다리기를 원할까요? 아니면 이런 종류의 일을 제한하는 또 다른 좋은 방법이 있습니까?
저스틴

어쨌든 나는 블라인드 challenge 도전을 좋아한다. "스택 x에서 스택 y로 이동"이라고 말하고 이동이 성공했는지 확인하고, 그렇게한다면 비용이 청구된다. 보너스 포인트는 이동 실패가 예외를 던지거나 올바르게 반환함으로써 표시됩니다.
John Dvorak

3
제공 한 "순열"목록에는 6**1+6**2+...+6**6=55986요소 가 포함되어 있습니다.
Howard

1
@ m.buettner 구별은 이것이 직교 곱 요소 인 1 ~ 6 배라는 것 입니다. 아마도 이것을 " 널 설정을 제외한 첫 6 개의 자연수 의 거듭 제곱 의 각 집합의 순열 집합"이라고 부를 것입니다 .
Justin

1
전원 세트를 제외한 @Quincunx는 반복되는 숫자 세트를 포함하지 않습니다. ;) ...하지만 우리가 세트의 요소에 대해 분명한 한, 이것을 심각하게 생각해서는 안됩니다.
Martin Ender

답변:


4

자바-최적의 솔루션 (1080544 이동)

이 솔루션은 대상에서 최단 경로 트리를 구축 한 다음 초기 상태에서 대상으로 경로를 이동합니다. 속도 향상을위한 충분한 공간이 있지만, 약 1 분 안에 여전히 55986 문제를 모두 완료합니다.

알고리즘이 올바르게 구현되었다고 가정하면 이론적으로 최상의 솔루션이어야합니다.

import java.util.*;

public class HanoiSort {

    public static void main(String[] args) {
        int sumNumMoves = 0;
        for (int size = 1; size <= 6; ++size) {
            Collection<List<Integer>> initMainStacks = generateInitMainStacks(Collections.<Integer>emptyList(), size);
            for (List<Integer> initMainStack : initMainStacks) {
                sumNumMoves += solve(initMainStack);
            }
        }
        System.out.println(sumNumMoves);
    }

    /*
     * Recursively create initial main stacks
     */
    private static Collection<List<Integer>> generateInitMainStacks(List<Integer> mainStack, int remainingSize) {
        Collection<List<Integer>> initMainStacks;
        if (remainingSize > 0) {
            initMainStacks = new ArrayList<>();
            for (int number = 1; number <= 6; ++number) {
                List<Integer> nextMainStack = new ArrayList<>(mainStack);
                nextMainStack.add(number);
                initMainStacks.addAll(generateInitMainStacks(nextMainStack, remainingSize - 1));
            }
        } else {
            List<Integer> initMainStack = new ArrayList<>(mainStack);
            initMainStacks = Collections.singleton(initMainStack);
        }
        return initMainStacks;
    }

    private static final List<Integer> EMPTY_STACK = Collections.emptyList();

    /*
     * Create a shortest path tree, starting from the target state (sorted main stack). Break when the initial state
     * is found, since there can be no shorter path. This is akin to building a chess endgame tablebase.
     *
     * Traverse the path from initial state to the target state to count the number of moves.
     */
    private static int solve(List<Integer> initMainStack) {
        List<List<Integer>> initState = Arrays.asList(new ArrayList<>(initMainStack), EMPTY_STACK, EMPTY_STACK);
        List<Integer> targetMainStack = new ArrayList<>(initMainStack);
        Collections.sort(targetMainStack);
        List<List<Integer>> targetState = Arrays.asList(new ArrayList<>(targetMainStack), EMPTY_STACK, EMPTY_STACK);
        Map<List<List<Integer>>,List<List<Integer>>> tablebase = new HashMap<>();
        Deque<List<List<Integer>>> workQueue = new ArrayDeque<>();
        tablebase.put(targetState, null);
        workQueue.add(targetState);
        while (!tablebase.containsKey(initState)) {
            assert !workQueue.isEmpty() : initState.toString();
            List<List<Integer>> state = workQueue.removeFirst();
            Collection<List<List<Integer>>> prevStates = calcPrevStates(state);
            for (List<List<Integer>> prevState : prevStates) {
                if (!tablebase.containsKey(prevState)) {
                    tablebase.put(prevState, state);
                    workQueue.add(prevState);
                }
            }
        }

        int numMoves = 0;
        List<List<Integer>> state = tablebase.get(initState);
        while (state != null) {
            ++numMoves;
            state = tablebase.get(state);
        }
        return numMoves;
    }

    /*
     * Given a state, calculate all possible previous states
     */
    private static Collection<List<List<Integer>>> calcPrevStates(List<List<Integer>> state) {
        Collection<List<List<Integer>>> prevStates = new ArrayList<>();
        for (int fromStackNo = 0; fromStackNo < 3; ++fromStackNo) {
            List<Integer> fromStack = state.get(fromStackNo);
            if (!fromStack.isEmpty()) {
                int number = fromStack.get(0);
                for (int toStackNo = 0; toStackNo < 3; ++toStackNo) {
                    if (toStackNo != fromStackNo) {
                        List<Integer> toStack = state.get(toStackNo);
                        if ((toStackNo == 0) || toStack.isEmpty() || (toStack.get(0) >= number)) {
                            List<List<Integer>> prevState = new ArrayList<>(state);
                            List<Integer> prevFromStack = new ArrayList<>(fromStack);
                            prevFromStack.remove(0);
                            prevState.set(fromStackNo, prevFromStack);
                            List<Integer> prevToStack = new ArrayList<>(toStack);
                            prevToStack.add(0, number);
                            prevState.set(toStackNo, prevToStack);
                            prevStates.add(prevState);
                        }
                    }
                }
            }
        }
        return prevStates;
    }

}

"가장 짧은 경로 트리"의 의미에 대해 더 자세히 설명하고 싶으십니까?
justhalf 2016 년

어쨌든 귀하의 답변에 감사드립니다. 휴리스틱 만 사용하여 거의 최적의 솔루션을 얻을 수있게되어 기쁩니다. :)
justhalf

최단 경로 트리는 각 노드 / 상태에 하나의 "다음"에지가 있으며 루트 / 대상 상태 (= 정렬 된 메인 스택)까지 최단 거리에있는 노드 / 상태로 이어지는 트리입니다. 거리가 같지만 루트에 가까운 다른 후보 다음 노드가있을 수 있습니다. 이 문제의 경우 최단 경로 트리의 모든 모서리는 거리가 하나입니다. 한 상태에서 다른 상태로 이동하려면 한 번의 이동이 필요하기 때문입니다. 기본적으로 완전한 최단 경로 트리에는 동일한 대상 상태를 가진 모든 상태에 대한 솔루션이 포함됩니다.
MrBackend

@justhalf 내 의견에서 당신을 언급하는 것을 잊었다. BTW, en.wikipedia.org/wiki/Retrograde_analysis
MrBackend

5

55986 입력의 경우 C-2547172

여기에는 개선의 여지가 많이 있습니다. 내 정신을 위해 각 스택의 최상위 요소 만 검사 할 수 있도록 이것을 단순화했습니다. 이 자체 부과 제한을 해제하면 최종 주문을 미리 결정하고이를 달성하는 데 필요한 이동 수를 최소화하는 등의 최적화가 가능합니다. 매력적인 예는 메인 스택이 이미 정렬 된 경우 구현시 최악의 동작을 수행한다는 것입니다.

연산:

  1. 보조 스택을 모두 채 웁니다 (여기서 최적화를위한 공간, 어떤 종류의 피벗을 기반으로 어떤 스택에 할당 가능).
  2. 보조 스택을 기본 스택으로 다시 병합합니다.
  3. 메인 스택이 정렬 될 때까지 (그러나 반대로) 1-2를 반복하십시오.
  4. 메인 스택을 뒤집습니다 (최적화를위한 더 많은 공간, 같은 요소를 반복적으로 섞습니다).

분석:

  • 추가적인 공간 복잡도는 O (n) (두 개의 보조 스택의 경우)이며 이는 문제의 요구 사항이므로 좋았습니다.
  • 내 계산에 의해 시간 복잡도는 O (n ^ 2)입니다. 정정을 환영합니다.

#include <assert.h>
#include <stdio.h>

#define SIZE 6

int s0[SIZE + 1];
int s1[SIZE + 1];
int s2[SIZE + 1];

int
count(int *stack)
{
    return stack[0];
}

int
top(int *stack)
{
    return stack[stack[0]];
}

void
push(int *stack, int value)
{
    assert(count(stack) < SIZE && "stack overflow");
    assert((stack == s0 || count(stack) == 0 || value <= top(stack)) && "stack order violated");
    stack[++stack[0]] = value;
}

int
pop(int *stack)
{
    int result = stack[stack[0]];
    assert(count(stack) > 0 && "stack underflow");
    stack[stack[0]] = 0;
    stack[0]--;
    return result;
}

int permutations;

void
permute(int len, int range, void (*cb)(void))
{
    int i;
    if(len == 0)
    {
        permutations++;
        cb();
        return;
    }
    for(i = 1; i <= range; i++)
    {
        push(s0, i);
        permute(len - 1, range, cb);
        pop(s0);
    }
}

void
print(void)
{
    int i;
    for(i = 1; i <= count(s0); i++)
    {
        printf("%d ", s0[i]);
    }
    printf("\n");
}

int save[SIZE + 1];

void
copy(int *src, int *dst)
{
    int i;
    for(i = 0; i <= SIZE; i++)
    {
        dst[i] = src[i];
    }
}

int total;

void
move(int *src, int *dst)
{
    total++;
    push(dst, pop(src));
}

void
merge(void)
{
    while(1)
    {
        if(count(s1) == 0 && count(s2) == 0)
        {
            break;
        }
        else if(count(s1) == 0 || (count(s2) > 0 && top(s2) < top(s1)))
        {
            move(s2, s0);
        }
        else
        {
            move(s1, s0);
        }
    }
}

void
reverse(void)
{
    while(1)
    {
        while(count(s2) == 0 || top(s0) == top(s2))
        {
            move(s0, s2);
        }
        if(count(s0) == 0 || top(s2) < top(s0))
        {
            while(count(s2) > 0)
            {
                move(s2, s0);
            }
            break;
        }
        while(count(s0) > 0 && (count(s1) == 0 || top(s0) <= top(s1)))
        {
            move(s0, s1);
        }
        while(count(s2) > 0)
        {
            move(s2, s0);
        }
        merge();
    }
}

void
sort(void)
{
    while(1)
    {
        if(count(s0) == 0)
        {
            merge();
            reverse();
            break;
        }
        else if(count(s1) == 0 || top(s1) >= top(s0))
        {
            move(s0, s1);
        }
        else if(count(s2) == 0 || top(s2) >= top(s0))
        {
            move(s0, s2);
        }
        else
        {
            merge();
        }
    }
}

void
helper(void)
{
    copy(s0, save);
    sort();
    copy(save, s0);
}

int
main(void)
{
    permute(1, 6, helper);
    permute(2, 6, helper);
    permute(3, 6, helper);
    permute(4, 6, helper);
    permute(5, 6, helper);
    permute(6, 6, helper);
    printf("%d\n", permutations);
    printf("%d\n", total);
    return 0;
}

4

파이썬, 3983838 3912258이 55986 개 이상의 입력으로 이동

이것은 매우 비효율적입니다.

OP가 모든 경우에 해당하는지 또는 다른 특정 경우에 대해 명확하게 지정한 후 총 이동 수를 추가하겠습니다.


from itertools import product       # Required for testing
import time                         # Required if you want to see it in action.

from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

def main( l ):
    ################### Data ###################
    global a , b , c , d , copy , total_moves
    total_moves = 0

    a = [ x for x in l ]  # Input stack, order doesn't matter, we'll try to empty this one
    b = []                # Usable stack, restricted by order, we'll try to get the final sorted order here
    c = []                # Usable stack, restricted by order, but we'll try to keep it as empty as possible

    d = { 'a':a , 'b':b , 'c':c }  # Passing the stacks to the nested functions by their names as a string
    copy = [ x for x in a ]        # reference copy, read-only


    ################### Functions ###################
    def is_correct( stack ):
        if d[ stack ] == sorted( copy , reverse = True ):
            return True
        else:
            return False

    def reverse_exchange( source , destination , keep = 0 ):
        #
        # keep is the number of elements to keep at the bottom of the source stack
        # The remaining top elements are moved to the destination stack
        # We first move the top elements to stack a
        # and from a to c so that the order is preserved
        # effectively splitting the source stack into two
        #

        i = 0
        while len( d[ source ] ) > keep :
            move( source , 'a' )
            i += 1
        else:
            while i > 0:
                move( 'a' , destination )
                i -= 1

    # def validate( source , destination ):
    #   # 
    #   # Validates the give move
    #   #
    #   if len( d[ source ] ) == 0:
    #       return False
    #   if destination == 'a' or len( d[ destination ] ) == 0:
    #       return True
    #   else:
    #       if d[ destination ][ len( d[ destination ] ) - 1 ] >= d[ source ][ len( d[ source ] ) - 1 ]:
    #           return True
    #       else:
    #           return False

    def move( source , destination ):
        global total_moves
        # if validate( source , destination ):
        d[ destination ].append( d[ source ].pop() )

        total_moves += 1

            # Uncomment the following to view the workings step-by-step
            # print '\n'
            # print a
            # print b
            # print c
            # print '\n'
            # time.sleep(0.1)

        return True
        # else:
        #   return False


    ################### Actual logic ###################
    while ( not is_correct( 'a' ) ):

        copy_b   = [x for x in b ]                         # Checking where the topmost element of a
        top_of_a = a[ len(a) - 1 ]                         # should be inserted
        copy_b.append( top_of_a )                          #
        sorted_copy_b = sorted( copy_b , reverse = True )  #

        reverse_exchange( 'b' , 'c' , sorted_copy_b.index( top_of_a ) )                                                  # Sandwiching the top-most element
        move( 'a' , 'b' )                                                                                                # to proper position in b
        while (len(b) > 0 and len(c) > 0 and len(a) > 0) and (sorted (b , reverse = True)[0] <= a[len(a) - 1] <= c[0]):  #  # Optimization
            move( 'a' , 'b' )                                                                                            #  #
        reverse_exchange( 'c' , 'b' )                                                                                    # b is always sorted, c is always empty

        if is_correct( 'b' ):                     # Just moving b to a
            while ( not is_correct( 'a' ) ):      # The entire program focuses on "insertion sorting"
                reverse_exchange( 'b' , 'c' , 1 ) # elements of a onto b while keeping c empty
                move( 'b' , 'a' )                 # 
                if len(c) > 0 :                       #
                    reverse_exchange( 'c' , 'b' , 1 ) # Slightly more efficient
                    move('c' , 'a' )                  #



    return total_moves


# with PyCallGraph( output= GraphvizOutput() ):


    ################### Test cases #############
i = 0
for elements in xrange( 1 , 7 ):
    for cartesian_product in product( [ 1 , 2 , 3 , 4 , 5 , 6 ] , repeat = elements ):
        input_list = [ int( y ) for y in cartesian_product ]
        i += main(input_list)
        print i
print i

설명

뭐, 의견이 충분하지 않니?


OP 참고 사항 :이 코드 골프를 만들지 않아서 감사합니다.


[code-golf] 이외의 도전을 수행하는 더 흥미로운 방법이 있다면 그렇게해야한다고 생각합니다.
Justin

좋아, 이것은 [1,1,2]에 실패합니다. 파이썬에서 목록을 고려할 때 2로 올라갈 [2,1,1]수있는 방법이 [2,1,1].index(1)있습니까?
user80551

@Quincunx No, [2,1,1,3,5].index(1)==2대신1
user80551

어, list.index(data)항목의 인덱스 반환 data에를 list. 나는 하지 않는 인덱스 알고 data1
user80551

해킹은len(list)-(list[::-1].index(1))
user80551

2

파이썬 : 1,688,293 1,579,182 1,524,054 1,450,842 1,093,195 이동

주요 방법은 main_to_help_best 선택된 일부 요소를 기본 스택에서 도우미 스택으로 이동하는 것입니다. 여기에는 everything모든 것을 지정된 위치로 옮길 destination것인지 또는 가장 큰 값만 유지할 것인지를 정의 하는 플래그 가 있습니다.destination 다른 도우미의 나머지 부분을 있습니다.

우리가 이사하고 있다고 가정 dsthelper 사용helper 기능은 대략 다음과 같이 설명 할 수 있습니다.

  1. 가장 큰 요소의 위치 찾기
  2. 가장 큰 요소의 상단에있는 모든 것을 helper 재귀 적으로 이동
  3. 가장 큰 요소를 dst
  4. 뒤로 밀기 helper메인
  5. 가장 큰 요소가 나타날 때까지 2-4 반복 dst
  6. 에이. 경우에 everything설정되고, 반복적으로 이동 주요 요소 dst
    B를. 그렇지 않으면, 주에서 요소를 재귀 적으로helper

( sort2내 코드에서) 주 정렬 알고리즘 은 main_to_help_besteverything설정하여 호출 합니다.False 한 다음 가장 큰 요소를 다시 주로 이동 한 다음 모든 것을 도우미에서 주로 이동하여 정렬합니다.

코드에 주석으로 포함 된 추가 설명

기본적으로 내가 사용한 원리는 다음과 같습니다.

  1. 한 명의 도우미가 최대 요소를 포함하도록 유지
  2. 다른 도우미가 다른 요소를 포함하도록 유지
  3. 불필요한 움직임은 가능한 많이하지 마십시오

원칙 3은 소스가 이전 목적지 인 경우 이동을 계산하지 않고 구현됩니다 (즉, main을 help1로 이동 한 다음 help1에서 help2로 이동하려고 함). 원래 위치로 다시 이동하고 있습니다 (예 : main은 help1, help1은 main). 또한 이전 n동작이 모두 같은 정수로 이동하면 실제로 순서를 바꿀 수 있습니다n 동작의 . 따라서 우리는 또한 그 수를 활용하여 더 많은 이동 수를 줄입니다.

이것은 메인 스택의 모든 요소를 ​​알고 있기 때문에 유효하므로 나중에 요소를 뒤로 이동시킬 것으로 해석 될 수 있으므로이 이동을해서는 안됩니다.

샘플 런 (스택은 아래에서 위로 표시되므로 첫 번째 요소는 아래로)

길이 1
이동 : 0
작업 : 6
최대 : 0 ([1])
평균 : 0.000

길이 2
이동 : 60
작업 : 36
최대 : 4 ([1, 2])
평균 : 1.667

길이 3
이동 : 1030
작업 : 216
최대 : 9 ([2, 3, 1])
평균 : 4.769

길이 4
이동 : 11765
작업 : 1296
최대 : 19 ([3, 4, 2, 1])
평균 : 9.078

길이 5
이동 : 112325
작업 : 7776
최대 : 33 ([4, 5, 3, 2, 1])
평균 : 14.445

길이 6
이동 : 968015
작업 : 46656
최대 : 51 ([5, 6, 4, 3, 2, 1])
평균 : 20.748

--------------
사무용 겉옷
이동 : 1093195
작업 : 55986
평균 : 19.526

가장 큰 경우는 가장 큰 요소가 두 번째 바닥에 배치되고 나머지는 정렬되는 경우입니다. 최악의 경우 알고리즘이 O (n ^ 2)임을 알 수 있습니다.

이동의 수에 분명히 최소 n=1n=2우리가 결과에서 볼 수 있듯이, 나는이 또한 더 큰 값을 최소 믿고 n내가 그것을 증명할 수는 없지만,.

자세한 설명은 코드에 있습니다.

from itertools import product

DEBUG = False

def sort_better(main, help1, help2):
    # Offset denotes the bottom-most position which is incorrect
    offset = len(main)
    ref = list(reversed(sorted(main)))
    for idx, ref_el, real_el in zip(range(len(main)), ref, main):
        if ref_el != real_el:
            offset = idx
            break

    num_moves = 0
    # Move the largest to help1, the rest to help2
    num_moves += main_to_help_best(main, help1, help2, offset, False)
    # Move the largest back to main
    num_moves += push_to_main(help1, main)
    # Move everything (sorted in help2) back to main, keep it sorted
    num_moves += move_to_main(help2, main, help1)
    return num_moves

def main_to_help_best(main, dst, helper, offset, everything=True):
    """
    Moves everything to dst if everything is true,
    otherwise move only the largest to dst, and the rest to helper
    """
    if offset >= len(main):
        return 0
    max_el = -10**10
    max_idx = -1
    # Find the location of the top-most largest element
    for idx, el in enumerate(main[offset:]):
        if el >= max_el:
            max_idx = idx+offset
            max_el = el
    num_moves = 0
    # Loop from that position downwards
    for max_idx in range(max_idx, offset-1, -1):
        # Processing only at positions with largest element
        if main[max_idx] < max_el:
            continue
        # The number of elements above this largest element
        top_count = len(main)-max_idx-1
        # Move everything above this largest element to helper
        num_moves += main_to_help_best(main, helper, dst, max_idx+1)
        # Move the largest to dst
        num_moves += move(main, dst)
        # Move back the top elements
        num_moves += push_to_main(helper, main, top_count)
    # Here, the largest elements are in dst, the rest are in main, not sorted
    if everything:
        # Move everything to dst on top of the largest
        num_moves += main_to_help_best(main, dst, helper, offset)
    else:
        # Move everything to helper, not with the largest
        num_moves += main_to_help_best(main, helper, dst, offset)
    return num_moves

def verify(lst, moves):
    if len(moves) == 1:
        return True
    moves[1][0][:] = lst
    for src, dst, el in moves[1:]:
        move(src, dst)
    return True

def equal(*args):
    return len(set(str(arg.__init__) for arg in args))==1

def move(src, dst):
    dst.append(src.pop())
    el = dst[-1]
    if not equal(dst, sort.lst) and list(reversed(sorted(dst))) != dst:
        raise Exception('HELPER NOT SORTED: %s, %s' % (src, dst))

    cur_len = len(move.history)
    check_idx = -1
    matched = False
    prev_src, prev_dst, prev_el = move.history[check_idx]
    # As long as the element is the same as previous elements,
    # we can reorder the moves
    while el == prev_el:
        if equal(src, prev_dst) and equal(dst, prev_src):
            del(move.history[check_idx])
            matched = True
            break
        elif equal(src, prev_dst):
            move.history[check_idx][1] = dst
            matched = True
            break
        elif equal(dst, prev_src):
            move.history[check_idx][0] = src
            matched = True
            break
        check_idx -= 1
        prev_src, prev_dst, prev_el = move.history[check_idx]
    if not matched:
        move.history.append([src, dst, el])
    return len(move.history)-cur_len

def push_to_main(src, main, amount=-1):
    num_moves = 0
    if amount == -1:
        amount = len(src)
    if amount == 0:
        return 0
    for i in range(amount):
        num_moves += move(src, main)
    return num_moves

def push_to_help(main, dst, amount=-1):
    num_moves = 0
    if amount == -1:
        amount = len(main)
    if amount == 0:
        return 0
    for i in range(amount):
        num_moves += move(main, dst)
    return num_moves

def help_to_help(src, dst, main, amount=-1):
    num_moves = 0
    if amount == -1:
        amount = len(src)
    if amount == 0:
        return 0
    # Count the number of largest elements
    src_len = len(src)
    base_el = src[src_len-amount]
    base_idx = src_len-amount+1
    while base_idx < src_len and base_el == src[base_idx]:
        base_idx += 1

    # Move elements which are not the largest to main
    num_moves += push_to_main(src, main, src_len-base_idx)
    # Move the largest to destination
    num_moves += push_to_help(src, dst, base_idx+amount-src_len)
    # Move back from main
    num_moves += push_to_help(main, dst, src_len-base_idx)
    return num_moves

def move_to_main(src, main, helper, amount=-1):
    num_moves = 0
    if amount == -1:
        amount = len(src)
    if amount == 0:
        return 0
    # Count the number of largest elements
    src_len = len(src)
    base_el = src[src_len-amount]
    base_idx = src_len-amount+1
    while base_idx < src_len and base_el == src[base_idx]:
        base_idx += 1

    # Move elements which are not the largest to helper
    num_moves += help_to_help(src, helper, main, src_len-base_idx)
    # Move the largest to main
    num_moves += push_to_main(src, main, base_idx+amount-src_len)
    # Repeat for the rest of the elements now in the other helper
    num_moves += move_to_main(helper, main, src, src_len-base_idx)
    return num_moves

def main():
    num_tasks = 0
    num_moves = 0
    for n in range(1, 7):
        start_moves = num_moves
        start_tasks = num_tasks
        max_move = -1
        max_main = []
        for lst in map(list,product(*[[1,2,3,4,5,6]]*n)):
            num_tasks += 1
            if DEBUG: print lst, [], []
            sort.lst = lst
            cur_lst = lst[:]
            move.history = [(None, None, None)]
            help1 = []
            help2 = []
            moves = sort_better(lst, help1, help2)
            if moves > max_move:
                max_move = moves
                max_main = cur_lst
            num_moves += moves

            if DEBUG: print '%s, %s, %s (moves: %d)' % (cur_lst, [], [], moves)
            if list(reversed(sorted(lst))) != lst:
                print 'NOT SORTED: %s' % lst
                return
            if DEBUG: print

            # Verify that the modified list of moves is still valid
            verify(cur_lst, move.history)
        end_moves = num_moves - start_moves
        end_tasks = num_tasks - start_tasks
        print 'Length %d\nMoves: %d\nTasks: %d\nMax: %d (%s)\nAverage: %.3f\n' % (n, end_moves, end_tasks, max_move, max_main, 1.0*end_moves/end_tasks)
    print '--------------'
    print 'Overall\nMoves: %d\nTasks: %d\nAverage: %.3f' % (num_moves, num_tasks, 1.0*num_moves/num_tasks)

# Old sort method, which assumes we can only see the top of the stack
def sort(main, max_stack, a_stack):
    height = len(main)
    largest = -1
    num_moves = 0
    a_stack_second_el = 10**10
    for i in range(height):
        if len(main)==0:
            break
        el = main[-1]
        if el > largest: # We found a new maximum element
            if i < height-1: # Process only if it is not at the bottom of main stack
                largest = el
                if len(a_stack)>0 and a_stack[-1] < max_stack[-1] < a_stack_second_el:
                    a_stack_second_el = max_stack[-1]
                # Move aux stack to max stack then reverse the role
                num_moves += help_to_help(a_stack, max_stack, main)
                max_stack, a_stack = a_stack, max_stack
                if DEBUG: print 'Moved max_stack to a_stack: %s, %s, %s (moves: %d)' % (main, max_stack, a_stack, num_moves)
                num_moves += move(main, max_stack)
                if DEBUG: print 'Moved el to max_stack: %s, %s, %s (moves: %d)' % (main, max_stack, a_stack, num_moves)
        elif el == largest:
            # The maximum element is the same as in max stack, append
            if i < height-1: # Only if the maximum element is not at the bottom
                num_moves += move(main, max_stack)
        elif len(a_stack)==0 or el <= a_stack[-1]:
            # Current element is the same as in aux stack, append
            if len(a_stack)>0 and el < a_stack[-1]:
                a_stack_second_el = a_stack[-1]
            num_moves += move(main, a_stack)
        elif a_stack[-1] < el <= a_stack_second_el:
            # Current element is larger, but smaller than the next largest element
            # Step 1
            # Move the smallest element(s) in aux stack into max stack
            amount = 0
            while len(a_stack)>0 and a_stack[-1] != a_stack_second_el:
                num_moves += move(a_stack, max_stack)
                amount += 1

            # Step 2
            # Move all elements in main stack that is between the smallest
            # element in aux stack and current element
            while len(main)>0 and max_stack[-1] <= main[-1] <= el:
                if max_stack[-1] < main[-1] < a_stack_second_el:
                    a_stack_second_el = main[-1]
                num_moves += move(main, a_stack)
                el = a_stack[-1]

            # Step 3
            # Put the smallest element(s) back
            for i in range(amount):
                num_moves += move(max_stack, a_stack)
        else: # Find a location in aux stack to put current element
            # Step 1
            # Move all elements into max stack as long as it will still
            # fulfill the Hanoi condition on max stack, AND
            # it should be greater than the smallest element in aux stack
            # So that we won't duplicate work, because in Step 2 we want
            # the main stack to contain the minimum element
            while len(main)>0 and a_stack[-1] < main[-1] <= max_stack[-1]:
                num_moves += move(main, max_stack)

            # Step 2
            # Pick the minimum between max stack and aux stack, move to main
            # This will essentially sort (in reverse) the elements into main
            # Don't move to main the element(s) found before Step 1, because
            # we want to move them to aux stack
            while True:
                if len(a_stack)>0 and a_stack[-1] < max_stack[-1]:
                    num_moves += move(a_stack, main)
                elif max_stack[-1] < el:
                    num_moves += move(max_stack, main)
                else:
                    break

            # Step 3
            # Move all elements in main into aux stack, as long as it
            # satisfies the Hanoi condition on aux stack
            while max_stack[-1] == el:
                num_moves += move(max_stack, a_stack)
            while len(main)>0 and main[-1] <= a_stack[-1]:
                if main[-1] < a_stack[-1] < a_stack_second_el:
                    a_stack_second_el = a_stack[-1]
                num_moves += move(main, a_stack)
        if DEBUG: print main, max_stack, a_stack
    # Now max stack contains largest element(s), aux stack the rest
    num_moves += push_to_main(max_stack, main)
    num_moves += move_to_main(a_stack, main, max_stack)
    return num_moves

if __name__ == '__main__':
    main()

에 대한 질문이 없습니다 4. 두 번째 도우미에서 두 번째로 큰 요소를 저장하는 것은 무엇입니까? 그게 무슨 뜻이야?
저스틴

기본적으로 변수에서 두 번째로 큰 요소를 추적하십시오. 나는 그것을 지나치게 생각하고 있었다고 생각한다. 나는 그것이 완벽하다고 생각합니다, haha
justhalf

"함수 / 서브 루틴은 언제든지 스택을 검사 할 수 있습니다". 따라서 스택을 실행하고 두 번째로 큰 요소의 위치를 ​​찾아서 수행중인 작업을 쉽게 수행 할 수 있다면 괜찮습니다.
저스틴

1
"언제든지 스택 검사"란 이동을 소비하지 않고 스택처럼 스택을 읽을 수 있음을 의미합니까? 그것은 모든 것을 바꿉니다. 설명과 관련하여, 나는 여전히 알고리즘을 업데이트하는 과정에 있습니다 (더 낮게 얻었으므로) 완료되면 업데이트 할 것입니다.
justhalf

1
내가 참조. 그것은 완전히 새로운 가능성을 열어 주며 분명히 우리는 더 많은 움직임을 줄일 수 있습니다. 하하, 작업이 본질적으로 "정수 배열을 부여 받았기 때문에 하노이 타워를 정렬하기 위해 최소한의 움직임을 찾으십시오". 스택의 상단 만 볼 수 있다면 내 알고리즘은 최적에 가깝습니다 (최적이 아닌 경우)
justhalf

1

Java- 2129090 2083142가 55986 어레이로 이동

ideone 링크 .

알고리즘이 올바른지 확인하는 프레임 워크 :

private final Deque<Integer> main = new ArrayDeque<Integer>();
private final Deque<Integer> help1 = new ArrayDeque<Integer>();
private final Deque<Integer> help2 = new ArrayDeque<Integer>();

private int taskCount = 0;
private int opCount = 0;

private void sort(List<Integer> input) {
    taskCount++;
    main.addAll(input);
    sortMain();
    verify();
    main.clear();
}

private void verify() {
    if (!help1.isEmpty()) {
        throw new IllegalStateException("non-empty help1\n" + state());
    }
    if (!help2.isEmpty()) {
        throw new IllegalStateException("non-empty help2\n" + state());
    }
    int last = 0;
    for (int i: main) {
        if (last > i) {
            throw new IllegalStateException("unsorted main: " + main);
        }
        last = i;
    }
}

private void done() {
    System.out.println();
    System.out.print(opCount + "/" + taskCount);
}

private void move(Deque<Integer> from, Deque<Integer> to) {
    if (from == to) throw new IllegalArgumentException("moving from/to " + from);
    Integer i = from.pop();
    if (to != main && !to.isEmpty() && i > to.peek()) {
        throw new IllegalStateException(
                from + " " + i + " -> " + to);
    }
    to.push(i);
    opCount++;
}

private String name(Deque<Integer> stack) {
    return stack == help1 ? "help1" :
           stack == help2 ? "help2" :
           "main";
}

private String state() {
    return "main:  " + main + 
            "\nhelp1: " + help1 +
            "\nhelp2: " + help2;
}

실제 알고리즘 :

private void ensureMain(Deque<Integer> stack) {
    if (stack != main) {
        throw new IllegalArgumentException("Expected main, got " + name(stack) + "\n" + state());
    }
}

private void ensureHelp(Deque<Integer> stack) {
    if (stack == main) {
        throw new IllegalArgumentException("Expected help, got main\n" + state());
    }
}

private void ensureHelpers(Deque<Integer> stack1, Deque<Integer> stack2) {
    ensureHelp(stack1);
    ensureHelp(stack2);
}

private void sortMain() {
    int height = main.size();
    int topIndex = height;
    while (topIndex == height && height > 1) {
        topIndex = lastIndexOfLargest(height, main);
        height--;
    }
    if (topIndex == height) { 
        // is already sorted
        return;
    }
    // split stack at largest element
    int part1Count = topIndex;
    int part2Count = height - topIndex;
    // move largest and first part to help 1
    moveFromMain(part1Count+1, help1, help2);
    // merge both parts to help 2, leaving largest on 1
    mergeToHelp(part2Count, main, part1Count, help1, help2);
    // move largest to main
    move(help1, main);
    // move remaining to main
    moveToMain(height, help2, help1);
}

/** Moves elements from main to helper, sorting them */
private void moveFromMain(int amount, Deque<Integer> target, Deque<Integer> help) {
    if (amount < 1) return;
    ensureHelpers(target, help);
    int topIndex = lastIndexOfLargest(amount, main);
    int part1Count = topIndex;
    int part2Count = amount - topIndex - 1;
    // move first part to help
    moveFromMain(part1Count, help, target);
    // move largest to target
    move(main, target);
    // merge both parts to target
    mergeToHelp(part2Count, main, part1Count, help, target);
}

/** Moves elements from helper to main, keeping them sorted */
private void moveToMain(int amount, Deque<Integer> source, Deque<Integer> help) {
    if (amount < 1) return;
    ensureHelpers(source, help);
    moveHelper(amount-1, source, help);
    move(source, main);
    moveToMain(amount-1, help, source);
}

/** Moves elements between helpers */
private void moveHelper(int amount, Deque<Integer> source, Deque<Integer> target) {
    pushToMain(amount, source);
    popFromMain(amount, target);
}

/** Merges top of main and helper to other helper */
private void mergeToHelp(int mainAmount, Deque<Integer> main, int helpAmount, Deque<Integer> help, Deque<Integer> target) {
    ensureMain(main);
    ensureHelpers(help, target);
    if (mainAmount < 0) mainAmount = 0;
    if (helpAmount < 0) helpAmount = 0;
    while (mainAmount > 0 || helpAmount > 0) {
        // while there is something to merge
        int largestMain = valueOfLargest(mainAmount, main);
        int largestHelp = valueOfLargest(helpAmount, help);
        if (largestMain > largestHelp) {
            // largest is in main
            int index = firstIndexOfLargest(mainAmount, main);
            if (index > 0) {
                // move excess to help:
                int mainTop = index;
                int helpTop = elementsSmallerThan(help, largestMain);
                if (helpTop > 0) {
                    // 1. move top of help to target
                    moveHelper(helpTop, help, target);
                    // 2. merge old top with excess from main
                    mergeToHelp(mainTop, main, helpTop, target, help);
                } else {
                    moveFromMain(mainTop, help, target);
                }
                mainAmount -= mainTop;
                helpAmount += mainTop;
            }
            move(main, target);
            mainAmount--;
        } else {
            // largest is at bottom of help
            int helpTop = helpAmount - 1; // largest is at bottom
            // move top to main
            pushToMain(helpTop, help);
            mainAmount += helpTop;
            // move largest to target
            move(help, target);
            helpAmount = 0;
        }
    }
}

private void pushToMain(int amount, Deque<Integer> from) {
    for (; amount > 0; amount--) move(from, main);
}

private void popFromMain(int amount, Deque<Integer> to) {
    for (; amount > 0; amount--) move(main, to);
}

private int firstIndexOfLargest(int height, Deque<Integer> stack) {
    if (height == 0) throw new IllegalArgumentException("height == 0");
    int topValue = 0;
    int topIndex = 0;
    int i = 0;
    for (Integer e: stack) {
        if (e > topValue) {
            topValue = e;
            topIndex = i;
        }
        if (++i == height) break;
    }
    return topIndex;
}

private int lastIndexOfLargest(int height, Deque<Integer> stack) {
    if (height == 0) throw new IllegalArgumentException("height == 0");
    int topValue = 0;
    int topIndex = 0;
    int i = 0;
    for (Integer e: stack) {
        if (e >= topValue) {
            topValue = e;
            topIndex = i;
        }
        if (++i == height) break;
    }
    return topIndex;
}

private int valueOfLargest(int height, Deque<Integer> stack) {
    int v = Integer.MIN_VALUE;
    for (Integer e: stack) {
        if (height-- == 0) break;
        if (e > v) v = e;
    }
    return v;
}

private int elementsSmallerThan(Deque<Integer> stack, int value) {
    int i = 0;
    for (Integer e: stack) {
        if (e >= value) return i;
        i++;
    }
    return i;
}

테스트 케이스 :

public static void main(String[] args) throws Exception {
    HanoiSort hanoi = new HanoiSort();
    int N = 6;
    for (int len = 1; len <= N; len++) {
        Integer[] input = new Integer[len];
        List<Integer> inputList = Arrays.asList(input);
        int max = N;
        for (int i = 1; i < len; i++) max *= N;
        for (int run = 0; run < max; run++) {
            int n = run;
            for (int i = 0; i < len; n /= N, i++) {
                input[i] = (n % N)+1;
            }
            try {
                hanoi.sort(inputList);
            } catch (Exception e) {
                System.out.println(inputList);
                e.printStackTrace(System.out);
                return;
            }
        }
    }
    hanoi.done();
}

-1

C / C ++ 움직임을 측정하지 못했습니다 (페그 : p1, p2, p3) STL (포맷 문제)을 사용하는 C ++ 코드를 추가하는 방법을 모릅니다. 코드의 html 태그 형식으로 인한 코드 일부 잃기

  1. N 가져 오기 : P1에서 최상위 정렬 요소 수
  2. p2를 사용하여 (n)을 p3으로 이동하십시오 (정렬 된 속성 유지)
  3. p1에서 p2로 역순으로 다음 m 요소 (이스트 1)를 이동하십시오.
  4. p2 및 p3의 이동 (n + m) 데이터를 p1로 병합합니다.

         4.1 merge sort p2 & P3 (reverse) to p1
         4.2 move (n+m) to p3
         4.3 hanoi p3 to p1 using p2
    
  5. 최소한 n + m + 1 개의 요소를 정렬하는 hanoi sort를 재귀 적으로 호출하십시오.
  6. n = p1의 크기 일 때 중지합니다.
#포함 
#포함 
네임 스페이스 std 사용;
/ ********************************************************** ***************************** 
   벡터를 보여주십시오
******************************************************** ***************************** /    

무효 쇼 (벡터 p, 문자열 msg)
{
   벡터 :: 반복자 it;
   printf ( "% s \ n", msg.c_str ());
   for (it = p.begin (); 그것은 & fr, 벡터 & inter, 벡터 & to, int n) {
   int d1;
   if (n & p1, vector & p2, vector & p3) {
   int d3, d2, d1;
   int count, n;
   부울 d2_added;

   d2 = p2.back (); p2.pop_back ();
   // p1에서 데이터가 내림차순으로 표시됩니다
   d2_added = 거짓;
   while (p3.size ()> 0) {
      d3 = p3.back (); p3.pop_back ();
      if (d2_added == false && d2 0) {
      d1 = p1.back (); p1.pop_back ();
      p3. 푸시 _ 백 (d1);
      --카운트;
   }
   // hanoi를 사용하여 p3에서 p1로 다시 이동
   // p2를 inter로 사용
   하노이 (p3, p2, p1, n);
}
/ ********************************************************** ******************************
   첫 번째 정렬 된 요소의 개수를 가져옵니다.
******************************************************** ***************************** /    
int get_top_sorted_count (벡터 & p1) {
   벡터 :: 반복자 it;
   int prev = 0;
   int n = 1;

   for (it = --p1.end (); it> = p1.begin (); --it) {
     if (it == --p1.end ()) {
    이전 = * it;
        잇다;
     }
     if (* it & p1, 벡터 & p2, 벡터 & p3) {
    int n, d1;
    n = get_top_sorted_count (p1);
    if (n == p1.size ()) {
       반환;
    }
    하노이 (p1, p2, p3, n);
    p2.push_back (p1.back ());
    p1.pop_back ();
    merge_move (p1, p2, p3);
    하노이 정렬 (p1, p2, p3);
}
/ ********************************************************** ******************************    
    하노이에서 입힌 종류
******************************************************** ***************************** /    
int main (void)
{
  벡터 p1, p2, p3;
  p1. 푸시 _ 백 (5);
  p1. 푸시 _ 백 (4);
  p1. 푸시 _ 백 (7);
  p1. 푸시 _ 백 (3);
  p1. 푸시 _ 백 (8);
  p1. 푸시 _ 백 (2);
  show (p1, "... Bef Sort ...");
  하노이 정렬 (p1, p2, p3);
  show (p1, "... 후방 정렬 ...");
}

이것에 대한 코드를 작성할 수 있습니까? 그렇지 않으면 대답이 아닙니다.
저스틴

hanoi(...)기능 이 보이지 않습니다 . 또한 #include컴파일하지 않은 2가 있습니다 . 전체 코드를 게시하십시오.
Hosch250
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.