Hanoi Kulesi Sırala


21

Tower of Hanoi tarzı bir tamsayı listesi düzenlemek için bir fonksiyon / alt yordam yazın .

Size bir tamsayı yığını verilecektir . Bu ana yığın.

Ayrıca size iki yardımcı yığın daha verilmiştir. Bu yardımcı istifler, ancak benzersiz bir özelliğe sahiptir: her eleman, altındaki elemanla aynı veya daha küçük olmalıdır. Ana yığında böyle bir kısıtlama yoktur.

En büyük tamsayıları altına koyarak ana yığını yerine yerleştirme görevini üstlenirsiniz. İşlev / alt yordamınız yığını ayırırken yaptığı hareket sayısını döndürür (veya eşdeğeri).
Not: Ana yığını yerinde sıralamanız, başka bir yığında sıralama yapmamanız ve cevabı çağırmanız gerekir. Ancak, bir nedenden ötürü bunu yapamazsanız, değişken yığınları taklit edebilirsiniz, ancak bunun Hanoi Kulesi sıralamaları olduğunu unutmayın; sadece 3 mandal var ve sadece 1 mandal sıralanmamış olabilir.

İşlev / alt yordamınız herhangi bir anda herhangi bir yığını denetleyebilir, ancak yalnızca fırlatıp iterek hareket edebilir. Tek bir hamle, bir yığından diğerine itilen bir pop'tur.

İlk 6 doğal sayının her permütasyonu için fonksiyon / alt rutininizi test edin. Başka bir deyişle, işlevinizi / alt rutininizi sınayın {1},{2},...,{6},{1,1},{1,2},...,{1,6},{2,1},...(bu toplam veya olasılık olmalıdır (matematiğimi düzeltmek için Howard'a teşekkürler)). Elemanları en az sayıda hareket ettiren fonksiyon / alt yordam kazanır.61+62+...+6655986


@JanDvorak Testlerde bu bir tür fikirdi. Programcının fonksiyonu 46656 defa çalıştırması gerekiyorsa, çıkış için neden bu kadar uzun süre beklemek istesin ki? Yoksa bu tür şeyleri kısıtlamanın başka bir yolu var mı?
Justin,

Her nasılsa, kör-zap yarışmasını beğendim: sadece "x yığınından y yığınına taşı" diyebilirsin ve hareketinin başarılı olup olmadığını görebilirsin, eğer öyleyse, bunun için ücret alırsın; Bonus puanları, hareket başarısızlığının bir istisna atması / doğru şekilde geri verilmesi ile gösterilir.
John Dvorak

3
Verdiğiniz "permütasyonlar" listesi 6**1+6**2+...+6**6=55986elementler içeriyor .
Howard

1
@ m.buettner Buradaki ayrım, bunun kartezyen ürünün 1 ila 6 katı olduğu elementlerdir . Muhtemelen buna " sıfır kümesi hariç ilk 6 doğal sayının güç kümesinin her bir elemanının permütasyonları" derdim .
Justin

1
@ Quincunx güç seti dışında tekrarlanan sayılar içeren kümeler içermiyor. ;) ... ama setteki öğeler hakkında net bir şekilde konuşmamız şartıyla, bunu zaten ciddiye almamamız gerektiğini düşünüyorum.
Martin Ender

Yanıtlar:


4

Java - optimum çözüm (1080544 hamle)

Bu çözüm, hedeften en kısa yol ağacını oluşturur ve geriye doğru ilerler, sonra yolu ilk durumdan hedefe doğru geçirir. Hız artışı için çok fazla alan var, ancak 55986'daki tüm problemleri yaklaşık bir dakikada tamamlıyor.

Algoritmanın doğru bir şekilde uygulandığını varsayarsak, bu teorik olarak en iyi çözüm olmalıdır.

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;
    }

}

"En kısa yol ağacı" ile ne demek istediğinizi açıklamak ister misiniz?
justhalf

Yine de cevabınız için teşekkürler, sadece sezgisel
tarama

En kısa yol ağacı, her bir düğümün / durumun bir "sonraki" kenara sahip olduğu ve sırasıyla kök / hedef duruma (= sıralı ana yığına) en yakın olan düğüme / duruma giden bir ağaçtır. Sonraki düğümlerde aynı mesafeye veya daha fazlasına sahip fakat hiçbiri köke yakın başka adaylar olabilir. Bu problem için, en kısa yol ağacındaki tüm kenarlar bir mesafeye sahiptir, çünkü bir durumdan diğerine gitmek bir hamle alır. Temel olarak, tam bir en kısa yol ağacı, aynı hedef duruma sahip olan tüm durumlar için çözümü içerir.
MrBackend

@justhalf Yorumumda sizden bahsetmeyi unuttum. BTW, ayrıca bkz en.wikipedia.org/wiki/Retrograde_analysis
MrBackend

5

55986 girişler için C - 2547172

Burada iyileştirme için çok yer var. Kendi aklım için, bunu basitleştirdim, böylece her yığının en üst elemanını kontrol etmek mümkün olabildi. Bu kendi kendini empoze eden kısıtlamayı kaldırmak, nihai siparişi önceden belirlemek ve bunu başarmak için gereken hareket sayısını en aza indirmeye çalışmak gibi optimizasyonlara izin verir. Zorlayıcı bir örnek, ana yığın zaten sıralanmışsa, uygulamamın en kötü durum davranışına sahip olmasıdır.

Algoritma:

  1. Her iki yardımcı yığını da doldurun (burada bir pivot türüne dayanarak hangi yığına atama yapacağınız konusunda optimizasyon için oda).
  2. Birleştirme yardımcı yığınları ana yığına geri sıralayın.
  3. Ana yığın sıralanana kadar 1-2'yi tekrarlayın (ancak tersi).
  4. Ana yığını ters çevirin (optimizasyon için daha fazla alan, aynı elementlerin çoğunu tekrar tekrar karıştırın).

Analiz:

  • Ek alan karmaşıklığı, O (n) 'dir (iki yardımcı yığın için), ki bu iyi, çünkü problemin bir gereğidir.
  • Zaman karmaşıklığı sayıma göre 0 (2). Düzeltmeler kabul edilir.

#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

Python, 3983838 3912258, 55986 girişleri aşar

Bu çok verimsiz.

OP, tüm bu durumlar için mi yoksa belirli durumlar için mi olduğunu açıkladıktan sonra toplam hamle sayısını ekleyeceğim.


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

açıklama

Ne, yorumlar senin için yeterince iyi değil mi?


OP'ye Not: Bu kod golfünü yapmadığınız için teşekkürler.


Ben kod [golf] dışında başka bir zorlu iş yapmanın daha ilginç bir yolu varsa, bu şekilde yapılması gerektiğine inanıyorum.
Justin,

Tamam, bu [1,1,2] için başarısız. Python'da, bir liste göz önüne alındığında 2 olarak [2,1,1]elde etmek için bir yol var [2,1,1].index(1)yani yüksek uçtan başlayarak?
user80551

@Quincunx Hayır, [2,1,1,3,5].index(1)==2yerine1
user80551 17

Er, list.index(data)öğenin dizinini döndürür dataiçinde list. Ben yok dizinini bilmeniz datayani1
user80551

len(list)-(list[::-1].index(1))
Kesmek

2

Python: 1,688,293 1,579,182 1,524,054 1,450,842 1,093,195

Ana yöntem, main_to_help_bestseçilen bazı öğeleri ana yığından yardımcı yığına taşımaktır. Her everythingşeyi belirtilen alana taşımasını destinationmı istediğimizi mi yoksa yalnızca en büyüğünü tutmak mı istediğimizi tanımlayan bir bayrağa sahiptir .destination diğer yardımcıda kalanlar içinde .

dstYardımcı kullanmaya geçtiğimizi varsayalım helper, işlev kabaca aşağıdaki gibi tanımlanabilir:

  1. En büyük elementlerin pozisyonlarını bulun
  2. Her şeyi helpertekrarlı bir şekilde en üstteki en büyük öğenin üstüne getirin
  3. En büyük öğeyi dst
  4. Dan geri itin helperana kadar
  5. En büyük unsurlar görünene kadar 2-4'ü tekrarlayın. dst
  6. a. Eğer everythingayarlandıysa, elemanları ana olarak dst
    b'ye getirin. Aksi takdirde, ana unsurları yinelemeli olarak anahelper

Ana sıralama algoritması ( sort2kodumda) daha sonra set main_to_help_bestile everythingçağrılacak Falseve daha sonra en büyük elemanı tekrar ana konuma getirecek, daha sonra her şeyi yardımcıdan ana konuma getirerek sıralı tutacaktır.

Daha fazla açıklama kodda yorum olarak gömülüdür.

Temelde kullandığım ilkeler:

  1. Maksimum eleman (lar) ı içerecek şekilde bir yardımcı tutun
  2. Başka elementler içerecek başka bir yardımcı tutun
  3. Gereksiz hareketleri mümkün olduğunca yapma

İlke 3, kaynak önceki hedef ise (yani, sadece yardım için ana 1'e hareket ettik, daha sonra yardım1'den yardım2'ye doğru hareket etmek istiyoruz2) hamleyi saymayarak uygulanır. orijinal konumuna geri götürüyorlar (yani ana yardım1, ardından ana1'e yardım ediyor). Ayrıca, önceki nhareketlerin tümü aynı tamsayıyı taşıyorsa, aslında bu nhareketleri yeniden sıralayabiliriz . Bu yüzden hareket sayısını daha da azaltmak için bundan faydalanıyoruz.

Bu, ana yığtaki tüm unsurları bildiğimiz için geçerlidir, bu nedenle gelecekte öğeyi geri taşıyacağımızı görünce yorumlanabilir, bu hareketi yapmamalıyız.

Örnek çalıştırma (yığınlar alttan üste gösterilir - bu nedenle ilk öğe alt olur):

Uzunluk 1
Hamle: 0
Görevler: 6
Maksimum: 0 ([1])
Ortalama: 0.000

Uzunluk 2
Hamle: 60
Görevler: 36
Maksimum: 4 ([1, 2])
Ortalama: 1.667

Uzunluk 3
Hamle: 1030
Görevler: 216
Maksimum: 9 ([2, 3, 1])
Ortalama: 4.769

Uzunluk 4
Hamle: 11765
Görevler: 1296
Maksimum: 19 ([3, 4, 2, 1])
Ortalama: 9.078

Uzunluk 5
Hamle: 112325
Görevler: 7776
Maksimum: 33 ([4, 5, 3, 2, 1])
Ortalama: 14.445

Uzunluk 6
Hamle: 968015
Görevler: 46656
Max: 51 ([5, 6, 4, 3, 2, 1])
Ortalama: 20.748

--------------
tüm
Hamle: 1093195
Görevler: 55986
Ortalama: 19.526

En kötü durumun, en büyük öğenin ikinci tabana yerleştirildiği, geri kalanların sıralandığı zaman olduğunu görebiliriz. En kötü durumdan algoritmanın O (n ^ 2) olduğunu görebiliriz.

Hamle sayısı açıkça n=1ve n=2sonuçtan da görebileceğimiz gibi minimum ve nispatlayamasam da bunun daha büyük değerler için de minimum olduğuna inanıyorum .

Daha fazla açıklama koddadır.

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()

Sorunu anlamadım 4. Bu, ikinci en büyük elemanı ikinci yardımcıya depolayan şey nedir? Bu ne anlama geliyor?
Justin,

Temel olarak, sadece bir değişkendeki en büyük ikinci elemanı takip edin. Sanırım ondan korkuyordum. Bence gayet iyi, haha
justhalf

"İşlev / alt yordamınız herhangi bir anda herhangi bir yığını denetleyebilir". Yani, yaptığınız şey yığınlardan geçerek ve en büyük ikinci elementin yerini bularak kolayca yapılabilirse, sorun değil.
Justin

1
"Herhangi bir zamanda herhangi bir yığını denetle" ifadesiyle, yığını herhangi bir hareket kullanmadan bir dizi gibi okuyabildiğim anlamına mı geliyor? Bu her şeyi değiştirir. Açıklama ile ilgili olarak, hala algoritmayı güncelleme sürecindeyim.
justhalf '

1
Anlıyorum. Bu yepyeni bir olasılık ortaya çıkarır ve kesinlikle hamle sayısını azaltabiliriz. Sorunu daha da zorlaştıracak, haha, çünkü görev temelde "bir tam sayı dizisi verildiğinde, onu bir Hanoi Kulesi la sıralamak için minimum hamle sayısını bulmak". Yığının en üstünü görmemize izin verilirse, algoritmam en
iyisine

1

Java - 2129090 2083142 55986 dizisinde hareket ediyor

İdeone bağlantı .

Algoritma sağlamak için çerçeve doğru:

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;
}

Gerçek algoritma:

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;
}

Test çantası:

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 ++ Hareketleri ölçmedim (pegs: p1, p2, p3) STL kullanan C ++ kodunun nasıl ekleneceğini bilmiyorum (oluşturma sorunu). Koddaki html etiketi formatları nedeniyle kod bölümlerini kaybetmek.

  1. N al: p1'deki üst sıradaki öğelerin sayısı
  2. Bunları (n) p3 kullanarak p2 ile hanoi hareketi yapın (sıralı özelliği korur)
  3. Sonraki m elemleri (en az 1) p1'den p2'ye ters sırayla hareket ettirin.
  4. P2 ve p3'ten p1'e taşıma (n + m) verilerini birleştir.

         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. Şimdi en az n + m + 1 elementlerini sıralayacak olan nihayetinde hanoi sort'i çağırın.
  6. N = p1 büyüklüğü durduğunda.
#Dahil etmek 
#Dahil etmek 
namespace std kullanarak;
/ ************************************************* ***************************** 
   Vektörü göster (Şans eseri olmak zorunda kaldım)
************************************************** ***************************** /    

geçersiz gösteri (vektör p, string msg)
{
   vector :: yineleyici;
   printf ("% s \ n", msg.c_str ());
   (it = p.begin (); için & fr, vektör & iç, vektör &, int n)
   int d1;
   eğer (n & p1, vektör ve p2, vektör ve p3) {
   int d3, d2, d1;
   int sayısı, n;
   bool d2_added;

   d2 = p2.back (); ) (P2.pop_back;
   // veri p1’de azalacak
   d2_added = yanlış;
   while (p3.size ()> 0) {
      d3 = p3.back (); ) (P3.pop_back;
      if (d2_added == yanlış && d2 0) {
      d1 = p1.back (); p1.pop_back ();
      (D1) p3.push_back;
      --count;
   }
   // Hanoi ile p3'ten p1'e geri dönün
   // p2'yi inter olarak kullanın
   hanoi (p3, p2, pl, n);
}
/ ************************************************* ******************************
   İlk sıralanan öğelerin sayısını alır
************************************************** ***************************** /    
int get_top_sorted_count (vektör & p1) {
   vector :: yineleyici;
   int prev = 0;
   int n = 1;

   ((= - p1.end (); için> = p1.begin (); --it) {
     if (it == --p1.end ()) {
    prev = * o;
        devam et;
     }
     if (* & p1, vektör ve p2, vektör ve p3) {
    int n, d1;
    n = get_top_sorted_count (p1);
    if (n == p1.size ()) {
       dönüş;
    }
    hanoi (p1, p2, p3, n);
    p2.push_back (p1.back ());
    ) (P1.pop_back;
    birleştirme_move (p1, p2, p3);
    hanoi_sort (p1, p2, p3);
}
/ ************************************************* ******************************    
    Hanoi üzerinde baed sıralar
************************************************** ***************************** /    
int main (geçersiz)
{
  vektör pl, p2, p3;
  p1.push_back (5);
  p1.push_back (4);
  p1.push_back (7);
  p1.push_back (3);
  p1.push_back (8);
  p1.push_back (2);
  göster (p1, "... Sırala ...");
  hanoi_sort (p1, p2, p3);
  show (p1, "... Aft Sort ...");
}

Bunun için kod yazabilir misin? Aksi takdirde, bu bir cevap değil.
Justin,

hanoi(...)Fonksiyonu göremiyorum . Ayrıca, #includederlemediğiniz 2 saniyeniz var. Lütfen tam kodu giriniz.
Hosch250,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.