ho qualche codice per iterare su una (multivariata) intervallo numerico:gamma più veloce per il ciclo (C++ 11)
#include <array>
#include <limits>
#include <iostream>
#include <iterator>
template <int N>
class NumericRange : public std::iterator<double, std::input_iterator_tag>
{
public:
NumericRange() {
_lower.fill(std::numeric_limits<double>::quiet_NaN());
_upper.fill(std::numeric_limits<double>::quiet_NaN());
_delta.fill(std::numeric_limits<double>::quiet_NaN());
}
NumericRange(const std::array<double, N> & lower, const std::array<double, N> & upper, const std::array<double, N> & delta):
_lower(lower), _upper(upper), _delta(delta) {
_state.fill(std::numeric_limits<double>::quiet_NaN());
}
const std::array<double, N> & get_state() const {
return _state;
}
NumericRange<N> begin() const {
NumericRange<N> result = *this;
result.start();
return result;
}
NumericRange<N> end() const {
NumericRange<N> result = *this;
result._state = _upper;
return result;
}
bool operator !=(const NumericRange<N> & rhs) const {
return in_range();
// return ! (*this == rhs);
}
bool operator ==(const NumericRange<N> & rhs) const {
return _state == rhs._state && _lower == rhs._lower && _upper == rhs._upper && _delta == rhs._delta;
}
const NumericRange<N> & operator ++() {
advance();
if (! in_range())
_state = _upper;
return *this;
}
const std::array<double, N> & operator *() const {
return _state;
}
void start() {
_state = _lower;
}
bool in_range(int index_to_advance = N-1) const {
return (_state[ index_to_advance ] - _upper[ index_to_advance ]) < _delta[ index_to_advance ];
}
void advance(int index_to_advance = 0) {
_state[ index_to_advance ] += _delta[ index_to_advance ];
if (! in_range(index_to_advance)) {
if (index_to_advance < N-1) {
// restart index_to_advance
_state[index_to_advance] = _lower[index_to_advance];
// carry
++index_to_advance;
advance(index_to_advance);
}
}
}
private:
std::array<double, N> _lower, _upper, _delta, _state;
};
int main() {
std::array<double, 7> lower{{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}};
std::array<double, 7> upper{{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}};
std::array<double, 7> delta{{0.03, 0.06, 0.03, 0.06, 0.03, 0.06, 0.03}};
NumericRange<7> nr(lower, upper, delta);
int c = 0;
for (nr.start(); nr.in_range(); nr.advance()) {
++c;
}
std::cout << "took " << c << " steps" << std::endl;
return 0;
}
Compilare con g++ -std=c++11 -O3
(o -std=c++0x
con gcc < 4.7) viene eseguito in circa 13,8 secondi sul mio computer.
Se cambio la funzione main
di utilizzare una gamma-based per ciclo:
for (const std::array<double, 7> & arr : nr) {
++c;
}
runtime aumenta a 29,8 secondi. Per coincidenza, questo runtime di ~ 30 secondi è quasi lo stesso del runtime dell'originale quando si utilizza std::vector<double>
anziché std::array<double, N>
, che mi porta a credere che il compilatore non possa srotolare il codice prodotto dal ciclo basato su intervallo.
C'è un modo per avere la velocità dell'originale e utilizzare ancora i cicli basati su intervalli?
Quello che ho provato:
posso ottenere la velocità desiderata con una gamma basata su ciclo for modificando due funzioni membro in NumericRange
:
bool operator !=(const NumericRange<N> & rhs) const {
return in_range();
// return ! (*this == rhs);
}
const NumericRange<N> & operator ++() {
advance();
// if (! in_range())
// _state = _upper;
return *this;
}
Tuttavia , questo codice si sente mal progettato perché il != operator
non funziona come previsto Normalmente per le operazioni numeriche, io uso <
per terminare un lo op piuttosto che ==
. Ho pensato di trovare il primo valore fuori range, ma farlo analiticamente potrebbe non dare una risposta esatta a causa di un errore numerico.
Come si può forzare il != operator
a comportarsi in modo simile a un <
senza fuorviare gli altri che vedranno il mio codice? Farei semplicemente le funzioni begin()
e end()
private, ma devono essere pubbliche per il ciclo basato su intervallo.
Grazie mille per il vostro aiuto.
Solo un suggerimento, nel ciclo 'for' basato sull'intervallo, perché non usare' auto'? Cioè 'for (auto arr: nr)'? –
@JoachimPileborg Hai perfettamente ragione, funzionerebbe e richiederebbe un minor numero di tasti. Stavo solo cercando di rendere il più chiaro possibile quello che stavo facendo (mostrare che il cambio di performance non era perché stavo copiando il risultato per valore diverse volte). – user
La parola chiave 'auto' dovrebbe anche selezionare il tipo * più appropriato *. – dirkgently