Saltar al contenido

Torre de Hanoi Sort

Solución:

Java: solución óptima (1080544 movimientos)

Esta solución crea un árbol de ruta más corto desde el objetivo y hacia atrás, luego atraviesa la ruta desde el estado inicial hasta el objetivo. Mucho espacio para mejorar la velocidad, pero aún resuelve todos los 55986 problemas en aproximadamente un minuto.

Suponiendo que el algoritmo se implemente correctamente, esta debería ser la mejor solución teóricamente.

import java.util.*;

public class HanoiSort 

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

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

    private static final List 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 initMainStack) 
        List> initState = Arrays.asList(new ArrayList<>(initMainStack), EMPTY_STACK, EMPTY_STACK);
        List targetMainStack = new ArrayList<>(initMainStack);
        Collections.sort(targetMainStack);
        List> targetState = Arrays.asList(new ArrayList<>(targetMainStack), EMPTY_STACK, EMPTY_STACK);
        Map>,List>> tablebase = new HashMap<>();
        Deque>> workQueue = new ArrayDeque<>();
        tablebase.put(targetState, null);
        workQueue.add(targetState);
        while (!tablebase.containsKey(initState)) 
            assert !workQueue.isEmpty() : initState.toString();
            List> state = workQueue.removeFirst();
            Collection>> prevStates = calcPrevStates(state);
            for (List> prevState : prevStates) 
                if (!tablebase.containsKey(prevState)) 
                    tablebase.put(prevState, state);
                    workQueue.add(prevState);
                
            
        

        int numMoves = 0;
        List> 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>> calcPrevStates(List> state) 
        Collection>> prevStates = new ArrayList<>();
        for (int fromStackNo = 0; fromStackNo < 3; ++fromStackNo) 
            List fromStack = state.get(fromStackNo);
            if (!fromStack.isEmpty()) 
                int number = fromStack.get(0);
                for (int toStackNo = 0; toStackNo < 3; ++toStackNo) 
                    if (toStackNo != fromStackNo) 
                        List toStack = state.get(toStackNo);
                        if ((toStackNo == 0) 
                
            
        
        return prevStates;
    


Pitón, 3983838 3912258 se mueve sobre 55986 entradas

Este es muy ineficiente.

Agregaré el número total de movimientos después de que el OP aclare si son para todos esos casos o para otros casos específicos.


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

Explicación

¿Qué, los comentarios no son lo suficientemente buenos para ti?


Nota para OP: Gracias por no hacer este código de golf.

C – 2547172 para 55986 entradas

Aquí hay mucho margen de mejora. Por mi propia cordura, simplifiqué esto para que solo fuera posible inspeccionar el elemento superior de cada pila. El levantamiento de esta restricción autoimpuesta permitiría optimizaciones como determinar el orden final por adelantado y tratar de minimizar el número de movimientos necesarios para lograrlo. Un ejemplo convincente es que mi implementación tiene el peor comportamiento de caso si la pila principal ya está ordenada.

Algoritmo:

  1. Llene ambas pilas auxiliares (espacio para la optimización aquí, posiblemente asignando a qué pila en función de algún tipo de pivote).
  2. Combinar y ordenar las pilas auxiliares de nuevo en la pila principal.
  3. Repita 1-2 hasta que la pila principal esté ordenada (pero al revés).
  4. Invierta la pila principal (más espacio para la optimización, mezclando muchos de los mismos elementos repetidamente).

Análisis:

  • La complejidad adicional del espacio es O (n) (para las dos pilas auxiliares), lo cual es bueno, ya que era un requisito del problema.
  • La complejidad del tiempo es O (n ^ 2) según mi recuento. Las correcciones son bienvenidas.

#include 
#include 

#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 

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 


void
reverse(void)

    while(1)
     top(s2) < top(s0))
        
            while(count(s2) > 0)
            
                move(s2, s0);
            
            break;
        
        while(count(s0) > 0 && (count(s1) == 0 


void
sort(void)

    while(1)
     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("%dn", permutations);
    printf("%dn", total);
    return 0;

Acuérdate de que tienes la capacidad de decir si te ayudó.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *