I'm trying to do task in C++. I need create this function:
void fillArray(std::array<std::array<int, maxColumns>, maxRows> array, size_t rows, size_t columns) {
}
Right now my example code looks like this:
#include <iostream>
#include <array>
constexpr int maxColumns = 42;
constexpr int maxRows = 334;
void fillArray(std::array<std::array<int, maxColumns>, maxRows> array, size_t rows, size_t columns) {
}
int main()
{
}
I need to fill the array with numbers from 1 to rows*columns starting from [0][0] and diagonally. How to declare and initialize the function with array in this example and then fill it diagonally? Any help would be greatly appreciated!
It should be
template <std::size_t maxColumns, std::size_t maxRows>
void fillArray(std::array<std::array<int, maxColumns>, maxRows>& array) {
// ...
}
Demo
Let's suppose you use a simple one-dimensional valarray (or array if you insist) of the size width * height wrapped in a class:
class Matrix
{
private:
std::valarray<int> _data;
int _width, _height;
public:
Matrix(int width, int height) : _width(width), _height(height), _data(width * height)
{
}
}
Then you can add a member function that maps x, y coordinates to an item reference:
int& item(int x, int y) { return _data[x + _width * y]; }
... and another one for filling it diagonally like this:
void fillDiagonally(int value = 0, int step = 1)
{
for (int i = 0; i < _height + _width; ++i) {
// calculate starting coordinates (along left edge and then bottom edge)
int row = i < _height ? i : _height - 1;
int col = i < _height ? 0 : i - _height + 1;
// move diagonally up and right until you reach margins, while filling-in values
for (int j = 0; j < _width - col && j <= row; ++j) {
item(col + j, row - j) = value;
value += step;
}
}
}
and use it like this:
int main()
{
Matrix m(8, 5);
m.fillDiagonally(1);
}
This way, you don't need to pass the array as an argument, because it's a part of the class. Otherwise you would have to pass it by reference, like you were suggested above.
Related
I have a pointer that points to the beginning of a 1000+ elements array that is initialized as below:
int numElements = 1200;
auto data = std::unique_ptr<float>{new float[numElements]};
Now I want to 'reshape' it into something like a (20,30,20) tensor, so I can access it the way I want (I can still read while it's 1-D as well but it feels weird). I want to access like this:
data[1][10][12] = 1337.0f;
Is there an efficient way of doing this (fast and short code)?
In the meantime, this is how I do it...
#include <iostream>
using std::cout;
using std::endl;
#include <vector>
using std::vector;
size_t get_index(const size_t x, const size_t y, const size_t z, const size_t x_res, const size_t y_res, const size_t z_res)
{
return z * y_res * x_res + y * x_res + x;
}
int main(void)
{
const size_t x_res = 10;
const size_t y_res = 10;
const size_t z_res = 10;
// Use new[] to allocate, and memset to clear
//float* vf = new float[x_res * y_res * z_res];
//memset(vf, 0, sizeof(float) * x_res * y_res * z_res);
// Better yet, use a vector
vector<float> vf(x_res*y_res*z_res, 0.0f);
for (size_t x = 0; x < x_res; x++)
{
for (size_t y = 0; y < y_res; y++)
{
for (size_t z = 0; z < z_res; z++)
{
size_t index = get_index(x, y, z, x_res, y_res, z_res);
// Do stuff with vf[index] here...
}
}
}
// Make sure to deallocate memory
// delete[] vf;
return 0;
}
I'm trying to code a function that, given an index as the input argument, extract the corresponding layer from a 3D matrix, returning a 2D matrix.
My default 3D matrix constructor looks something like this:
Matrice3D(unsigned int depth, unsigned int height, unsigned int width, const T &value) : _3D_matrix(0), _height(0), _width(0), _depth(0) {
try {
_3D_matrix = new T[height * width * depth];
for (int z = 0; z < depth; z++) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
_3D_matrix[y * width * depth + x * depth + z] = value;
}
}
}
}
catch(...) {
delete[] _3D_matrix;
throw;
}
_height = height;
_width = width;
_depth = depth;
}
(the try/catch is still a wip, I know it's not a thing to do).
So far, I've coded this:
void slice(int index) {
try{
_2D_matrix = new T[_height * _width];
}catch(...){
delete[] _2D_matrix;
_height = 0;
_width = 0;
_depth = 0;
throw;
}
_depth = 1;
for (int k = 0; k< _depth; k++) {
for (int j = 0; j< _height; j++) {
for (int i = 0; i< _width; i++) {
_2D_matrix[j * _width + i] =
_3D_matrix[j * _width * _depth + i * _depth + index];
}
}
}
}
I guess the logic behind the assignment done with the nested for cycles is right, but I don't really know how to return the new matrix.
From the main, used to test the code, I'm calling
std::cout << "--------TEST SLICE------------" << std::endl;
Matrice3D<int> m1(3,3,3,17);
std::cout << "Setter su (0,0,2,99)" << std::endl;
m1(0,0,2,91); //setting a value
m1.slice(2);
std::cout << "Matrix obtained from slicing the layer 2: " <<std::endl;
std::cout << m1;
but I keep getting the first layer of the matrix, whatever index I choose for the input.
Create a new class Matrice2D and return that in slice().
The reason why your get total garbage in your code is that you destroy the _depth of the 3D matrix. It's not even the first layer but really just garbage.
The only time new and delete should appear is in classes named something_ptr. You don't need raw pointers here, and you should be returning a 2DMatrix from slice.
template <typename T>
class 2DMatrix;
template <typename T>
class 3DMatrix {
std::vector<T> data;
std::size_t height, width, depth;
public:
3DMatrix(std::size_t height, std::size_t width, std::size_t depth, T value = {})
: data(height * width * depth, value),
height(height),
width(width),
depth(depth)
{}
2DMatrix<T> slice(std::size_t index) {
2DMatrix<T> result(height, width);
for (std::size_t i = index; i < data.size(); i += depth) {
result.data[i / depth] = data[i];
}
return result;
}
// other members
}
template <typename T>
class 2DMatrix {
std::vector<T> data;
std::size_t height, width;
friend class 3DMatrix<T>;
public:
2DMatrix(std::size_t height, std::size_t width, T value = {})
: data(height * width, value),
height(height),
width(width)
{}
// other members
}
I'm writing a simulation of the Ising model in 2D. The model behaves as predicted except for one thing: the critical temperature is roughly 3.5 while it should be near 2/ln(2 + sqrt (2)).
The project is a C++ program that generates the data, and a shell script that exercises the program. The full code can be found here. Also here's lattice.cpp
#include <iostream>
#include "include/lattice.h"
using namespace std;
/*
Copy assignment operator, too long to include in the header.
*/
lattice &lattice::operator=(const lattice &other) {
size_ = other.size_;
spins_ = other.spins_;
J_ = other.J_;
H_ = other.H_;
delete spins_;
return *this;
}
void lattice::print() {
unsigned int area = size_ * size_;
for (unsigned int i = 0; i < area; i++) {
cout << to_symbol(spins_->at(i));
if (i % size_ == size_ - 1)
cout << endl;
}
cout << endl;
}
/*
Computes the energy associated with a spin at the given point.
It is explicitly float as that would allow the compiler to make use of multiple
registers instead of keeping track of unneeded precision. (typically J, H ~ 1).
*/
float lattice::compute_point_energy(int row, int col) {
int accumulator = get(row + 1, col) + get(row - 1, col) + get(row, col - 1) +
get(row, col + 1);
return -get(row, col) * (accumulator * J_ + H_);
}
/*
Computes total magnetisation in O(n^2). Thread safe
*/
int lattice::total_magnetisation() {
int sum = 0;
#pragma omp parallel for reduction(+ : sum)
for (unsigned int i = 0; i < size_ * size_; i++) {
sum += spins_->at(i);
}
return sum;
}
int inline to_periodic(int row, int col, int size) {
if (row < 0 || row >= size)
row = abs(size - abs(row));
if (col < 0 || col >= size)
col = abs(size - abs(col));
return row * size + col;
}
with lattice.h
#ifndef lattice_h
#define lattice_h
#include <cmath>
#include <vector>
/* Converts spin up/down to easily printable symbols. */
char inline to_symbol(int in) { return in == -1 ? '-' : '+'; }
/* Converts given pair of indices to those with periodic boundary conditions. */
int inline to_periodic(int row, int col, int size) {
if (row < 0 || row >= size)
row = abs(size - abs(row));
if (col < 0 || col >= size)
col = abs(size - abs(col));
return row * size + col;
}
class lattice {
private:
unsigned int size_;
// vector<bool> would be more space efficient, but it would not allow
// multithreading
std::vector<short> *spins_;
float J_;
float H_;
public:
lattice() noexcept : size_(0), spins_(NULL), J_(1.0), H_(0.0) {}
lattice(int new_size, double new_J, double new_H) noexcept
: size_(new_size), spins_(new std::vector<short>(size_ * size_, 1)),
J_(new_J), H_(new_H) {}
lattice(const lattice &other) noexcept
: lattice(other.size_, other.J_, other.H_) {
#pragma omp parallel for
for (unsigned int i = 0; i < size_ * size_; i++)
spins_->at(i) = other.spins_->at(i);
}
lattice &operator=(const lattice &);
~lattice() { delete spins_; }
void print();
short get(int row, int col) {
return spins_->at(to_periodic(row, col, size_));
}
unsigned int get_size() { return size_; }
void flip(int row, int col) { spins_->at(to_periodic(row, col, size_)) *= -1; }
int total_magnetisation();
float compute_point_energy(int row, int col);
};
#endif
and simulation.cpp
#include <iostream>
#include <math.h>
#include "include/simulation.h"
using namespace std;
/*
Advances the simulation a given number of steps, and updates/prints the statistics
into the given file pointer.
Defaults to stdout.
The number of time_steps is explcitly unsigned, so that linters/IDEs remind
the end user of the file that extra care needs to be taken, as well as to allow
advancing the simulation a larger number of times.
*/
void simulation::advance(unsigned int time_steps, FILE *output) {
unsigned int area = spin_lattice_.get_size() * spin_lattice_.get_size();
for (unsigned int i = 0; i < time_steps; i++) {
// If we don't update mean_energy_ every time, we might get incorrect
// thermodynamic behaviour.
total_energy_ = compute_energy(spin_lattice_);
double temperature_delta = total_energy_/area - mean_energy_;
if (abs(temperature_delta) < 1/area){
cerr<<temperature_delta<<"! Reached equilibrium "<<endl;
}
temperature_ += temperature_delta;
mean_energy_ = total_energy_ / area;
if (time_ % print_interval_ == 0) {
total_magnetisation_ = spin_lattice_.total_magnetisation();
mean_magnetisation_ = total_magnetisation_ / area;
print_status(output);
}
advance();
}
}
/*
Advances the simulation a single step.
DOES NOT KEEP TRACK OF STATISTICS. Hence private.
*/
void simulation::advance() {
#pragma omp parallel for collapse(2)
for (unsigned int row = 0; row < spin_lattice_.get_size(); row++) {
for (unsigned int col = 0; col < spin_lattice_.get_size(); col++) {
double dE = compute_dE(row, col);
double p = r_.random_uniform();
float rnd = rand() / (RAND_MAX + 1.);
if (exp(-dE / temperature_) > rnd) {
spin_lattice_.flip(row, col);
}
}
}
time_++;
}
/*
Computes change in energy due to flipping one single spin.
The function returns a single-precision floating-point number, as data cannot under
most circumstances make use of greater precision than that (save J is set to a
non-machine-representable value).
The code modifies the spin lattice, as an alternative (copying the neighborhood
of a given point), would make the code run slower by a factor of 2.25
*/
float simulation::compute_dE(int row, int col) {
float e_0 = spin_lattice_.compute_point_energy(row, col);
return -4*e_0;
}
/*
Computes the total energy associated with spins in the spin_lattice_.
I originally used this function to test the code that tracked energy as the lattice
itself was modified, but that code turned out to be only marginally faster, and
not thread-safe. This is due to a race condition: when one thread uses a neighborhood
of a point, while another thread was computing the energy of one such point in
the neighborhood of (row, col).
*/
double simulation::compute_energy(lattice &other) {
double energy_sum = 0;
unsigned int max = other.get_size();
#pragma omp parallel for reduction(+ : energy_sum)
for (unsigned int i = 0; i < max; i++) {
for (unsigned int j = 0; j < max; j++) {
energy_sum += other.compute_point_energy(i, j);
}
}
return energy_sum;
}
void simulation::set_to_chequerboard(int step){
if (time_ !=0){
return;
}else{
for (unsigned int i=0; i< spin_lattice_.get_size(); ++i){
for (unsigned int j=0; j<spin_lattice_.get_size(); ++j){
if ((i/step)%2-(j/step)%2==0){
spin_lattice_.flip(i, j);
}
}
}
}
}
with simulation.h
#ifndef simulation_h
#define simulation_h
#include "lattice.h"
#include "rng.h"
#include <gsl/gsl_rng.h>
/*
The logic of the entire simulation of the Ising model of magnetism.
This simulation will run and print statistics at a given time interval.
A simulation can be advanced a single time step, or many at a time,
*/
class simulation {
private:
unsigned int time_ = 0; // Current time of the simulation.
rng r_ = rng();
lattice spin_lattice_;
double temperature_;
double mean_magnetisation_ = 1;
double mean_energy_;
double total_magnetisation_;
double total_energy_;
unsigned int print_interval_ = 1;
void advance();
public:
void set_print_interval(unsigned int new_print_interval) { print_interval_ = new_print_interval; }
simulation(int new_size, double new_temp, double new_J, double new_H)
: time_(0), spin_lattice_(lattice(new_size, new_J, new_H)), temperature_(new_temp),
mean_energy_(new_J * (-4)), total_magnetisation_(new_size * new_size),
total_energy_(compute_energy(spin_lattice_)) {}
void print_status(FILE *f) {
f = f==NULL? stdout : f;
fprintf(f, "%4d\t%e \t%e\t%e\n", time_, mean_magnetisation_,
mean_energy_, temperature_);
}
void advance(unsigned int time_steps, FILE *output);
double compute_energy(lattice &other);
float compute_dE(int row, int col);
void set_to_chequerboard(int step);
void print_lattice(){
spin_lattice_.print();
};
// void load_custom(const lattice& custom);
};
#endif
The output right now looks something like this:
while it should be a step down near 2.26
I have found a few issues in your code:
The compute_dE method returns the wrong energy, as the factor of 2 shouldn't be there. The Hamiltonian of the Ising system is
While you are effectively using
The compute_energy method returns the wrong energy. The method should iterate over each spin pair only once. Something like this should do the trick:
for (unsigned int i = 0; i < max; i++) {
for (unsigned int j = i + 1; j < max; j++) {
energy_sum += other.compute_point_energy(i, j);
}
}
You use a temperature that is updated on the fly instead of using the target temperature. I do not really understand the purpose of that.
I would like to create a function which initializes a vector or array of size width * height, but which also creates a border around these values.
The values around the outside also need to be initialized to a different value from the ones in the center.
The objects I am storing do not have a default constructor, so I cannot rely on that for initialization.
This is the code I have so far, but it feels like there should be a simpler or more idiomatic way of doing this.
I can use any features up to and including C++1z.
#include <iostream>
#include <vector>
void fill_values(const unsigned width, const unsigned height, std::vector<int> &values) {
for(unsigned y=0; y<height+2; ++y) {
for(unsigned x=0; x<width+2; ++x) {
if(x==0 || x==width+1 || y==0 || y==height+1) {
values.push_back(1);
} else {
values.push_back(0);
}
}
}
}
int main(int argc, char *argv[]) {
const unsigned width = 4;
const unsigned height = 3;
std::vector<int> values;
fill_values(width, height, values);
for(unsigned y=0; y<height+2; ++y) {
for(unsigned x=0; x<width+2; ++x) {
std::cout << values[y * (width+2) + x];
}
std::cout << '\n';
}
return 0;
}
Output : -
111111
100001
100001
100001
111111
Honestly, your code is fine. I pretty easily understood what it does.
But in the spirit of proposing alternate complex implementations, I'd propose the following. A different way to fill the matrix is to add a full row of 1s, then height rows of 1000...001, then another full row of 1s. We can make that a bit more explicit. Also, would suggest returning a vector instead of filling it:
std::vector<int> fill_values(const unsigned width, const unsigned height) {
std::vector<int> m;
m.reserve((width + 2) * (height + 2));
// add row of 1s
m.insert(m.end(), width + 2, 1);
// add height middle rows
for (int i = 0; i < height; ++i) {
m.push_back(1);
m.insert(m.end(), width, 0);
m.push_back(1);
}
// and a final row of 1s
m.insert(m.end(), width + 2, 1);
return m;
}
As #Fedorico said in the comments, using a vector of vectors is a better representation for your values variable. Rather than pass the values by reference as a parameter, it would be better to depend on copy elision for the return value. I also found it easier to just use the set height and width to be the total number of rows and cols in the data so that there's no need to add two.
The following code depends on c++11 or newer:
#include <iostream>
#include <vector>
using namespace std;
// Fills the 2D matrix with 1s on the border and 0s in the middle.
vector<vector<int>> generate_matrix(int rows, int cols);
void print_matrix(const vector<vector<int>>& matrix);
int main()
{
// Don't sync I/O with C stdio.
ios_base::sync_with_stdio(false);
// Height and Width of the entire 2D matrix.
const int rows = 6;
const int cols = 5;
vector<vector<int>> matrix = generate_matrix(rows, cols);
print_matrix(matrix);
return 0;
}
vector<vector<int>> generate_matrix(int rows, int cols)
{
// fill a rows x cols 2D vector with 0s.
vector<vector<int>> matrix(rows, vector<int>(cols, 0));
// fill in 1s on top and bottom rows.
if (rows > 0)
{
for (int i = 0; i < cols; ++i)
{
matrix[0][i] = 1;
matrix[rows-1][i] = 1;
}
}
// fill in 1s on the left and right columns.
if (cols > 0)
{
for (int i = 0; i < rows; ++i)
{
matrix[i][0] = 1;
matrix[i][cols-1] = 1;
}
}
return matrix;
}
void print_matrix(const vector<vector<int>>& matrix)
{
// Use a reference for the row iterator to prevent a vector copy.
for (auto& row : matrix)
{
for (auto element : row)
{
cout << element;
}
cout << '\n';
}
}
Not a great difference, but you can use std::generate_n() (starting from c++11) with a lambda function.
The following is a full working example
#include <vector>
#include <iostream>
#include <algorithm>
int main ()
{
constexpr std::size_t width { 4U };
constexpr std::size_t height { 3U };
constexpr std::size_t w2 { width + 2U };
constexpr std::size_t h2 { height + 2U };
std::vector<int> values;
values.resize ( w2 * h2 );
std::generate_n(values.begin(), w2 * h2, [=]() -> int
{
static std::size_t i = -1;
++i;
return ( 0U == i / w2 ) || ( h2 - 1U == i / w2 )
|| ( 0U == i % w2 ) || ( w2 - 1U == i % w2 );
});
for(unsigned y=0; y<height+2; ++y) {
for(unsigned x=0; x<width+2; ++x) {
std::cout << values[y * (width+2) + x] << ' ';
}
std::cout << '\n';
}
return 0;
}
If width and heigth are known at compile time, you can initialize the std::vector (or the std::array?) with initializer list, using a little template work (give me some time and I'll show an example).
How can I allocate 3D, 4D, 5D arrays with one malloc in a contigious way and access the individual items?
Something like this:
int* array = malloc(sizeof(int) * width * height);
int item = array[x + y * width];
A 3D array is an array of 2D arrays. A 4D array is an array of 3D arrays. You just multiply by your other dimensions. For example, a 3D array can be allocated in this way:
int *array = malloc(sizeof(int) * width * height * depth);
A 4D array can be made by multiplying by your other dimension:
int *array = malloc(sizeof(int) * width * height * depth * other_dimension);
and so on for 5D, 6D, etc. arrays.
You can access elements by using something like this (for 3D arrays, easily extended), assuming you have access to the width and height of the array:
int get_element(int x, int y, int z)
{
return array[(z * width * height) + (y * width) + x];
}
For 4D arrays:
int get_element(int x, int y, int z, int dimension_4)
{
return array[(dimension_4 * width * height * depth) + (z * width * height) + (y * width) + x];
}
As answered here (Setting pointer to arbitrary dimension array?
):
Look specially computeIndex/computeIndexes.
#include <cstddef>
#include <vector>
template <typename T>
class MultiArray
{
public:
explicit MultiArray(const std::vector<size_t>& dimensions) :
dimensions(dimensions),
values(computeTotalSize(dimensions))
{
assert(!dimensions.empty());
assert(!values.empty());
}
const T& get(const std::vector<size_t>& indexes) const
{
return values[computeIndex(indexes)];
}
T& get(const std::vector<size_t>& indexes)
{
return values[computeIndex(indexes)];
}
size_t computeIndex(const std::vector<size_t>& indexes) const
{
assert(indexes.size() == dimensions.size());
size_t index = 0;
size_t mul = 1;
for (size_t i = 0; i != dimensions.size(); ++i) {
assert(indexes[i] < dimensions[i]);
index += indexes[i] * mul;
mul *= dimensions[i];
}
assert(index < values.size());
return index;
}
std::vector<size_t> computeIndexes(size_t index) const
{
assert(index < values.size());
std::vector<size_t> res(dimensions.size());
size_t mul = values.size();
for (size_t i = dimensions.size(); i != 0; --i) {
mul /= dimensions[i - 1];
res[i - 1] = index / mul;
assert(res[i - 1] < dimensions[i - 1]);
index -= res[i - 1] * mul;
}
return res;
}
private:
size_t computeTotalSize(const std::vector<size_t>& dimensions) const
{
size_t totalSize = 1;
for (auto i : dimensions) {
totalSize *= i;
}
return totalSize;
}
private:
std::vector<size_t> dimensions;
std::vector<T> values;
};
int main()
{
MultiArray<int> m({3, 2, 4});
m.get({0, 0, 3}) = 42;
m.get({2, 1, 3}) = 42;
for (size_t i = 0; i != 24; ++i) {
assert(m.computeIndex(m.computeIndexes(i)) == i);
}
}
Demo
Arrays are by nature allocated as a single dimension. You bestow dimensionality on them via the way you compute indexes to them. The size you need to allocate is the size of a scalar element multiplied by the number of elements in each of however many dimensions you intend to use, e.g., if you want a 10 x 20 x 30 array of 4-byte elements, multiply 4 x 10 x 20 x 30 to get the size of the malloc you need. Then, I'd probably write a function such as my_index(int i, int j, int k) that would compute the one-dimensional index for any valid (i,j,k) combination. This idea can be extended into as many dimensions as you wish.