递归呼吸优先搜索适用于首次执行但不执行

时间:2016-06-26 01:08:49

标签: c++ visual-studio-2012 recursion console breadth-first-search

CodeReview提供的解决方案在CentOS 7.1上运行良好。我试图将其移植到Windows 7,Visual Studio 2012.通过微小的编辑,允许VS 2012不支持代码编译的C ++ 11部分,并且循环的第一次执行正常工作。测试用例的其余执行失败,每次执行时逐渐变得越来越不正确。

可以在github上找到此问题的代码。

完成计算0 / *问题这里没有问题* /
经过时间:0.202012秒

所有路径搜索的起始点是A3
所有路径搜索的目标点是H4
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有5条生成路径 有323个尝试路径
平均路径长度为4.8
中值路径长度为4
最长的路径是6次移动 最短的路径是4次移动

我认为问题出现在下面列出的其中一个文件中。我已经调试了2天,我可以帮忙。

CRKnightMoves_Cpp2.cpp

/*
 * KnightMoves.cpp
 *
 *      Author: pacmaninbw
 */

#include "stdafx.h"
#include <iostream>
#include <stdexcept>
#include <chrono>
#include <ctime>
#include <algorithm>
#include <functional>
#include <vector>
#include "KnightMoves.h"
#include "KnightMovesImplementation.h"
#include "KMBoardDimensionConstants.h"

double Average(std::vector<double> TestTimes)
{
    double AverageTestTime = 0.0;
    double SumOfTestTimes = 0.0;
    int CountOfTestTimes = 0;

    for (auto TestTimesIter : TestTimes)
    {
        SumOfTestTimes += TestTimesIter;
        CountOfTestTimes++;
    }

    if (CountOfTestTimes) { // Prevent division by zero.
        AverageTestTime = SumOfTestTimes / static_cast<double>(CountOfTestTimes);
    }

    return AverageTestTime;
}

void OutputOverAllStatistics(std::vector<double> TestTimes)
{
    if (TestTimes.size() < 1) {
        std::cout << "No test times to run statistics on!" << std::endl;
        return;
    }

    std::cout << std::endl << "Overall Results" << std::endl;
    std::cout << "The average execution time is " << Average(TestTimes) << " seconds" << std::endl;
    std::nth_element(TestTimes.begin(), TestTimes.begin() + TestTimes.size()/2, TestTimes.end());
    std::cout << "The median execution time is " << TestTimes[TestTimes.size()/2] << " seconds" << std::endl;
    std::nth_element(TestTimes.begin(), TestTimes.begin()+1, TestTimes.end(), std::greater<double>());
    std::cout << "The longest execution time is " << TestTimes[0] << " seconds" << std::endl;
    std::nth_element(TestTimes.begin(), TestTimes.begin()+1, TestTimes.end(), std::less<double>());
    std::cout << "The shortest execution time is " << TestTimes[0] << " seconds" << std::endl;
}

double ExecutionLoop(KMBaseData UserInputData)
{
    KnightMovesImplementation *KnightPathFinder = new KnightMovesImplementation(UserInputData);

    std::chrono::time_point<std::chrono::system_clock> start, end;
    start = std::chrono::system_clock::now();
    KMOutputData OutputData = KnightPathFinder->CalculatePaths();
    end = std::chrono::system_clock::now();

    std::chrono::duration<double> elapsed_seconds = end-start;
    std::time_t end_time = std::chrono::system_clock::to_time_t(end);
    double ElapsedTimeForOutPut = elapsed_seconds.count();
    char ctimebuffer[1024];
    std::cout << "finished computation at " << ctime_s(ctimebuffer, 1024, &end_time) << "\n"
          << "elapsed time: " << ElapsedTimeForOutPut << " Seconds\n" << "\n" << "\n";
    // Don't include output of results in elapsed time calculation
    OutputData.DontShowPathData();
    // OutputData.ShowPathData();
    OutputData.ShowResults();

    delete KnightPathFinder;

    return  ElapsedTimeForOutPut;
}

int LetUserEnterTestCaseNumber(std::vector<KMBaseData> &TestData)
{
    int i = 1;
    int Choice = -1;

    std::cout << "Select the number of the test case you want to run.\n";
    std::cout << "Test Case #" << "\tStart Name" << "\tTarget Name" << "\tBoard Size" << "\tSlicing Method" << "\n";
    for (auto TestCase: TestData) {
        std::cout << i << "\t" << TestCase.m_StartName << "\t" << TestCase.m_TargetName << "\t" << TestCase.m_DimensionOneSide << "\t";
        switch (TestCase.m_LimitationsOnMoves)
        {
            default :
                throw std::runtime_error("LetUserEnterTestCaseNumber : Unknown type of Path Limitation.");
            case DenyByPreviousLocation :
                std::cout << "Can't return to previous location";
                break;
            case DenyByPreviousRowOrColumn:
                std::cout << "Can't return to previous row or column";
                break;
        }
        std::cout << "\n";
        i++;
    }
    std::cout << i << "\tAll of the above except for 13 and 14\n";
    std::cout << ++i <<"\tAll of the above (Go get lunch)\n";

    std::cin >> Choice;

    if (Choice == 15)
    {
        std::vector<KMBaseData> TempTests;
        for (auto TestCase: TestData)
        {
            if ((TestCase.m_DimensionOneSide != MaximumBoardDimension) && (TestCase.m_LimitationsOnMoves != DenyByPreviousLocation))
            {
                TempTests.push_back(TestCase);
            }
        }
        TestData = TempTests;
    }
    return Choice;
}

void InitTestData(std::vector<KMBaseData> &TestData)
{
    KMBaseData TestCases[] = {
        {1,3,"A3",8,4,"H4", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},
        {1,1,"A1",8,8,"H8", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},
        {1,8,"A8",8,1,"H1", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},
        {2,3,"B3",8,4,"H4", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},
        {2,3,"B3",8,8,"H8", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},
        {3,1,"C1",8,4,"H4", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},
        {3,1,"A3",8,8,"H8", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},
        {1,3,"A3",2,5,"B5", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},    // Minimum should be one move
        {8,4,"H4",1,3,"A3", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},
        {4,4,"D4",1,8,"A8", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},
        {4,4,"D4",5,6,"E6", DefaultBoardDimensionOnOneSide, DenyByPreviousRowOrColumn},
        {1,3,"A3",2,5,"B5", 12, DenyByPreviousRowOrColumn},                            // Minimum should be one move
        {1,3,"A3",2,5,"B5", DefaultBoardDimensionOnOneSide,  DenyByPreviousLocation},            // Minimum should be one move
        {1,3,"A3",2,5,"B5", MaximumBoardDimension, DenyByPreviousRowOrColumn}        // Minimum should be one move
    };
    for (int i = 0; i < sizeof(TestCases)/sizeof(KMBaseData); i++) {
        TestData.push_back(TestCases[i]);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    int status = 0;

    std::vector<KMBaseData> TestData;
    std::vector<double> TestTimes;

    try {

        InitTestData(TestData);

        int Choice = LetUserEnterTestCaseNumber(TestData);

        if (Choice < 0)
        {
            return status;
        }

        if (Choice < 15)
        {
            ExecutionLoop(TestData[Choice-1]);
        }
        else
        {
            for (auto TestDataIter: TestData) {
                TestTimes.push_back(ExecutionLoop(TestDataIter));
            }
        }

        OutputOverAllStatistics(TestTimes);

        return status;
    }
    catch(std::runtime_error &e) {
        std::cerr << "A fatal error occurred in KnightMoves: ";
        std::cerr << e.what()  << std::endl;
        status = 1;
    }
    catch(std::runtime_error *e) {
        std::cerr << "A fatal error occurred in KnightMoves: ";
        std::cerr << e->what()  << std::endl;
        status = 1;
    }
    catch(...) {
        std::cerr << "An unknown fatal error occurred in KnightMoves." << std::endl;
        status = 1;
    }
    return status;
}

KnightMovesImplementation.h

#pragma once
/*
 * KnightMovesImplementation.h
 *
 *  Created on: Mar 18, 2016
 *  Modified on: June 20, 2016
 *      Author: pacmaninbw
 *
 *      This class provides the search for all the paths a Knight on a chess
 *      board can take from the point of origin to the destination. It
 *      implements a modified Knights Tour. The classic knights tour problem
 *      is to visit every location on the chess board without returning to a
 *      previous location. That is a single path for the knight. This
 *      implementation returns all possible paths from point a to point b.
 *      The actual implementation is documented in the CPP file because it
 *      can be changed. This head file provides the public interface which
 *      should not be changed. The public interface may be moved to a
 *      super class in the future.
 */

#ifndef KNIGHTMOVESIMPLEMENTATION_H_
#define KNIGHTMOVESIMPLEMENTATION_H_

#include "KMPath.h"
#include "KMOutputData.h"
#include "KMMoveFilters.h"

class KnightMovesImplementation {
private:
    KMBoardLocation m_PointOfOrigin;
    KMBoardLocation m_Destination;
    unsigned int m_SingleSideBoardDimension;
    KnightMovesMethodLimitations m_PathLimitations;
    KMOutputData *m_Results;
    KMMoveFilters *m_MoveFilters;
    KMPath *m_Path;

protected:
    bool CalculatePath(KMMove CurrentMove);        // Recursive function
    void InitPointOfOrigin(KMBaseData UserData);
    void InitDestination(KMBaseData UserData);

public:
    KnightMovesImplementation(KMBaseData UserData);
    virtual ~KnightMovesImplementation(void);
    KMOutputData CalculatePaths();
};

#endif /* KNIGHTMOVESIMPLEMENTATION_H_ */

KnightsImplementation.cpp

/*
 * KnightMovesImplementation.cpp
 *
 *  Created on: Mar 18, 2016
 *  Modified on: June 21, 2016
 *  Commented on: June 24, 2016
 *      Author: pacmaninbw
 *
 *      This class implements the search for all possible paths for a Knight
 *      on a chess board from one particular square on the board to another
 *      particular square on the board.
 *
 *      The current implementation is a Recursive Breadth First Search. Conceptually
 *      the algorithm implements a B+ tree with a maximum of 8 possible branches
 *      at each level. The root of the tree is the point of origin. A particular
 *      path terminates in a leaf. A leaf is the result of either reaching the
 *      destination, or reaching a point where there are no more branches to
 *      traverse.
 *
 *      The m_Path variable is used as a stack within the search.
 *
 *      The public interface CalculatePaths establishes the root and creates
 *      the first level of branching. The protected interface CalculatePath
 *      performs the recursive depth first search, however, the
 *      m_MoveFilters.GetPossibleMoves() function it calls performs a breadth
 *      first search of the current level.
 *
 */

#include "stdafx.h"
#include "KnightMoves.h"
#include "KnightMovesImplementation.h"
#include "KMBoardDimensionConstants.h"

KnightMovesImplementation::~KnightMovesImplementation(void)
{
    delete m_MoveFilters;
    delete m_Results;
    delete m_Path;
}

KnightMovesImplementation::KnightMovesImplementation(KMBaseData UserInputData)
 : m_SingleSideBoardDimension(UserInputData.m_DimensionOneSide),
   m_PathLimitations(UserInputData.m_LimitationsOnMoves)
{
    InitPointOfOrigin(UserInputData);
    InitDestination(UserInputData);
    m_Path = new KMPath;
    m_MoveFilters = new KMMoveFilters(static_cast<unsigned int>(UserInputData.m_DimensionOneSide), UserInputData.m_LimitationsOnMoves);
    m_Results = new KMOutputData(m_PointOfOrigin, m_Destination, m_SingleSideBoardDimension, m_PathLimitations);
}

void KnightMovesImplementation::InitPointOfOrigin(KMBaseData UserInputData)
{
    m_PointOfOrigin.SetRow(UserInputData.m_StartRow);
    m_PointOfOrigin.SetColumn(UserInputData.m_StartColumn);
    m_PointOfOrigin.SetName(UserInputData.m_StartName);
    m_PointOfOrigin.SetBoardDimension(m_SingleSideBoardDimension);
}

void KnightMovesImplementation::InitDestination(KMBaseData UserInputData)
{
    m_Destination.SetRow(UserInputData.m_TargetRow);
    m_Destination.SetColumn(UserInputData.m_TargetColumn);
    m_Destination.SetName(UserInputData.m_TargetName);
    m_Destination.SetBoardDimension(m_SingleSideBoardDimension);
}

KMOutputData KnightMovesImplementation::CalculatePaths()
{
    KMRandomAccessMoveCollection PossibleFirstMoves = m_MoveFilters->GetPossibleMoves(m_PointOfOrigin);

    if (PossibleFirstMoves.empty())
    {
        std::cerr << "No Possible Moves in KnightMovesImplementation::CalculatePaths" << std::endl;
    }
    else
    {
        for (auto CurrentMoveIter : PossibleFirstMoves)
        {
            KMMove CurrentMove = CurrentMoveIter;
            CurrentMove.SetOriginCalculateDestination(m_PointOfOrigin);
            if (CurrentMove.IsValid()) {
                CalculatePath(CurrentMove);
            }
        }
    }
    return *m_Results;
}

bool KnightMovesImplementation::CalculatePath(KMMove CurrentMove)
    {
    bool CompletedSearch = false;
    KMBoardLocation CurrentLocation = CurrentMove.GetNextLocation();
m_Path->AddMoveToPath(CurrentMove);
    m_MoveFilters->PushVisited(CurrentLocation);

    if (CurrentLocation == m_Destination)
    {
        m_Results->AddPath(*m_Path);
        CompletedSearch =  true;
        m_Results->IncrementAttemptedPaths();
    }
    else
    {
        if (CurrentMove.IsValid())
        {
            KMRandomAccessMoveCollection PossibleMoves = m_MoveFilters->GetPossibleMoves(CurrentLocation);
            if (!PossibleMoves.empty())
            {
                for (auto NextMove : PossibleMoves)
                {
                    CalculatePath(NextMove);
                }
            }
            else
            {
                // No more moves to test, record the attempted path
                m_Results->IncrementAttemptedPaths();
            }
        }
        else
        {
            // There is a logic error if we get here.
            std::cerr << "In KnightMovesImplementation::CalculatePath CurrentMove Not Valid" << std::endl;
        }
    }

    m_Path->RemoveLastMove();
    m_MoveFilters->PopVisited();
    return CompletedSearch;
}

KMMoveFilters.h

#pragma once
/*
 * KMMoveFilters.h
 *
 *  Created on: Jun 20, 2016
 *      Author: pacmaninbw
 *
 *      This class provides all the possible Knight moves for a specified location
 *      on the chess board. In the center of the chess board there are 8 possible
 *      moves. In the middle of the edge on the chess board there are 4 possible
 *      moves. In a corner of the chess board there are 2 possible moves. The
 *      location on the board provides the first filter.
 *      Slicing is used to allow the program to complete in a reasonable finite
 *      amount of time. The slicing method can be varied, the default slicing
 *      method is the knight can't return to any row or column it has previously
 *      visited. The slicing is the second filter.
 */

#ifndef KMMOVEFILTERS_H_
#define KMMOVEFILTERS_H_

#include <vector>
#include "KnightMoves.h"
#include "KMMove.h"

class KMMoveFilters {
private:
    std::vector<KMBoardLocation> m_VisitedLocations;
    std::vector<unsigned int> m_VisitedRows;
    std::vector<unsigned int> m_VisitedColumns;
    unsigned int m_SingleSideBoardDimension;
    KnightMovesMethodLimitations m_PathLimitations;
    static KMRandomAccessMoveCollection AllPossibleMoves;
    // The 8 possible moves the knight can make.
    static KMMove Left1Up2;
    static KMMove Left2Up1;
    static KMMove Left2Down1;
    static KMMove Left1Down2;
    static KMMove Right1Up2;
    static KMMove Right2Up1;
    static KMMove Right2Down1;
    static KMMove Right1Down2;

protected:
    bool IsNotPreviouslyVisited(KMMove Move) const { return IsNotPreviouslyVisited(Move.GetNextLocation()); };
    bool IsNotPreviouslyVisited(KMBoardLocation Destination) const;

public:
    KMMoveFilters(void);
    KMMoveFilters(unsigned int BoardDimension, KnightMovesMethodLimitations SlicingMethod);
    void ResetFilters(unsigned int BoardDimension, KnightMovesMethodLimitations SlicingMethod) {m_SingleSideBoardDimension = BoardDimension; m_PathLimitations = SlicingMethod; }
    virtual ~KMMoveFilters(void);
    void PushVisited(KMBoardLocation Location);
    void PopVisited();
    KMRandomAccessMoveCollection GetPossibleMoves(const KMBoardLocation CurrentLocation) const;
};

KMMoveFilters.cpp

/*
 * KMMoveFilters.cpp
 *
 *  Created on: Jun 20, 2016
 *      Author: pacmaninbw
 */

#include "stdafx.h"
#include <stdexcept>
#include <algorithm>
#include "KMBoardDimensionConstants.h"
#include "KMMoveFilters.h"

KMMoveFilters::~KMMoveFilters(void)
{
}

KMMoveFilters::KMMoveFilters(void)
 : m_SingleSideBoardDimension(DefaultBoardDimensionOnOneSide),
   m_PathLimitations(DenyByPreviousRowOrColumn)
{
    AllPossibleMoves.push_back(Left1Up2);
    AllPossibleMoves.push_back(Left2Up1);
    AllPossibleMoves.push_back(Left2Down1);
    AllPossibleMoves.push_back(Left1Down2);
    AllPossibleMoves.push_back(Right1Up2);
    AllPossibleMoves.push_back(Right2Up1);
    AllPossibleMoves.push_back(Right2Down1);
    AllPossibleMoves.push_back(Right1Down2);
}

KMMoveFilters::KMMoveFilters(unsigned int BoardDimension, KnightMovesMethodLimitations SlicingMethod)
: m_SingleSideBoardDimension(BoardDimension), m_PathLimitations(SlicingMethod)
{
    AllPossibleMoves.push_back(Left1Up2);
    AllPossibleMoves.push_back(Left2Up1);
    AllPossibleMoves.push_back(Left2Down1);
    AllPossibleMoves.push_back(Left1Down2);
    AllPossibleMoves.push_back(Right1Up2);
    AllPossibleMoves.push_back(Right2Up1);
    AllPossibleMoves.push_back(Right2Down1);
    AllPossibleMoves.push_back(Right1Down2);
}

const int Left1 = -1;
const int Left2 = -2;
const int Down1 = -1;
const int Down2 = -2;
const int Right1 = 1;
const int Right2 = 2;
const int Up1 = 1;
const int Up2 = 2;

KMMove KMMoveFilters::Left1Up2(Left1, Up2, DefaultBoardDimensionOnOneSide);
KMMove KMMoveFilters::Left2Up1(Left2, Up1, DefaultBoardDimensionOnOneSide);
KMMove KMMoveFilters::Left2Down1(Left2, Down1, DefaultBoardDimensionOnOneSide);
KMMove KMMoveFilters::Left1Down2(Left1, Down2, DefaultBoardDimensionOnOneSide);
KMMove KMMoveFilters::Right1Up2(Right1, Up2, DefaultBoardDimensionOnOneSide);
KMMove KMMoveFilters::Right2Up1(Right2, Up1, DefaultBoardDimensionOnOneSide);
KMMove KMMoveFilters::Right2Down1(Right2, Down1, DefaultBoardDimensionOnOneSide);
KMMove KMMoveFilters::Right1Down2(Right1, Down2, DefaultBoardDimensionOnOneSide);

KMRandomAccessMoveCollection KMMoveFilters::AllPossibleMoves;

KMRandomAccessMoveCollection KMMoveFilters::GetPossibleMoves(const KMBoardLocation CurrentLocation) const
{
    KMRandomAccessMoveCollection PossibleMoves;
    for (auto PossibeMove : AllPossibleMoves) {
        KMMove *TempMove = new KMMove(PossibeMove);
        TempMove->SetBoardDimension(m_SingleSideBoardDimension);
        TempMove->SetOriginCalculateDestination(CurrentLocation);
        if ((TempMove->IsValid()) && (IsNotPreviouslyVisited(*TempMove))) {
            PossibleMoves.push_back(*TempMove);
        }
    }
    return PossibleMoves;
}

bool KMMoveFilters::IsNotPreviouslyVisited(KMBoardLocation PossibleDestination) const
{
    bool NotPrevioslyVisited = true;

    if (!m_VisitedLocations.empty()) {    // This is always a test, we can't move backwards
        if (std::find(m_VisitedLocations.begin(), m_VisitedLocations.end(), PossibleDestination)
            != m_VisitedLocations.end()) {
            NotPrevioslyVisited = false;
        }
    }

    switch (m_PathLimitations) {
    default :
        throw std::runtime_error("KMPath::CheckMoveAgainstPreviousLocations : Unknown type of Path Limitation.");
    case DenyByPreviousLocation :
        // Always tested above.
        break;
    case DenyByPreviousRowOrColumn:
        if ((!m_VisitedRows.empty()) && (!m_VisitedColumns.empty())) {
            unsigned int PossibleRow = PossibleDestination.GetRow();
            if (std::find(m_VisitedRows.begin(), m_VisitedRows.end(), PossibleRow) != m_VisitedRows.end()) {
                NotPrevioslyVisited = false;
                break;
            }
            unsigned int PossibleColum = PossibleDestination.GetColumn();
            if (std::find(m_VisitedColumns.begin(), m_VisitedColumns.end(), PossibleColum) != m_VisitedColumns.end()) {
                NotPrevioslyVisited = false;
            }
        }
        break;
    }

    return NotPrevioslyVisited;
}

void KMMoveFilters::PushVisited(KMBoardLocation Location)
{
    m_VisitedRows.push_back(Location.GetRow());
    m_VisitedColumns.push_back(Location.GetColumn());
    m_VisitedLocations.push_back(Location);
}

void KMMoveFilters::PopVisited()
{
    m_VisitedRows.pop_back();
    m_VisitedColumns.pop_back();
    m_VisitedLocations.pop_back();
}

1 个答案:

答案 0 :(得分:0)

问题是AllPossibleMoves的静态声明,GetPossibleMoves中的内存泄漏可能是问题的另一个原因。在CentOS C ++ 11版本中,AllPossibleMoves被声明为静态const,并且未在构造函数中初始化,它在外部初始化,因为它的每个成员移动都是。这不能在Visual Studio 2012 C ++中编译。由于原始版本中的执行时间原因,AllPossibleMoves被声明为静态const。

我对结果感到失望,因为这比使用用g ++编译的C ++ 11的CentOS版本慢得多。我运行它的计算机比CentOS计算机新2年,并且有8GB内存和i7处理器。

首先我展示工作代码,然后我展示程序的输出。

解决问题的最终代码是:

<强> KMMoveFilters.h

#pragma once
/*
 * KMMoveFilters.h
 *
 *  Created on: Jun 20, 2016
 *      Author: pacmaninbw
 *
 *      This class provides all the possible Knight moves for a specified location
 *      on the chess board. In the center of the chess board there are 8 possible
 *      moves. In the middle of the edge on the chess board there are 4 possible
 *      moves. In a corner of the chess board there are 2 possible moves. The
 *      location on the board provides the first filter.
 *      Slicing is used to allow the program to complete in a reasonable finite
 *      amount of time. The slicing method can be varied, the default slicing
 *      method is the knight can't return to any row or column it has previously
 *      visited. The slicing is the second filter.
 */

#ifndef KMMOVEFILTERS_H_
#define KMMOVEFILTERS_H_

#include <vector>
#include "KnightMoves.h"
#include "KMMove.h"

class KMMoveFilters {
private:
    std::vector<KMBoardLocation> m_VisitedLocations;
    std::vector<unsigned int> m_VisitedRows;
    std::vector<unsigned int> m_VisitedColumns;
    unsigned int m_SingleSideBoardDimension;
    KnightMovesMethodLimitations m_PathLimitations;
    KMRandomAccessMoveCollection AllPossibleMoves;
    // The 8 possible moves the knight can make.
    static KMMove Left1Up2;
    static KMMove Left2Up1;
    static KMMove Left2Down1;
    static KMMove Left1Down2;
    static KMMove Right1Up2;
    static KMMove Right2Up1;
    static KMMove Right2Down1;
    static KMMove Right1Down2;

protected:
    bool IsNotPreviouslyVisited(KMMove Move) const { return IsNotPreviouslyVisited(Move.GetNextLocation()); }
    bool IsNotPreviouslyVisited(KMBoardLocation Destination) const;

public:
    KMMoveFilters(void);
    KMMoveFilters(unsigned int BoardDimension, KnightMovesMethodLimitations SlicingMethod);
    void ResetFilters(unsigned int BoardDimension, KnightMovesMethodLimitations SlicingMethod) {m_SingleSideBoardDimension = BoardDimension; m_PathLimitations = SlicingMethod; }
    virtual ~KMMoveFilters(void);
    void PushVisited(KMBoardLocation Location);
    void PopVisited();
    KMRandomAccessMoveCollection GetPossibleMoves(const KMBoardLocation CurrentLocation) const;
};

#endif /* KMMOVEFILTERS_H_ */

仅限KMMoveFilters.cpp中的更改

KMRandomAccessMoveCollection KMMoveFilters::GetPossibleMoves(const KMBoardLocation CurrentLocation) const
{
    KMRandomAccessMoveCollection SafeAllPossibleMoves = AllPossibleMoves;
    KMRandomAccessMoveCollection PossibleMoves;
    for (auto PossibleMove : SafeAllPossibleMoves) {
        PossibleMove.SetBoardDimension(m_SingleSideBoardDimension);
        PossibleMove.SetOriginCalculateDestination(CurrentLocation);
        if ((PossibleMove.IsValid()) && (IsNotPreviouslyVisited(PossibleMove))) {
            PossibleMoves.push_back(PossibleMove);
        }
    }
    return PossibleMoves;
}

结果输出

选择要运行的测试用例的编号 测试用例#开始名称目标名称板尺寸切片方法
1 A3 H4 8无法返回上一行或列
2 A1 H8 8无法返回上一行或列
3 A8 H1 8无法返回上一行或列
4 B3 H4 8无法返回上一行或列
5 B3 H8 8无法返回上一行或列
6 C1 H4 8无法返回上一行或列
7 A3 H8 8无法返回上一行或列
8 A3 B5 8无法返回上一行或列
9 H4 A3 8无法返回上一行或列
10 D4 A8 8无法返回上一行或列
11 D4 E6 8无法返回上一行或列
12 A3 B5 12无法返回上一行或列
13 A3 B5 8无法返回上一个位置
14 A3 B5 26无法返回上一行或列
15以上所有以上除13和14外 16以上所有(去吃午餐)

完成计算0 经过时间:0.209012秒

所有路径搜索的起始点是A3
所有路径搜索的目标点是H4
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有5条生成路径 有323个尝试路径
平均路径长度为4.8
中值路径长度为4
最长的路径是6次移动 最短的路径是4次移动

完成计算0 已用时间:0.0930054秒

所有路径搜索的起始点是A1
所有路径搜索的目标点是H8
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有22条生成路径 有160个尝试路径
平均路径长度为6.36364
中值路径长度为6
最长的路径是8次移动 最短路径是6次移动

完成计算0 已用时间:0.0950054秒

所有路径搜索的起点是A8
所有路径搜索的目标点都是H1
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有22条生成路径 有160个尝试路径
平均路径长度为6.36364
中值路径长度为6
最长的路径是8次移动 最短路径是6次移动

完成计算0 经过时间:0.248014秒

所有路径搜索的起点都是B3
所有路径搜索的目标点是H4
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有8条生成路径 有446个尝试路径
平均路径长度为5
中值路径长度为5
最长的路径是7次移动 最短的路径是3次移动

完成计算0 已用时间:0.251014秒

所有路径搜索的起点都是B3
所有路径搜索的目标点是H8
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有39条生成路径 有447个尝试路径
平均路径长度为6.23077
中值路径长度为7
最长的路径是7次移动 最短路径是5次移动

完成计算0 已用时间:0.17801秒

所有路径搜索的起始点是C1
所有路径搜索的目标点是H4
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有7条生成路径 有324个尝试路径
平均路径长度为4.85714
中值路径长度为4
最长的路径是6次移动 最短的路径是4次移动

完成计算0 经过时间:0.18201秒

所有路径搜索的起始点是A3
所有路径搜索的目标点是H8
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有36条结果路径 有324个尝试路径
平均路径长度为6
中值路径长度为6
最长的路径是8次移动 最短的路径是4次移动

完成计算0 已用时间:0.131008秒

所有路径搜索的起始点是A3
所有路径搜索的目标点是B5
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有6条生成路径 有243个尝试路径
平均路径长度为3
中值路径长度为3
最长的路径是5次移动 最短路径是1次移动

完成计算0 已用时间:0.17301秒

所有路径搜索的起点都是H4
所有路径搜索的目标点是A3
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 有12条结果路径
有318个尝试路径
平均路径长度为5.66667
中值路径长度为6
最长的路径是8次移动 最短的路径是4次移动

完成计算0 经过时间:0.332019秒

所有路径搜索的起点是D4
所有路径搜索的目标点是A8
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 有24条结果路径
有602个尝试路径
平均路径长度为5.25
中值路径长度为5
最长的路径是7次移动 最短的路径是3次移动

完成计算0 经过时间:0.266015秒

所有路径搜索的起点是D4
所有路径搜索的目标点是E6
电路板每个边缘的方格数为8
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有21条结果路径 有487条尝试路径
平均路径长度为4.14286
中值路径长度为5
最长的路径是7次移动 最短路径是1次移动

完成计算0 已用时间:1.86411秒

所有路径搜索的起始点是A3
所有路径搜索的目标点是B5
电路板每个边缘的方格数为12
用于进一步限制搜索的切片方法是不会重复访问任何行或列 共有6条生成路径 有3440个尝试路径
平均路径长度为3
中值路径长度为3
最长的路径是5次移动 最短路径是1次移动

总体结果
平均执行时间为0.335186秒 中位执行时间为0.209012秒
最长执行时间为1.86411秒
最短执行时间为0.0930054秒

优化版本的总体结果。

总体结果
平均执行时间为0.00266682秒
中位执行时间为0.0020001秒
最长执行时间为0.0140008秒
最短的执行时间是0.001秒

CentOS版本整体结果
 平均执行时间为0.00195405秒  中位执行时间为0.00103346秒
 最长执行时间为0.00130368秒
 最短执行时间为0.000716237秒