Tháp Hà Nội Sắp xếp


21

Viết hàm / chương trình con để sắp xếp danh sách các số nguyên, kiểu tháp Hà Nội .

Bạn sẽ được cung cấp một đống số nguyên. Đây là ngăn xếp chính.

Bạn cũng được cung cấp thêm hai ngăn xếp trợ giúp. Các ngăn xếp trợ giúp này có một thuộc tính duy nhất: mọi phần tử phải nhỏ hơn hoặc cùng kích thước với phần tử bên dưới nó. Ngăn xếp chính không có hạn chế như vậy.

Bạn có nhiệm vụ sắp xếp ngăn xếp chính vào vị trí, đặt các số nguyên lớn nhất bên dưới. Hàm / chương trình con của bạn sẽ trả về (hoặc tương đương) số lần di chuyển mà nó thực hiện khi sắp xếp ngăn xếp.
Lưu ý: bạn phải sắp xếp ngăn xếp chính tại chỗ , không sắp xếp vào ngăn xếp khác và gọi đó là câu trả lời. Tuy nhiên, nếu bạn vì một lý do nào đó không thể làm như vậy, bạn có thể mô phỏng các ngăn xếp có thể thay đổi, nhưng hãy nhớ rằng đây là Tháp Hà Nội sắp xếp; chỉ có 3 chốt và chỉ có 1 chốt có thể không được sắp xếp.

Hàm / chương trình con của bạn có thể kiểm tra bất kỳ ngăn xếp nào bất cứ lúc nào, nhưng nó chỉ có thể thực hiện di chuyển bằng cách bật và đẩy. Một di chuyển duy nhất là một pop từ một ngăn xếp được đẩy sang một ngăn xếp khác.

Kiểm tra chức năng / chương trình con của bạn cho mỗi hoán vị của 6 số tự nhiên đầu tiên. Nói cách khác, kiểm tra chức năng / chương trình con của bạn {1},{2},...,{6},{1,1},{1,2},...,{1,6},{2,1},...(đây phải là tổng số hoặc khả năng (cảm ơn Howard đã sửa lỗi toán học của tôi)). Hàm / chương trình con di chuyển các phần tử với số lần ít nhất sẽ thắng.61+62+...+6655986


@JanDvorak Đó là loại ý tưởng trong các bài kiểm tra. Nếu lập trình viên cần chạy chức năng 46656 lần, tại sao anh ta / cô ta lại muốn đợi quá lâu cho đầu ra? Hoặc có một cách tốt khác để hạn chế loại điều này?
Justin

Bằng cách nào đó tôi thích thử thách mù-zap: bạn chỉ có thể nói "chuyển từ stack x sang stack y" và xem liệu di chuyển của bạn có thành công hay không, và nếu có, bạn sẽ bị tính phí cho nó; điểm thưởng là di chuyển thất bại được chỉ định bằng cách ném một ngoại lệ / trả lại chính xác.
John Dvorak

3
Danh sách "hoán vị" bạn đưa ra có chứa 6**1+6**2+...+6**6=55986các yếu tố.
Howard

1
@ m.buettner Điểm khác biệt là đây là các yếu tố của sản phẩm cartes s từ 1 đến 6 lần . Có lẽ tôi gọi đây là "tập hợp hoán vị của từng phần tử của tập hợp sức mạnh của 6 số tự nhiên đầu tiên ngoại trừ tập hợp null."
Justin

1
@Quincunx ngoại trừ bộ năng lượng không chứa các bộ có số lặp lại. ;) ... nhưng tôi không nghĩ rằng chúng ta nên thực hiện việc này quá nghiêm túc, miễn là chúng ta đều rõ về các yếu tố trong tập hợp.
Martin Ender

Câu trả lời:


4

Java - giải pháp tối ưu (di chuyển 1080544)

Giải pháp này xây dựng một cây đường dẫn ngắn nhất từ ​​mục tiêu và ngược lại, sau đó đi qua đường dẫn từ trạng thái ban đầu đến mục tiêu. Rất nhiều chỗ để cải thiện tốc độ, nhưng nó vẫn hoàn thành tất cả 55986 vấn đề trong khoảng một phút.

Giả sử thuật toán được thực hiện chính xác, đây sẽ là giải pháp tốt nhất về mặt lý thuyết.

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

}

Muốn giải thích thêm về ý của bạn về "cây đường đi ngắn nhất"?
ngay

Dù sao cũng cảm ơn câu trả lời của bạn, điều đó làm tôi vui vì tôi có thể đạt được giải pháp gần tối ưu chỉ bằng cách sử dụng phương pháp
phỏng

Cây đường dẫn ngắn nhất là một cây trong đó mỗi nút / trạng thái có một cạnh "tiếp theo", dẫn đến nút / trạng thái, đến lượt nó, có khoảng cách ngắn nhất đến trạng thái gốc / đích (= ngăn xếp chính được sắp xếp). Có thể có các nút ứng cử viên tiếp theo có cùng khoảng cách hoặc nhiều hơn, nhưng không có nút nào gần với gốc hơn. Đối với vấn đề này, tất cả các cạnh trong cây đường đi ngắn nhất có khoảng cách là một, vì phải di chuyển từ trạng thái này sang trạng thái khác. Về cơ bản, một cây đường dẫn ngắn nhất hoàn chỉnh chứa giải pháp cho tất cả các trạng thái có cùng trạng thái đích.
MrBackend

@justhalf Quên đề cập đến bạn trong bình luận của tôi. BTW, xem thêm en.wikipedia.org/wiki/RetroTHER_analysis
MrBackend

5

C - 2547172 cho 55986 đầu vào

Có rất nhiều phòng để cải thiện ở đây. Đối với sự tỉnh táo của riêng tôi, tôi đã đơn giản hóa điều này để chỉ có thể kiểm tra phần tử trên cùng của mỗi ngăn xếp. Nâng cao hạn chế tự áp đặt này sẽ cho phép tối ưu hóa như xác định thứ tự cuối cùng trước và cố gắng giảm thiểu số lượng di chuyển cần thiết để đạt được nó. Một ví dụ hấp dẫn là việc triển khai của tôi có hành vi xấu nhất nếu ngăn xếp chính đã được sắp xếp.

Thuật toán:

  1. Điền vào cả hai ngăn phụ (phòng để tối ưu hóa ở đây, có thể gán cho ngăn xếp nào dựa trên một số loại trục).
  2. Hợp nhất sắp xếp các ngăn phụ trở lại vào ngăn xếp chính.
  3. Lặp lại 1-2 cho đến khi ngăn xếp chính được sắp xếp (nhưng ngược lại).
  4. Đảo ngược ngăn xếp chính (nhiều chỗ hơn để tối ưu hóa, xáo trộn rất nhiều yếu tố tương tự lặp đi lặp lại).

Phân tích:

  • Độ phức tạp không gian bổ sung là O (n) (đối với hai ngăn phụ), điều này là tốt, vì đó là một yêu cầu của vấn đề.
  • Độ phức tạp thời gian là O (n ^ 2) theo tính của tôi. Sửa chữa được chào đón.

#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 di chuyển hơn 55986 đầu vào

Điều này rất không hiệu quả.

Tôi sẽ thêm tổng số lần di chuyển sau khi OP làm rõ liệu chúng dành cho tất cả các trường hợp đó hay cho các trường hợp cụ thể khác.


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

Giải trình

Cái gì, ý kiến ​​không đủ tốt cho bạn?


Lưu ý với OP: Cảm ơn bạn đã không tạo ra mã golf này.


Tôi tin rằng nếu có một cách thú vị hơn để thực hiện thử thách ngoài [code-golf], thì nó nên được thực hiện theo cách đó.
Justin

Ok, điều này không thành công cho [1,1,2]. Trong python, xem xét một danh sách [2,1,1]có cách nào để có được [2,1,1].index(1)là 2 tức là bắt đầu từ cấp cao hơn không?
user80551

@Quincunx Không, [2,1,1,3,5].index(1)==2thay vì1
user80551

Er, list.index(data)trả về chỉ mục của mục datatrong list. Tôi không biết chỉ số datatức là1
user80551

Bản hack sẽ làlen(list)-(list[::-1].index(1))
user80551

2

Python: 1.688.293 1.579.182 1.524.054 1.450.842 1.093.195 di chuyển

Phương thức chính là main_to_help_best, để di chuyển một số phần tử được chọn từ ngăn xếp chính sang ngăn xếp trợ giúp. Nó có một lá cờ everythingxác định xem chúng ta muốn nó di chuyển mọi thứ vào trong chỉ định destinationhay chúng ta muốn giữ chỉ lớn nhất trong destinationkhi phần còn lại trong trình trợ giúp khác.

Giả sử chúng ta đang chuyển sang dstsử dụng trình trợ giúp helper, hàm có thể được mô tả đại khái như sau:

  1. Tìm vị trí của các phần tử lớn nhất
  2. Di chuyển mọi thứ lên trên đỉnh của phần tử lớn nhất trên cùng để helperđệ quy
  3. Di chuyển phần tử lớn nhất sang dst
  4. Đẩy lùi từ helperchính
  5. Lặp lại 2-4 cho đến khi các phần tử lớn nhất nằm trong dst
  6. a. Nếu everythingđược đặt, di chuyển đệ quy các phần tử trong chính sang dst
    b. Mặt khác, di chuyển đệ quy các phần tử trong chính sanghelper

Thuật toán sắp xếp chính ( sort2trong mã của tôi) sau đó sẽ gọi main_to_help_bestvới everythingset False, và sau đó di chuyển phần tử lớn nhất trở lại main, sau đó di chuyển mọi thứ từ trình trợ giúp trở lại chính, giữ cho nó được sắp xếp.

Giải thích thêm được nhúng dưới dạng nhận xét trong mã.

Về cơ bản các nguyên tắc mà tôi đã sử dụng là:

  1. Giữ một người trợ giúp để chứa (các) phần tử tối đa
  2. Giữ một người trợ giúp khác để chứa bất kỳ yếu tố nào khác
  3. Đừng làm những động tác không cần thiết càng nhiều càng tốt

Nguyên tắc 3 được thực hiện bằng cách không tính di chuyển nếu nguồn là đích trước đó (nghĩa là chúng ta chỉ chuyển chính sang help1, sau đó chúng ta muốn chuyển từ help1 sang help2), và hơn nữa, chúng ta giảm số lần di chuyển xuống 1 nếu chúng ta đang di chuyển nó trở lại vị trí ban đầu (tức là chính thành help1 rồi help1 thành main). Ngoài ra, nếu ntất cả các di chuyển trước đó đều di chuyển cùng một số nguyên, chúng ta thực sự có thể sắp xếp lại các ndi chuyển đó. Vì vậy, chúng tôi cũng tận dụng điều đó để giảm số lượng di chuyển hơn nữa.

Điều này là hợp lệ vì chúng ta biết tất cả các phần tử trong ngăn xếp chính, vì vậy điều này có thể được hiểu là trong tương lai chúng ta sẽ di chuyển phần tử trở lại, chúng ta không nên thực hiện bước này.

Chạy mẫu (ngăn xếp được hiển thị từ dưới lên trên - vì vậy phần tử đầu tiên là dưới cùng):

Độ dài 1
Di chuyển: 0
Nhiệm vụ: 6
Tối đa: 0 ([1])
Trung bình: 0.000

Độ dài 2
Di chuyển: 60
Nhiệm vụ: 36
Tối đa: 4 ([1, 2])
Trung bình: 1.667

Chiều dài 3
Di chuyển: 1030
Nhiệm vụ: 216
Tối đa: 9 ([2, 3, 1])
Trung bình: 4,769

Độ dài 4
Di chuyển: 11765
Nhiệm vụ: 1296
Tối đa: 19 ([3, 4, 2, 1])
Trung bình: 9.078

Độ dài 5
Di chuyển: 112325
Nhiệm vụ: 7776
Tối đa: 33 ([4, 5, 3, 2, 1])
Trung bình: 14.445

Độ dài 6
Di chuyển: 968015
Nhiệm vụ: 46656
Tối đa: 51 ([5, 6, 4, 3, 2, 1])
Trung bình: 20,748

--------------
Nhìn chung
Di chuyển: 1093195
Nhiệm vụ: 55986
Trung bình: 19,526

Chúng ta có thể thấy rằng trường hợp xấu nhất là khi phần tử lớn nhất được đặt ở dưới cùng thứ hai, trong khi phần còn lại được sắp xếp. Từ trường hợp xấu nhất chúng ta có thể thấy rằng thuật toán là O (n ^ 2).

Số lượng di chuyển rõ ràng là tối thiểu n=1n=2như chúng ta có thể thấy từ kết quả, và tôi tin rằng đây cũng là mức tối thiểu cho các giá trị lớn hơn n, mặc dù tôi không thể chứng minh điều đó.

Giải thích thêm là trong mã.

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

Tôi không nhận được câu hỏi của bạn trên 4. Điều này lưu trữ phần tử lớn thứ hai trên trình trợ giúp thứ hai là gì? Điều đó nghĩa là gì?
Justin

Về cơ bản chỉ cần theo dõi các yếu tố lớn thứ hai trong một biến. Tôi đoán tôi đã lật đổ nó. Tôi nghĩ nó hoàn toàn ổn, haha
cần

"Hàm / chương trình con của bạn có thể kiểm tra bất kỳ ngăn xếp bất cứ lúc nào". Vì vậy, nếu những gì bạn đang làm có thể được thực hiện dễ dàng bằng cách chạy qua các ngăn xếp và tìm vị trí của phần tử lớn thứ hai, thì tốt thôi.
Justin

1
Bằng cách "kiểm tra bất kỳ ngăn xếp bất cứ lúc nào" có nghĩa là tôi có thể đọc ngăn xếp như một mảng mà không tốn bất kỳ động thái nào? Điều đó thay đổi mọi thứ. Về lời giải thích, tôi vẫn đang trong quá trình cập nhật thuật toán (tôi đã nhận được nó thậm chí thấp hơn), vì vậy tôi sẽ cập nhật một khi tôi đã hoàn thành.
ngay

1
Tôi hiểu rồi. Điều đó mở ra một khả năng hoàn toàn mới và chắc chắn chúng ta có thể giảm số lượng di chuyển hơn nữa. Mặc dù vậy, nó sẽ làm cho vấn đề trở nên khó khăn hơn, haha, vì nhiệm vụ lúc đó về cơ bản là "đưa ra một loạt các số nguyên, tìm số lượng di chuyển tối thiểu để sắp xếp nó thành một tòa tháp của Hà Nội". Nếu chúng ta chỉ được phép nhìn thấy đỉnh của ngăn xếp, thì thuật toán của tôi gần đạt mức tối ưu (nếu không phải là tối ưu)
cần

1

Java - 2129090 2083142 di chuyển trên mảng 55986

Các liên kết ideone .

Khung để đảm bảo thuật toán là chính xác:

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

Thuật toán thực tế:

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

Các thử nghiệm:

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 ++ Không đo được các bước di chuyển (chốt: p1, p2, p3) Không biết cách thêm mã C ++ sử dụng STL (vấn đề hình thành). Mất các phần của mã do định dạng thẻ html trong mã.

  1. Nhận n: số phần tử được sắp xếp hàng đầu trong p1
  2. Thực hiện di chuyển hà nội của chúng (n) sang p3 bằng cách sử dụng p2 (giữ thuộc tính được sắp xếp)
  3. Di chuyển các elem tiếp theo (ít nhất 1) theo thứ tự ngược lại từ p1 đến p2.
  4. Hợp nhất dữ liệu di chuyển (n + m) trong p2 & p3 sang 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. Gọi hanoi sort theo cách đệ quy sẽ sắp xếp các phần tử n + m + 1 ít nhất bây giờ.
  6. Dừng khi n = kích thước của p1.
#inc loại 
#inc loại 
sử dụng không gian tên std;
/ ****** / TÌM HIỂU ****** / TÌM KIẾM 
   Hiển thị vector (Có cơ hội cout
****** / TÌM KIẾM ****** / TÌM HIỂU    

void show (vector p, chuỗi tin nhắn)
{
   vector :: lặp nó;
   printf ("% s \ n", dir.c_str ());
   for (it = p.begin (); it & fr, vectơ & inter, vectơ & to, int n) {
   int d1;
   if (n & p1, vectơ & p2, vectơ & p3) {
   int d3, d2, d1;
   int đếm, n;
   bool d2_added;

   d2 = p2.back (); p2.pop_back ();
   // dữ liệu sẽ giảm dần trong p1
   d2_added = sai;
   trong khi (p3.size ()> 0) {
      d3 = p3.back (); p3.pop_back ();
      if (d2_added == false && d2 0) {
      d1 = p1.back (); p1.pop_back ();
      p3.push_back (d1);
      --đếm;
   }
   // Di chuyển trở lại với hà nội từ p3 đến p1
   // sử dụng p2 làm liên
   hà nội (p3, p2, p1, n);
}
/ ****** / TÌM HIỂU ****** / TÌM KIẾM
   Lấy số lượng các yếu tố được sắp xếp đầu tiên
****** / TÌM KIẾM ****** / TÌM HIỂU    
int get_top_sort_count (vector & p1) {
   vector :: lặp nó;
   int trước = 0;
   int n = 1;

   for (it = --p1.end (); it> = p1.begin (); --it) {
     if (nó == --p1.end ()) {
    trước = * nó;
        tiếp tục;
     }
     if (* it & p1, vector & p2, vector & p3) {
    int n, d1;
    n = get_top_sort_count (p1);
    if (n == p1.size ()) {
       trở về;
    }
    hà nội (p1, p2, p3, n);
    p2.push_back (p1.back ());
    p1.pop_back ();
    merge_move (p1, p2, p3);
    hanoi_sort (p1, p2, p3);
}
/ ****** / TÌM HIỂU ****** / TÌM KIẾM    
    Sắp xếp ba lô trên hà nội
****** / TÌM KIẾM ****** / TÌM HIỂU    
int chính (void)
{
  vectơ p1, 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);
  hiển thị (p1, "... Sắp xếp Bef ...");
  hanoi_sort (p1, p2, p3);
  hiển thị (p1, "... Sắp xếp sau ...");
}

Bạn có thể viết mã cho điều này? Mặt khác, đây không phải là một câu trả lời.
Justin

Tôi không thấy hanoi(...)chức năng. Ngoài ra, bạn có 2 #includegiây không biên dịch. Xin vui lòng gửi mã đầy đủ.
Hosch250
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.