7
votes

Initialisation d'un tableau de dimensionnalité inconnue

J'ai été surpris que je ne puisse pas trouver cette question existante. J'ai essayé de le généralaliser (avec un bon code non testé) à quelque chose que tout le monde peut bénéficier de.

Supposons que j'ai un point multidimensionnel code>: p> xxx pré>

Maintenant, je crée un tableau multidimensionnel d'entre eux: P>

//Example: dim==1
for (int x=0; x<counts[0]; ++x) {
    Point<1>& point = array[x];
    point.data[0] = (x+0.5) / (double)counts[0];
}

//Example: dim==2
for (int y=0; y<counts[1]; ++y) {
    for (int x=0; x<counts[0]; ++x) {
        Point<2>& point = array[y*counts[0]+x];
        point.data[0] = (x+0.5) / (double)counts[0];
        point.data[1] = (y+0.5) / (double)counts[1];
    }
}

//Example: dim==3
for (int z=0; z<counts[2]; ++z) {
    for (int y=0; y<counts[1]; ++y) {
        for (int x=0; x<counts[0]; ++x) {
            Point<3>& point = array[(z*counts[1]+y)*counts[0]+x];
            point.data[0] = (x+0.5) / (double)counts[0];
            point.data[1] = (y+0.5) / (double)counts[1];
            point.data[2] = (z+0.5) / (double)counts[2];
        }
    }
}


6 commentaires

Quelle est la source de valeurs? c'est-à-dire à quoi nous initialisons


@Drop "Initialisez ce tableau à ses points de grille multidimensionnels". Ensuite, voyez l'exemple de la suite?


Je l'ai, mais d'où obtenez-vous des valeurs exactes pour votre tableau de points? Juste à partir de ceci: point.data [i] = (v [i] +0.5) / (double) compte [i]; ? Je pense que je ne comprends pas ce qui est "ses points de grille multidimensionnels".


@Drop je pense que je ne comprends pas ce que vous demandez. Ce code exemple est paramètre les valeurs dans le tableau. Je les définis vers les indices de grille x , y et z (normalisé à [0,1]) - c'est-à-dire leur position dans la matrice multidimensionnelle.


Récursion pourrait toujours fonctionner, vous ne recueillez que au plus la quantité de dimensions, pourriez-vous mieux définir "High Dimensional"


Il devrait également être possible de le faire en inversant la cartographie de N DIM à 1 et simplement en boucle à travers la projection 1D, mais je devrai penser à ce sujet.


3 Réponses :


1
votes

Modifier en réponse au commentaire et question mise à jour

Si vous avez besoin de performance et de "élégance", je voudrais:

  • Déposez votre approche multidimensionnelle et aplatit-la (c'est-à-dire une dimension d'une matrice). Non Nouveau , Aucun pointeur, utilisez une approche moderne C ++ avec std :: vecteur ou std :: tableau . .
  • Fournissez un conteneur abstrait pour votre matrice multi-pompes avec des méthodes pratiques telles qu'une boucle imbriquée générique "Générateur"
  • Remplacez vos arguments variadiques avec une matrice de taille fixe (puisque vous savez Dims au moment de la compilation.

    J'ai donc trouvé une solution suivante qui est assez cohérente avec votre implémentation et vos besoins et essayez de rester simple.

    J'ai géré la réécriture d'une petite classe multi barreaux dans une «moderne C ++ 11 Way». Je considère ici que les dimensions ne sont peut-être pas connues à l'heure de la compilation d'où l'utilisation de std :: vecteur maintenant. Il est bien sûr possible d'avoir un code temporel plus générique et compilé avec std :: array voir ma réponse d'origine ci-dessous. xxx

    Vivez sur Coliru

    Comme vous pouvez le voir dans les résultats Data _ peut être initialisé de manière générale pour n dimensions. Si vous avez besoin d'une classe abstraite de niveau supérieur, vous pouvez vérifier ci-dessous ma réponse d'origine dans laquelle vous pouvez effectuer certaines opérations pratiques (c'est-à-dire accès à grille [{i, j, k}] pour remplir les valeurs).

    Réponse originale

    J'avais besoin d'une grille multidimensionnelle pour mes besoins et de poser des améliorations de mon code sur Code Examen . Voici un travail de travail exemple Où certaines fonctionnalités peuvent être inutiles pour vous ... Ma mise en œuvre se rapporte sur le modèle et compiler des calculs de temps. Notez que la taille des dimensions doit être connue au moment de la compilation.

    brièvement, la classe ressemblerait à ceci: xxx

    , puis vous pouvez construire votre réseau multidimensionnel et l'initialiser de ces manières: < Pré> xxx

    Si vous devez spécifier un algorithme pour des boucles imbriquées par variadiques pour remplir les valeurs que vous pouvez jeter un coup d'oeil ici et faites-le de cette façon avec la première réponse: xxx

    Si vous avez besoin de quelque chose que je manque dans ma mise en œuvre, n'hésitez pas à demander. Je serais aussi heureux que quelqu'un puisse souligner des améliorations.


5 commentaires

Comment cela généralisez-t-il des boucles nichées de profondeur?


Avec un modèle variadique, vous pouvez avoir un réseau multis dimensionnel. par exemple. Multigramme .


@COINCOIN GLENN indique le problème ici. Je peux facilement faire une matrice multidimensionnelle qui a l'air jolie, mais remplissage n'est toujours pas. Les tableaux imbriqués par exemple que j'ai donné sont le comportement Je veux Je veux, pas la mise en œuvre: je ne veut pas écrire à l'aide de boucles à la main pour toutes les dimensions possibles que je pouvais m'occuper d'utiliser . La difficulté est l'initialisation des points de grille, comme je l'ai montré dans mon exemple.


Qu'en est-il de mon deuxième exemple où vous pouvez remplir la matrice via les itérateurs (boucle unique)? Je pense qu'il est possible de le faire dépendre de la façon dont vous remplissez vos valeurs pouvez-vous expliquer comment vous souhaitez l'initialiser afin de voir si nous pouvons faire mieux?


La boucle unique pourrait fonctionner, mais seulement si vous pouvez mieux préciser comment. Le comportement est comme dans mon exemple ci-dessus. Dans pseudocode, pour chaque élément de tableau, do matry [A] [b] [c] ... [z] = nouveau point (A, B, C, ..., Z); .



0
votes

Voici ma solution, en utilisant des modèles de variadiques C ++ 11 et des extensions de paquet.

My "Foobar" compile comme une fonction consexpr code> Donc, je dirais que c'est assez efficace: p p >

comme pour l'élégance, vous pouvez être le juge, mais je pense que c'est plutôt bien. P>

Fondamentalement, l'idée est de remplacer pour code> boucles avec une programmation fonctionnelle de faire L'itération, où nous cherchons simplement à construire explicitement l'ensemble que nous voulons itérer sur la première. Ce code peut ensuite être généralisé à une dimensionnalité arbitraire de manière plus simple. P>

Le code est totalement autonome Enregistrer pour code> en-tête. P>

Il compile pour moi à la norme C ++ 11 avec GCC 4.8.2 et Clang 3.6. P>

Remarque: Si vous souhaitez utiliser la même technique pour le code où les dimensions sont un paramètre de temps d'exécution, essentiellement de quoi Vous feriez est de réilementer le cartésian_product code> métafonctionné ci-dessous sous la forme d'une fonction d'exécution à l'aide de quelque chose comme std :: vecteur > code>. Ensuite, vous pouvez construire votre itération définie en prenant le numéro Dim code> Nombre de produits Cartésiens et itérer sur celui utilisant un seul pour la boucle pour remplir le résultat. P>

#include <array>
#include <iostream>

/////////////////////////////////////////////
// The point class from problem statement
/////////////////////////////////////////////

template <size_t dims> class Point { public: double data[dims]; };

// Some basic type list and meta function objects to work with

// List of indices
template<size_t... sl>
struct StList {
    static constexpr size_t length = sizeof...(sl);
};

// Metafunction to compute a volume
template <typename T>
struct Compute_Volume;

template<size_t s>
struct Compute_Volume<StList<s>> {
    static constexpr size_t value = s;
};

template <size_t s1, size_t s2, size_t... sl>
struct Compute_Volume<StList<s1, s2, sl...>> {
    static constexpr size_t value = s1 * Compute_Volume<StList<s2, sl...>>::value;
};

// Concatenate
template<typename TL, typename TR>
struct Concat;

template<size_t... SL, size_t... SR>
struct Concat<StList<SL...>, StList<SR...>> {
    typedef StList<SL..., SR...> type;
};

template<typename TL, typename TR>
using Concat_t = typename Concat<TL, TR>::type;

// Meta function to check if a typelist is component-wise less than another typelist
// For testing purposes
template <typename TL, typename TR>
struct all_less_than;

template <size_t l1, size_t... SL, size_t r1, size_t... SR>
struct all_less_than<StList<l1, SL...>, StList<r1, SR...>> {
    static constexpr bool value = (l1 < r1) && all_less_than<StList<SL...>, StList<SR...>>::value;
};

template<>
struct all_less_than<StList<>, StList<>> {
    static constexpr bool value = true;
};

/////////////////////////////////////////////////////////////////////////////
// constexpr template function for the point initializations you described
/////////////////////////////////////////////////////////////////////////////
template <typename index, typename dims>
struct point_maker;

template <size_t... idx, size_t... dim>
struct point_maker<StList<idx...>, StList<dim...>> {
    static_assert(sizeof...(idx) == sizeof...(dim), "misuse of 'point_maker' template, idx and dim must have same number of coordinates");
    static_assert(all_less_than<StList<idx...>, StList<dim...>>::value, "misuse of 'point_maker' template, idx is out of bounds");
    static constexpr Point<sizeof...(idx)> make_point() {
        return {{ ((idx + 0.5) / static_cast<double>(dim))... }};
    }
};

//////////////////////////////////////////////////////////////////////////////////////////
// Now we need to abstract the for loops. We need a little more infrastructure for this.
//////////////////////////////////////////////////////////////////////////////////////////

// A basic typelist object
template <typename... tl>
struct TypeList {
    static constexpr size_t length = sizeof...(tl);
};

// Specialization for the Concat metafunction
template<typename... TL, typename... TR>
struct Concat<TypeList<TL...>, TypeList<TR...>> {
    typedef TypeList<TL..., TR...> type;
};


// Metafunction to compute the cartesian product of two lists of lists, and evaluate the `Concat` metafunction for each pair.
template <typename S, typename T>
struct Cartesian_Product;

template <typename s1, typename s2, typename... sl, typename T>
struct Cartesian_Product<TypeList<s1, s2, sl...>, T> {
    typedef Concat_t<typename Cartesian_Product<TypeList<s1>, T>::type, typename Cartesian_Product<TypeList<s2, sl...>, T>::type> type;
};

template <typename s, typename t1, typename t2, typename... tl>
struct Cartesian_Product<TypeList<s>, TypeList<t1, t2, tl...>> {
    typedef Concat_t<typename Cartesian_Product<TypeList<s>, TypeList<t1>>::type, typename Cartesian_Product<TypeList<s>, TypeList<t2, tl...>>::type> type;
};

template <typename s, typename t>
struct Cartesian_Product<TypeList<s>, TypeList<t>> {
    typedef TypeList<Concat_t<s, t>> type;
};

template <typename S, typename T>
using Cartesian_Product_t = typename Cartesian_Product<S, T>::type;

// Some unit tests for the above :)

static_assert( std::is_same<Cartesian_Product_t<TypeList<StList<1>, StList<2>>, TypeList<StList<3>, StList<4>>>, TypeList<StList<1,3>, StList<1,4>, StList<2,3>, StList<2,4>>>::value , "cartesian product not working");

static_assert( std::is_same<Cartesian_Product_t<TypeList<StList<1,5>, StList<2>>, TypeList<StList<3>, StList<4>>>, TypeList<StList<1,5,3>, StList<1,5,4>, StList<2,3>, StList<2,4>>>::value , "cartesian product not working");

static_assert( std::is_same<Cartesian_Product_t<TypeList<StList<1,5>, StList<2>>, TypeList<StList<>>>, TypeList<StList<1,5>, StList<2>>>::value , "cartesian product not working");

// Metafunction to count from 0 to n

// Count from zero to n-1, and make a sequence of singleton sets containing the numbers
template<size_t s>
struct Count {
    typedef Concat_t<typename Count<s-1>::type, TypeList<StList<s-1>>> type;
};

template<>
struct Count<0> {
    typedef TypeList<> type;
};

template<size_t s>
using Count_t = typename Count<s>::type;


// Metafunction to abstract a series of for loops, generating the list of all the index tuples the collection of loops would generate

template <typename T>
struct Volume_Maker;

template <>
struct Volume_Maker<StList<>> {
    typedef TypeList<StList<>> type;
};

template <size_t s, size_t... sl>
struct Volume_Maker<StList<s, sl...>> {
    typedef Cartesian_Product_t<Count_t<s>, typename Volume_Maker<StList<sl...>>::type> type;
};

template <typename T>
using Volume_t = typename Volume_Maker<T>::type;

// Some more quick unit tests

template <typename T>
struct Volume_Test {
    static_assert( Volume_t<T>::length == Compute_Volume<T>::value, "volume computation mismatch");
};

Volume_Test<StList<1,2,3>> test1{};
Volume_Test<StList<1,1,1>> test2{};
Volume_Test<StList<1>> test3{};
Volume_Test<StList<7,6,8>> test4{};

/////////////////
// Grand finale
/////////////////

template <typename dim_list, typename idx_list = Volume_t<dim_list>>
struct foobar_helper;

template <size_t... dim, typename... idx>
struct foobar_helper<StList<dim...>, TypeList<idx...>> {
    typedef StList<dim...> dim_list;
    typedef std::array<Point<sizeof...(dim)>, sizeof...(idx)> array_type;

    static constexpr array_type make_array() {
        return {{ point_maker<idx, dim_list>::make_point()... }};
    }
};

template <size_t... dim>
constexpr std::array<Point<sizeof...(dim)>, Compute_Volume<StList<dim...>>::value> foobar() {
    return foobar_helper<StList<dim...>>::make_array();
}

/////////////////
// Example usage
/////////////////

template <size_t ndim>
std::ostream & operator << (std::ostream & s, const Point<ndim> & p) {
    s << "[ ";
    for (size_t i = 0; i < ndim; ++i) {
        if (i) { s << ", "; }
        s << p.data[i];
    }
    s << " ]";
    return s;
}

template<size_t ndim, size_t N>
void print_array(std::ostream & s, const std::array<Point<ndim>, N> & a) {
    for (size_t i = 0; i < N; ++i) {
        s << a[i] << std::endl;
    }
}

int main() {
    constexpr auto array1 = foobar<2,2,2>();
    print_array(std::cout, array1);

    constexpr auto array2 = foobar<3,3,3,3>();
    print_array(std::cout, array2);
}


1 commentaires

Vous semblez être amusant aussi :)



1
votes

aller de x, y, z code> à la matrice aplatie (f) Nous avons l'équation suivante xxx pré>

ou p> xxx

dans un tableau 7 x 3 x 5, Z = 3, Y = 1, x = 2 serait à 3 code> * 3 * 5 + 1 code> 1 code> * 5 + 2 code> = 45 + 5 + 2 = 52 code> p> xxx pré>

dans un tableau 7 x 3 x 5, Z = 4, y = 2, x = 3 serait à 4 code> * 3 * 5 + 2 code> * 5 + 3 code> = 60 + 10 + 3 = 73 code> p> xxx pré>

Si nous conservons les produits cumulatifs dans un tableau, mul code> {1, X, x * y, x * y * z, ...} code> et tous les points d'une matrice, val code> p>

points à la matrice plate: p > xxx pré>

matrice plate à des points: p> xxx pré>

Nous pouvons ensuite itérer sur F (réseau plat), ingénieur inversé de l'index dans Le tableau plat Tout points: x, y, ... comme ci-dessus et que vous voulez l'initialisation que vous souhaitez dans une boucle générique: p>

donnée mul code> comme MUT [0] = 1; MUL [D + 1] = MUL [D] * Nombre [D]; CODE> P>

for (int i = 0; i < total_count; ++i) {
    for (int d=0; d < dims; ++d) {
      int dim=(i%mult[d+1])/mult[d];
      point.data[d] = (dim+0.5) / (double)counts[d];
  }
}


0 commentaires