하노이 타워 정렬


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. 나는 하지 않는 인덱스 알고 data즉1
— 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_best로 everything설정하여 호출 합니다.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=1와 n=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.