8

私は現在、巡回セールスマンの問題を解決するためのシミュレーテッド アニーリング コードを書いていますが、txt ファイルから読み取ったデータを保存して使用する際に問題が発生しました。ファイルの各行と列は各都市を表し、2 つの異なる都市間の距離は 15 x 15 の行列として保存されます。

0.0 5.0 5.0 6.0 7.0 2.0 5.0 2.0 1.0 5.0 5.0 1.0 2.0 7.1 5.0
5.0 0.0 5.0 5.0 5.0 2.0 5.0 1.0 5.0 6.0 6.0 6.0 6.0 1.0 7.1
5.0 5.0 0.0 6.0 1.0 6.0 5.0 5.0 1.0 6.0 5.0 7.0 1.0 5.0 6.0
6.0 5.0 6.0 0.0 5.0 2.0 1.0 6.0 5.0 6.0 2.0 1.0 2.0 1.0 5.0
7.0 5.0 1.0 5.0 0.0 7.0 1.0 1.0 2.0 1.0 5.0 6.0 2.0 2.0 5.0
2.0 2.0 6.0 2.0 7.0 0.0 5.0 5.0 6.0 5.0 2.0 5.0 1.0 2.0 5.0
5.0 5.0 5.0 1.0 1.0 5.0 0.0 2.0 6.0 1.0 5.0 7.0 5.0 1.0 6.0
2.0 1.0 5.0 6.0 1.0 5.0 2.0 0.0 7.0 6.0 2.0 1.0 1.0 5.0 2.0
1.0 5.0 1.0 5.0 2.0 6.0 6.0 7.0 0.0 5.0 5.0 5.0 1.0 6.0 6.0
5.0 6.0 6.0 6.0 1.0 5.0 1.0 6.0 5.0 0.0 7.0 1.0 2.0 5.0 2.0
5.0 6.0 5.0 2.0 5.0 2.0 5.0 2.0 5.0 7.0 0.0 2.0 1.0 2.0 1.0
1.0 6.0 7.0 1.0 6.0 5.0 7.0 1.0 5.0 1.0 2.0 0.0 5.0 6.0 5.0
2.0 6.0 1.0 2.0 2.0 1.0 5.0 1.0 1.0 2.0 1.0 5.0 0.0 7.0 6.0
7.0 1.0 5.0 1.0 2.0 2.0 1.0 5.0 6.0 5.0 2.0 6.0 7.0 0.0 5.0
5.0 7.0 6.0 5.0 5.0 5.0 6.0 2.0 6.0 2.0 1.0 5.0 6.0 5.0 0.0

これを読むために、以下に示すように LoadCities() 関数があります。

#include "iostream"
#include "fstream"      
#include "string"   
using namespace std;

double distances [15][15];  

void LoadCities()
{
    ifstream CityFile;

    if (!CityFile.is_open()) //check is file has been opened
    {
        CityFile.open ("Cities.txt", ios::in | ios::out);

        if (!CityFile)
        {
            cerr << "Failed to open " << CityFile << endl;
            exit(EXIT_FAILURE);  //abort program
        }
    }

    int length;
    char * buffer;
    string cities;

    CityFile.seekg(0, ios::end);
    length = CityFile.tellg();
    CityFile.seekg (0, ios::beg);

    buffer = new char [length];

    cities = CityFile.read (buffer,length); 

    string rows = strtok(cities, "\n");

    distances = new double[rows.length()][rows.length()];

            for (int i = 0; i < (string) rows.length(); i++)
            {
                string distance = strtok(rows[i], " ");

                for (int j = 0; j < distance.length(); j++)
                {
                    distances[i][j] = (double) Parse(distance[j]);
                }
            }

    CityFile.close();
}

代わりの istreambuf_iterator メソッドを試して、読み取ったマテリアルを配列に操作するところまで到達しましたが、常に問題が発生するようです。

ifstream CityFile("Cities.txt");
string theString((std::istreambuf_iterator<char>(CityFile)), std::istreambuf_iterator<char>());

どんな助けでも大歓迎です。これに対して私の頭をぶつけてきましたが、ほとんど成功していません!

################ 編集/更新

@ SoapBox - SA コード、関数、および main() の詳細。これはクリーンでも効率的でも整理整頓されたものでもなく、この段階にあるとは言えません。とりあえず動作する必要があります。このバージョン (以下) は機能し、多項式 (最も単純な問題) を解くように設定されています。これを巡回セールスマン問題に変換するには、次のことを行う必要があります。

  1. 距離データを収集する LoadCities() 関数を記述します。(現時点の)

  2. 関連する距離の合計を取得するように Initialise() を変更します。

  3. E() を TSP 関数に変更します (例: ランダム ルートの距離を計算します)。

後者の 2 つは実行できることがわかっていますが、それには LoadCities() が必要です。次のスクリプトでは、他に何も変更する必要はありません。

#include "math.h"
#include "iostream"
#include "fstream"
#include "time.h"   // Define time()
#include "stdio.h"  // Define printf()
#include "randomc.h"    // Define classes for random number generators
#include "mersenne.cpp" // Include code for the chosen random number generator

using namespace std; // For the use of text generation in application

double T;
double T_initial;

double S;
double S_initial;
double S_current;
double S_trial;

double E_current;

int N_step;        // Number of Iterations for State Search per Temperature
int N_max;         //Number of Iterations for Temperature
int Write;

const double EXP = 2.718281828;

//------------------------------------------------------------------------------
//Problem Function of Primary Variable (Debugged 17/02/09 - Works as intended)

double E(double x) //ORIGNINAL
{
    double y = x*x - 6*x + 2;

     return y;
}

//------------------------------------------------------------------------------
//Random Number Generation Function (Mod 19/02/09 - Generated integers only & fixed sequence)

double Random_Number_Generator(double nHigh, double nLow) 
{
    int seed = (int)time(0);            // Random seed

    CRandomMersenne RanGen(seed);       // Make instance of random number generator

    double fr;                          // Random floating point number

    fr = ((RanGen.Random() * (nHigh - nLow)) + nLow);   // Generatres Random Interger between nLow & nHigh

    return fr;
}

//------------------------------------------------------------------------------
//Initializing Function (Temp 17/02/09)

void Initialize() //E.g. Getting total Distance between Cities
{
    S_initial = Random_Number_Generator(10, -10);

    cout << "S_Initial: " << S_initial << endl;
}

//------------------------------------------------------------------------------
//Cooling Schedule Function (make variables) (Completed 16/02/09)

double Schedule(double Temp, int i) // Need to find cooling schedule
{
    double CoolingRate = 0.9999;

    return Temp *= CoolingRate; 
}

//------------------------------------------------------------------------------
//Next State Function (Mod 18/02/09)

double Next_State(double T_current, int i)
{
        S_trial = Random_Number_Generator(pow(3, 0.5), pow(3, 0.5)*-1); 

        S_trial += S_current;

        double E_t = E(S_trial);
        double E_c = E(S_current);

        double deltaE = E_t - E_c;                              //Defines gradient of movement

        if ( deltaE <= 0 )                                      //Downhill
        {    
            S_current = S_trial;
            E_current = E_t;
        }
        else                                                    //Uphill
        {
            double R = Random_Number_Generator(1,0);            //pseudo random number generated
            double Ratio = 1-(float)i/(float)N_max;             //Control Parameter Convergence to 0
            double ctrl_pram = pow(EXP, (-deltaE / T_current)); //Control Parameter

            if (R < ctrl_pram*Ratio)                            //Checking 
            {   
                S_current = S_trial;                            //Expresses probability of uphill acceptance
                E_current = E_t;                                
            }
            else 
                E_current = E_c;
        }

        return S_current;
}

//------------------------------------------------------------------------------
//Metropolis Function (Mod 18/02/09)

double Metropolis(double S_start, double T_current, int N_Steps, int N_temperatures)
{
     S_current = S_start;                                       //Initialised S_initial equated to S_current

     for ( int i=1; i <= N_step; i++ )                          //Iteration of neighbour states
        S_current = Next_State(T_current, N_temperatures);      //Determines acceptance of new states

     return S_current;
}

//------------------------------------------------------------------------------
//Write Results to Notepad (Completed 18/02/09)

void WriteResults(double i, double T, double x, double y)
{
//This function opens a results file (if not already opened)
//and stores results for one time step

    static ofstream OutputFile;
    const int MAXLENGTH = 80;

    if (!OutputFile.is_open()) //check is file has been opened
    {
        //no it hasn't. Get a file name and open it.
        char FileName[MAXLENGTH];

        //read file name
        cout << "Enter file name: ";
        do
        {
            cin.getline(FileName, MAXLENGTH);
        }
        while (strlen(FileName) <= 0); //try again if length of string is 0

        //open file
        OutputFile.open(FileName);

        // check if file was opened successfully
        if (!OutputFile)
        {
            cerr << "Failed to open " << FileName << endl;
            exit(EXIT_FAILURE);  //abort program
        }

        OutputFile << "Iterations" << '\t' << "Temperatures" << '\t' << "X-Value" << '\t' << "Y-Value" << endl; 
        OutputFile << endl;
    }

    //OutputFile.width(10);
    OutputFile << i << '\t' << T << '\t' << x << '\t' << y << endl; 

    if (i == N_max) 
    {   
        OutputFile << endl
               << "Settings: " << endl
               << "Initial Temperature: " << T_initial << endl
               << "Temperature Iterations: " << N_max << endl
               << "Step Iterations: " << N_step << endl
               << endl
               << "Results: " << endl
               << "Final Temperature: " << T << endl 
               << "Minimum: " << S << endl;

        OutputFile.close();
    }
}

//------------------------------------------------------------------------------
//Main SA Function (Mod 17/02/09)

void SA(int W)
{
    S = S_initial;
    T = T_initial;

    for ( int N_temperatures = 1 ; N_temperatures <= N_max ; N_temperatures++ )
    {
        S = Metropolis( S, T, N_step, N_temperatures);
        T = Schedule(T, N_temperatures);

        if (W == 1)
            WriteResults(N_temperatures, T, S, E_current);
    }

    cout << "Result" << endl
    << "Y-value> " << S << endl
    << "Temperature> " << T << endl;

}

//------------------------------------------------------------------------------
//Execution of Traveling Salesman Problem (Progress 18/02/09)


int main()
{
    cout << "Quadratic Function" << endl
         << "Solving method: Simulated Annealing" << endl;
    cout << "" << endl;

    cout << "Select desired Initial Temperature:" << endl
         << "> ";
    cin >> T_initial;

    cout << "Select desired number of Temperature Iterations:" << endl
         << "> ";
    cin >> N_max;

    cout << "Select desired number of step Iterations:" << endl
         << "> ";
    cin >> N_step;

    Initialize();

    cout << "Write to file: (1 / 0) " << endl
         << "> ";
    cin >> Write;

    SA(Write);

    system ("PAUSE");

    return 0;
}

@ strager - 私はその悪いコードを知っていますが、残念ながら、私のプロジェクトに関連する時間の制約とその結果としての学習曲線を考えると、必要なのは結果です! :) 後の段階で整理されます。

@ dirkgently - それがこのようにする最初の理由でした。

4

5 に答える 5

13

これはどう?( KISSソリューション)

void LoadCities() {
  int x, y;
  ifstream in("Cities.txt");

  if (!in) {
    cout << "Cannot open file.\n";
    return;
  }

  for (y = 0; y < 15; y++) {
    for (x = 0; x < 15; x++) {
      in >> distances[x][y];
    }
  }

  in.close();
}

私のために働きます。それほど複雑ではないかもしれませんし、パフォーマンスも高くないかもしれませんが、1000x1000 の配列を読み取っていない限り、違いはわかりません。

于 2009-02-21T13:51:48.300 に答える
1

コンパイルもしますか?〜7個のエラーが発生します。サンプル:

strtok(cities, "\n");

strtok()の最初の引数はachar *であり、std::stringではありません。

これは役に立ちますか?

void LoadCities()
{
  std::vector<double> f((std::istream_iterator<double>
       (std::ifstream("city.txt"))), /* replace filename with your own */
    (std::istream_iterator<double>()));
  if (!f.empty()) {
    std::cout << f.size() << "\n";
    /* print an arbitrary data point with 2 places of decimal */
    std::cout << std::setprecision(2) << f[ 0 ] << std::endl; 

  }
}

行列を操作することは、多次元配列が必要であることを意味しません。特に、2Dアレイの場合。もちろん、読み書きは簡単です;)

于 2009-02-21T13:30:58.960 に答える
1

おそらく、次のようなもっと単純なものが必要です。

std::vector<std::vector<std::string> > LoadCities(const std::string &filename)
{
    using namespace std;

    ifstream file;
    file.open(filename, ios::in | ios::out);

    if(!file.is_open()) {
        // error
        return vector<vector<double> >();
    }

    vector<vector<double> > data;
    string line;

    while(!std::getline(file, line, '\n').eof()) {
        istringstream reader(line);

        vector<double> lineData;

        string::const_iterator i = line.begin();

        while(!reader.eof()) {
            double val;
            reader << val;

            if(reader.fail())
                break;

            lineData.push_back(val);
        }

        data.push_back(lineData);
    }

    return data;
}

基本的に、ストリームを使用してデータを入力します。私はおそらく何か間違ったことをしています (私は iostream を扱ったことはありません ;P) が、これでマトリックス リーダーを構築する方法の一般的なアイデアが得られるはずです。

于 2009-02-21T13:51:50.580 に答える
0

これが私がそれをロード/保存する方法です:

#include <iostream>
#include <fstream>
#include <string>

int width = 0;
int height = 0;
double **distances;

void WriteDouble( std::ofstream &stream, double toWrite )
{
    char buffer[8];
    memcpy( buffer, &toWrite, 8 );
    stream.write( buffer, 8 );
}

void WriteInt( std::ofstream &stream, int toWrite )
{
    char buffer[4];
    memcpy( buffer, &toWrite, 4 );
    stream.write( buffer, 4 );
}

double ReadDouble( std::ifstream &stream )
{
    double d = 0;
    stream.read( (char *)&d, 8 );
    return d;
}

int ReadInt( std::ifstream &stream )
{
    int i = 0;
    stream.read( (char *)&i, 4 );
    return i;
}

void Save()
{
    std::ofstream stream( "cities", std::ios::out | std::ios::binary );

    if( !stream.good() ) {
        throw std::exception( "Error opening stream" );
    }

    WriteInt( stream, width );
    WriteInt( stream, height );

    for( int x = 0; x < width; x++ ) {
        for( int y = 0; y < height; y++ ) {
            WriteDouble( stream, distances[x][y] );
        }
    }

    stream.close();
}

void Load()
{
    std::ifstream stream( "cities", std::ios::in | std::ios::binary );

    if( !stream.good() ) {
        throw std::exception( "Error opening stream" );
    }

    width = ReadInt( stream );
    height = ReadInt( stream );

    distances = new double *[width];

    for( int i = 0; i < width; i++ ) {
        distances[i] = new double[height];
    }

    for( int x = 0; x < width; x++ ) {
        for( int y = 0; y < height; y++ ) {
            distances[x][y] = ReadDouble( stream );
        }
    }

    stream.close();
}

void RunSaveTest()
{
    width = 15;
    height = 15;

    distances = new double *[width];

    for( int i = 0; i < width; i++ ) {
        distances[i] = new double[height];
    }

    for( int x = 0; x < width; x++ ) {
        for( int y = 0; y < height; y++ ) {
            distances[x][y] = (double)x / (double)( y + 1 );
            std::cout << distances[x][y] << std::endl;
        }
    }

    Save();
}

void RunLoadTest()
{
    Load();

    for( int x = 0; x < width; x++ ) {
        for( int y = 0; y < height; y++ ) {
            std::cout << distances[x][y] << std::endl;
        }
    }
}

int main()
{
    RunSaveTest();
    // RunLoadTest();

    return 0;
}
于 2009-02-21T20:53:51.797 に答える
0

私のブログからの参照: http://www.topbug.ne​​t/ blog/2013/01/10/load-a-matrix-from-an-ascii-format-file/

このコード スニペットは、すべてが適切にフォーマットされていると想定するのではなく、より高いフォールト トレランスを備えています。

#include <istream>
#include <string>
#include <sstream>
#include <vector>

// load matrix from an ascii text file.
void load_matrix(std::istream* is,
        std::vector< std::vector<double> >* matrix,
        const std::string& delim = " \t")
{
    using namespace std;

    string      line;
    string      strnum;

    // clear first
    matrix->clear();

    // parse line by line
    while (getline(*is, line))
    {
        matrix->push_back(vector<double>());

        for (string::const_iterator i = line.begin(); i != line.end(); ++ i)
        {
            // If i is not a delim, then append it to strnum
            if (delim.find(*i) == string::npos)
            {
                strnum += *i;
                if (i + 1 != line.end()) // If it's the last char, do not continue
                    continue;
            }

            // if strnum is still empty, it means the previous char is also a
            // delim (several delims appear together). Ignore this char.
            if (strnum.empty())
                continue;

            // If we reach here, we got a number. Convert it to double.
            double       number;

            istringstream(strnum) >> number;
            matrix->back().push_back(number);

            strnum.clear();
        }
    }
}

// example
#include <fstream>
#include <iostream>

int main()
{
    using namespace std;

    // read the file
    std::ifstream is("input.txt");

    // load the matrix
    std::vector< std::vector<double> > matrix;
    load_matrix(&is, &matrix);

    // print out the matrix
    cout << "The matrix is:" << endl;
    for (std::vector< std::vector<double> >::const_iterator it = matrix.begin(); it != matrix.end(); ++ it)
    {
        for (std::vector<double>::const_iterator itit = it->begin(); itit != it->end(); ++ itit)
            cout << *itit << '\t';

        cout << endl;
    }

    return 0;
}
于 2016-09-27T16:34:28.793 に答える