ハノイの塔


21

ハノイの塔スタイルの整数のリストをソートする関数/サブルーチンを記述します。

整数のスタックが与えられます。これがメインスタックです。

また、さらに2つのヘルパースタックが提供されます。ただし、これらのヘルパースタックには固有のプロパティがあります。すべての要素は、その下の要素よりも小さいか、同じサイズでなければなりません。メインスタックにはこのような制限はありません。

メインスタックを所定の位置に並べ替え、最大の整数をその下に配置する必要があります。関数/サブルーチンは、スタックのソートで行われた移動の数(または同等の)を返します。
注:メインスタックを所定の場所に並べ替える必要があります。別のスタックに並べ替えて、その答えを呼び出す必要はありません。ただし、何らかの理由でそうできない場合は、可変スタックをシミュレートできますが、これはハノイの塔の並べ替えであることに注意してください。ペグは3つのみで、順序付けられていないペグは1つだけです。

関数/サブルーチンはいつでもスタックを検査できますが、ポップとプッシュによってのみ移動できます。シングルムーブとは、あるスタックから別のスタックにプッシュされるポップです。

最初の6つの自然数の順列ごとに関数/サブルーチンをテストします。言い換えれば、関数/サブルーチンをテストします{1},{2},...,{6},{1,1},{1,2},...,{1,6},{2,1},...(これは合計または可能性があるはずです(数学を修正してくれたハワードに感謝します))。要素を最小の回数だけ移動する関数/サブルーチンが勝ちます。61+62+...+6655986


@JanDvorakこれは、テストに関する一種のアイデアでした。プログラマが関数を46656回実行する必要がある場合、なぜ出力をそれほど長く待たなければならないのでしょうか?または、この種のことを制限する別の良い方法はありますか?
ジャスティン14

どういうわけか私はブラインドザップチャレンジが好きです。「スタックxからスタックyへの移動」としか言えず、移動が成功したかどうかを確認できます。成功した場合は、課金されます。ボーナスポイントは、移動の失敗は、例外をスローすることによって示され、正しく戻ることです。
ジョンドヴォルザーク14

3
指定した「順列」のリストには6**1+6**2+...+6**6=55986要素が含まれています。
ハワード14

1
@ m.buettner区別は、これがデカルト積sの要素であるということです1から6回。私はおそらくこれを「ヌル集合を除く最初の6つの自然数のべき集合の各要素の順列の集合」と呼ぶでしょう。
ジャスティン14

1
パワーセットを除く@Quincunxは、繰り返し番号を持つセットを含みません。;)...しかし、セットの要素についてすべて明確である限り、とにかくこれをあまりにも真剣に受け止めるべきではないと思います。
マーティンエンダー14

回答:


4

Java-最適なソリューション(1080544の動き)

このソリューションは、ターゲットから逆方向に最短パスツリーを構築し、初期状態からターゲットまでパスをトラバースします。速度を改善する余地はたくさんありますが、それでも約1分で55986の問題がすべて解決します。

アルゴリズムが正しく実装されていると仮定すると、これが理論的に最良のソリューションになるはずです。

import java.util.*;

public class HanoiSort {

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

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

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

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

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

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

}

「最短パスツリー」の意味をもっと詳しく説明してください
ちょうど半分14年

とにかくあなたの答えに感謝します、それは私が経験則だけを使用してほぼ最適な解決策を達成できることを嬉しく思います:)
ちょうど半分14年

最短パスツリーは、各ノード/状態に1つの「次の」エッジがあり、ルート/ターゲット状態(=ソートされたメインスタック)までの最短距離を持つノード/状態につながるツリーです。同じ距離以上の他の候補の次のノードがあるかもしれませんが、ルートに近いものはありません。この問題では、最短経路ツリーのすべてのエッジの距離は1です。これは、ある状態から別の状態に移行するのに1つの動きが必要だからです。基本的に、完全な最短パスツリーには、同じターゲット状態を持つすべての状態のソリューションが含まれます。
MrBackend 14年

@justhalf私のコメントであなたに言及するのを忘れました。ところで、en.wikipedia.org
wiki / Retrograde_analysis

5

C-55986入力の場合は2547172

ここには改善の余地がたくさんあります。私自身の健全性のために、これを単純化して、各スタックの最上位要素のみを検査できるようにしました。この自主規制を解除すると、事前に最終的な順序を決定し、それを達成するために必要な移動の数を最小限にしようとするなどの最適化が可能になります。説得力のある例は、メインスタックが既にソートされている場合、私の実装では最悪のケースの動作になることです。

アルゴリズム:

  1. 両方の補助スタックを埋めます(ここで最適化の余地があり、場合によっては何らかのピボットに基づいてどのスタックに割り当てるか)。
  2. 補助スタックをソートしてメインスタックに戻します。
  3. メインスタックがソートされるまで(ただし逆に)1-2を繰り返します。
  4. メインスタックを反転します(最適化の余地を増やし、同じ要素を繰り返しシャッフルします)。

分析:

  • 追加のスペースの複雑さはO(n)(2つの補助スタックの場合)であり、これは問題の要件であるため良好です。
  • 私のカウントでは、時間の複雑さはO(n ^ 2)です。訂正は大歓迎です。

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

#define SIZE 6

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

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

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

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

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

int permutations;

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

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

int save[SIZE + 1];

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

int total;

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

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

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

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

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

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

4

Python、3983838 3912258は55986入力を超えて移動します

これは非常に非効率的です。

OPがそれらのすべてのケースまたは特定の他のケースのいずれであるかを明確にした後、移動の総数を追加します。


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

from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

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

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

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


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

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

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

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

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

        total_moves += 1

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

        return True
        # else:
        #   return False


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

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

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

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



    return total_moves


# with PyCallGraph( output= GraphvizOutput() ):


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

説明

なにか、コメントはあなたにとって十分ではありませんか?


OPへの注意:このコードゴルフを作ってくれてありがとう。


[code-golf]以外のチャレンジを行うもっと面白い方法があれば、そうするべきだと思います。
ジャスティン14

[1,1,2]の場合、これは失敗します。Pythonでは、リストを考慮すると、2 [2,1,1]を取得する方法、[2,1,1].index(1)つまり上位から開始する方法がありますか?
user80551 14

@Quincunxいいえ、[2,1,1,3,5].index(1)==2代わりに1
user80551

えー、list.index(data)アイテムのインデックスを返すdataではlist。私はしていないのインデックスを知っているdata、すなわち1
user80551

ハックは以下のようになりますlen(list)-(list[::-1].index(1))
user80551

2

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

主な方法はmain_to_help_best、選択した要素をメインスタックからヘルパースタックに移動することです。これには、everything指定されたにすべてを移動するかどうかを定義するフラグがありますdestination。またはdestination、他のヘルパーの残りの部分を最大の部分のみに保持するかどうかを定義します。

dsthelperの使用に移行すると仮定するとhelper、関数はおおよそ次のように説明できます。

  1. 最大の要素の位置を見つける
  2. 一番大きい要素の上にあるものをすべてhelper再帰的に移動します
  3. 最大の要素を dst
  4. helperからメインにプッシュバック
  5. 最大要素が収まるまで2〜4を繰り返します dst
  6. a。everythingが設定されている場合、mainの要素をdst
    b に再帰的に移動します。それ以外の場合、mainの要素を再帰的に移動しますhelper

(メインソートアルゴリズムsort2私のコードで)、その後呼び出しますmain_to_help_besteverythingにセットFalseした後、メインの最大の要素の背中を動かすには、それはソート維持、メインにヘルパー背面からすべてを移動します。

コードにコメントとして埋め込まれた詳細な説明。

基本的に私が使用した原則は次のとおりです。

  1. 最大の要素を含むヘルパーを1つ保持します
  2. 他の要素を含めるために別のヘルパーを保持します
  3. 可能な限り不要な動きをしないでください

原則3は、移動元が前の移動先である場合に移動をカウントしないことで実装されます(つまり、mainをhelp1に移動した後、help1からhelp2に移動したい場合)。さらに、移動数を1減らす元の位置に戻す(つまり、mainをhelp1に、次にhelp1をmain)。また、前のn動きがすべて同じ整数で移動している場合、実際にそれらのn動きを並べ替えることができます。そのため、これを利用して移動数をさらに削減します。

メインスタック内のすべての要素を知っているため、これは有効です。したがって、これは、将来要素を戻すことを理解していると解釈できるため、この移動は行わないでください。

サンプルの実行(スタックは下から上に表示されるため、最初の要素は下に表示されます):

長さ1
移動:0
タスク:6
最大:0([1])
平均:0.000

長さ2
移動:60
タスク:36
最大:4([1、2])
平均:1.667

長さ3
移動:1030
タスク:216
最大:9([2、3、1])
平均:4.769

長さ4
移動:11765
タスク:1296
最大:19([3、4、2、1])
平均:9.078

長さ5
移動:112325
タスク:7776
最大:33([4、5、3、2、1])
平均:14.445

長さ6
移動:968015
タスク:46656
最大:51([5、6、4、3、2、1])
平均:20.748

--------------
全体
移動:1093195
タスク:55986
平均:19.526

最悪のケースは、最大の要素が2番目の下部に配置され、残りがソートされている場合です。最悪の場合から、アルゴリズムはO(n ^ 2)であることがわかります。

動きの数は明らかに最小でn=1ありn=2、結果からわかるように、これはの値が大きい場合も最小であると信じていますがn、それを証明することはできません。

詳細な説明はコードにあります。

from itertools import product

DEBUG = False

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

if __name__ == '__main__':
    main()

私はあなたの質問を受け取りません4。これは、2番目のヘルパーに2番目に大きい要素を格納するものですか?どういう意味ですか?
ジャスティン

基本的には、変数の2番目に大きい要素を追跡するだけです。私はそれを考えすぎていたと思います。私はそれが完全に大丈夫だと思います、
ハハ

「関数/サブルーチンはいつでもスタックを検査できます」。したがって、スタックを実行して2番目に大きい要素の場所を見つけることで、あなたがしていることを簡単に行えるなら、それは問題ありません。
ジャスティン

1
「いつでもスタックを検査する」とは、移動を消費せずにスタックを配列のように読み取ることができるということですか?それがすべてを変えます。説明に関しては、私はまだアルゴリズムを更新している最中であるため(さらに低くなりました)、完了したら更新します。
14年

1
そうですか。これによりまったく新しい可能性が開かれ、間違いなくムーブの数をさらに減らすことができます。ただし、タスクは本質的に「整数の配列が与えられ、最小数の移動を見つけてハノイの塔に並べ替える」ため、問題はさらに難しくなります。スタックの最上部のみを表示できる場合、アルゴリズムは最適に近くなります(最適でない場合)
14年

1

Java- 2129090 2083142は55986アレイで移動します

ideoneリンク

アルゴリズムが正しいことを確認するフレームワーク:

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

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

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

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

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

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

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

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

実際のアルゴリズム:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

テストケース:

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

-1

C / C ++移動を測定しませんでした(pegs:p1、p2、p3)STL(formatting issue)を使用するC ++コードを追加する方法がわかりません。コード内のhtmlタグ形式が原因でコードの一部が失われます。

  1. nを取得:p1の上位のソートされた要素の数
  2. p2を使用してそれらを(n)からp3にハノイ移動します(ソートされたプロパティを保持します)
  3. 次の要素(少なくとも1)をp1からp2に逆順に移動します。
  4. p2およびp3の移動(n + m)データをp1にマージします。

         4.1 merge sort p2 & P3 (reverse) to p1
         4.2 move (n+m) to p3
         4.3 hanoi p3 to p1 using p2
    
  5. 少なくともn + m + 1個の要素を並べ替えるhanoi sortを再帰的に呼び出します。
  6. n = p1のサイズのときに停止します。
#含める 
#含める 
名前空間stdを使用します。
/ ************************************************* ***************************** 
   ベクターを表示する
************************************************** ***************************** /    

void show(vector p、string msg)
{
   vector :: iterator it;
   printf( "%s \ n"、msg.c_str());
   for(it = p.begin(); it&fr、vector&inter、vector&to、int n){
   int d1;
   if(n&p1、vector&p2、vector&p3){
   int d3、d2、d1;
   int count、n;
   bool d2_added;

   d2 = p2.back(); p2.pop_back();
   //データはp1で降順になります
   d2_added = false;
   while(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);
      - カウント;
   }
   //ハノイでp3からp1に戻る
   // interとしてp2を使用します
   hanoi(p3、p2、p1、n);
}
/ ************************************************* ******************************
   最初にソートされた要素の数を取得します
************************************************** ***************************** /    
int get_top_sorted_count(vector&p1){
   vector :: iterator it;
   int prev = 0;
   int n = 1;

   for(it = --p1.end(); it> = p1.begin(); --it){
     if(it == --p1.end()){
    prev = * it;
        持続する;
     }
     if(* it&p1、vector&p2、vector&p3){
    int n、d1;
    n = get_top_sorted_count(p1);
    if(n == p1.size()){
       戻り;
    }
    hanoi(p1、p2、p3、n);
    p2.push_back(p1.back());
    p1.pop_back();
    merge_move(p1、p2、p3);
    hanoi_sort(p1、p2、p3);
}
/ ************************************************* ******************************    
    ハノイに基づいたソート
************************************************** ***************************** /    
int main(void)
{
  ベクトル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);
  show(p1、 "... Bef Sort ...");
  hanoi_sort(p1、p2、p3);
  show(p1、 "... Aft Sort ...");
}

このためのコードを書くことができますか?そうでなければ、これは答えではありません。
ジャスティン

hanoi(...)機能が表示されません。また、#includeコンパイルしない2 があります。完全なコードを投稿してください。
Hosch250
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.