【问题标题】:Reading a Matrix txt file and storing as an array读取 Matrix txt 文件并存储为数组
【发布时间】:2009-02-21 13:12:53
【问题描述】:

我目前正在编写模拟退火代码来解决旅行推销员问题,并且在存储和使用我从 txt 文件中读取的数据时遇到了困难。文件中的每一行和每一列代表每个城市,两个不同城市之间的距离存储为一个 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 函数(例如计算随机路线的距离)

后两个我知道我可以做到,但是我需要 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 - 这是这样做的最初原因,也是我第一次尝试这样做的原因。

【问题讨论】:

  • 更多关于问题的详细信息可能会有所帮助。你提供了很好的代码和很多细节,但大部分都忽略了你真正想要解决的问题.....
  • 这不是好的代码......它甚至不应该编译!有一些问题。 distances 是一个 double[15][15] 但像指针一样分配。在对文件执行任何操作之前检查文件是否已打开。他将整个文件读入缓冲区……等等,等等。
  • @strager:在缓冲区中读取整个文件是一种优化技术。我认识的很多人在为编程比赛编写代码时都使用它;)
  • @dirkgently,它会占用更多内存,并且必须等待从磁盘读取所有数据才能进行任何处理。对我来说,这听起来不像是一种优化技术!
  • 很抱歉我退出了这个游戏:) 我认为 OP 应该先纠正明显的错误(至少尝试编译他的代码)然后再询问。

标签: c++ matrix ifstream


【解决方案1】:

这个怎么样? (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 数组,您就不会看到任何差异。

【讨论】:

  • 漂亮而简单,但是由于某种原因,当我测试它时它输出 0 或每个值。例如,在
  • @Temperedsoul:看看你的数据。它是双倍的,但小数点后没有重要部分。如果你想要一个 5.0,你需要告诉 cout。我将更新我的示例。
【解决方案2】:

你可能想要一些更简单的东西,像这样:

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),但这应该让您大致了解如何构建矩阵读取器。

【讨论】:

  • 导致 4 个错误,其中三个 C2664 - 'function' : cannot convert parameter number from 'type1' to 'type2' - 我看看能不能把它们清除掉。
【解决方案3】:

它甚至可以编译吗?我得到〜7个错误。一个样本:

strtok(cities, "\n");

strtok() 的第一个参数是 char * 而不是 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; 

  }
}

使用矩阵并不意味着您需要一个多维数组。尤其是二维数组。当然它更容易读写;)

【讨论】:

  • 因为它不能编译。编译它的唯一方法是删除从 'cities = ..." 到最后的所有内容,并简单地拥有: CityFile.read(buffer,length);, cout.write(buffer,length); CityFile.close(); -这对我没有多大帮助
  • @Temperedsoul:那么,现在你解决了吗?如果这样做,为什么不将 schnaader 的解决方案标记为已接受?
【解决方案4】:

这是我将如何加载/保存它:

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

【讨论】:

    【解决方案5】:

    来自我博客的参考:http://www.topbug.net/blog/2013/01/10/load-a-matrix-from-an-ascii-format-file/

    这段代码 sn-p 具有更高的容错性,而不是假设所有内容都格式正确。

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

    【讨论】:

    • 这个解决方案超级超级慢
    • @Pedro77 怎么会这样?
    • 我正在阅读一个 1200x1200 的数字文本文件。 Matlab ⁠⁠⁠⁠dlmread 在几秒钟内读取文件。此方法需要一分钟。出了点问题。
    • @Pedro77 您是否使用优化级别编译了代码?
    • 我想我做到了,VS中的默认配置。以文本形式存储的 1200x1200 矩阵在调试中用 9 秒加载,在发布中用 1.5 秒加载。
    猜你喜欢
    • 2013-03-24
    • 1970-01-01
    • 2012-11-22
    • 1970-01-01
    • 2015-07-23
    • 1970-01-01
    • 2020-05-25
    • 1970-01-01
    • 2022-01-05
    相关资源
    最近更新 更多