2014-12-19 54 views
6

N boyutlu bir ızgara üzerinde çalışıyorum.
Herhangi bir boyuta (2B, 3B, 4B, vb.) Bağlı olarak yuvalanmış döngüler oluşturmak istiyorum.
Bunu zarif ve hızlı bir şekilde nasıl yapabilirim? Problemimin basit bir resminin altında. C++ 'da yazıyorum ama bu tür soruların diğer diller için yararlı olabileceğini düşünüyorum.
Yapmam gerekenler bölümündeki endeksleri (i, j, k ...) bilmem gerekiyor. Düzenleme: lower_bound ve upper_bound, kılavuzdaki dizinleri temsil eder, böylece her zaman pozitif olurlar.Değişken iç içe döngüler

#include <vector> 

int main() 
{ 
    // Dimension here is 3D 
    std::vector<size_t> lower_bound({4,2,1}); 
    std::vector<size_t> upper_bound({16,47,9}); 

    for (size_t i = lower_bound[0]; i < upper_bound[0]; i ++) 
     for (size_t j = lower_bound[1]; j < upper_bound[1]; j ++) 
      for (size_t k = lower_bound[2]; k < upper_bound[2]; k ++) 
       // for (size_t l = lower_bound[3]; l < upper_bound[3]; l ++) 
       // ... 
       { 
        // Do stuff such as 
        grid({i,j,k}) = 2 * i + 3 *j - 4 * k; 
        // where grid size is the total number of vertices 
       } 
} 
+0

... ve '// Do şeylere gerçekten emin 'kısım? – Wolf

+0

Ne demek istiyorsun? Herhangi bir boyut için genel olan grid gibi [[i, j, k}] dizin ızgaramı arıyorum. Belki farklı yapabilirim ama yine de – coincoin

+1

Recursion cevabı ile ilgileniyorum. –

cevap

4

yardımcı olabilir takiben

int main() { 
    const std::vector<int> lower_bound({4,2,1}); 
    const std::vector<int> upper_bound({6,7,4}); 
    std::vector<int> current = lower_bound; 

    do { 
     std::copy(current.begin(), current.end(), std::ostream_iterator<int>(std::cout, " ")); 
     std::cout << std::endl; 
    } while (increment(current, lower_bound, upper_bound)); 
} 

Live demo

+0

Bu gerçekten hoş görünüyor ... Bu teşekkürler deneyeceğim! – coincoin

+0

Bu benim amacım için gerçekten iyi çalışıyor, tekrar teşekkürler! Sadece Clang derleyicisini kullanarak bir sanitizer mesajından bahsetmek için: * çalışma zamanı hatası: imzasız tamsayı taşması: 0 - 1 'unsigned long' * tipinde gösterilemez *. İlgili satır * için (auto i = current.size(); i -! = 0;) *. – coincoin

+1

@coincoin: Bu iletiyi kaldırmak için, döngü gövdesindeki azalmayı taşıyabilirsiniz. – Jarod42

1

Yinelemeli bir işlev, istediğiniz şeyi elde etmenize yardımcı olabilir. Geçerli endekslerini bilmek gerekiyorsa

void Recursive(int comp) 
{ 
    if(comp == dimension) 
    { 
     // Do stuff 
    } 
    else 
    { 
     for (int e = lower_bound[comp]; e < upper_bound[comp]; e++) 
      Recursive(comp+1); 
    } 
} 

Bazı eklemeler işlev imzası gerekli olabilir (i, j, k, ...) senin "Do Stuff" bölümünde.

Bu, bu endekslerin

void Recursive(int comp, int dimension) 
{ 
    static std::vector<int> indices; 
    if(comp == 0) // initialize indices 
    { 
     indices.clear(); 
     indices.resize(dimension, 0); 
    } 

    if(comp == dimension -1) 
    { 
     // Do stuff 
    } 
    else 
    { 
     int& e = indices[comp]; 
     for (e = lower_bound[comp]; e < upper_bound[comp]; e++) 
      Recursive(comp+1); 
    } 
} 

Bunun nedeni paylaşılan statik vektöre Ancak birden fazla konu boyunca kullanılabilir değil erişmesini temiz bir yoldur.

+0

Teşekkürler Bu fikri sevdim ama bu güncel endeksleri almam gerekiyor. Bunu yapmanın güzel bir yolunu bulmaya çalışıyorum. – coincoin

+0

Bir [Variadic Argument] (http://en.cppreference.com/w/cpp/language/variadic_arguments) kullanabilir veya bu endeksleri depolamak için bir vektörü geçirebilirsiniz. Ya da onları, çağıran sınıfın bir üyesi gibi erişilebilir bir alanda tutabilirsiniz. –

1

Muhtemelen bazı yazım hataları olabilir, ancak tüm aralığı düzleştirirdim.

Bu

yüzden de o tarafa kendi

std::vector<int> lower_bound{-4,-5,6}; 
std::vector<int> upper_bound{6,7,4}; 

//ranges 
std::vector<int> ranges; 
for (size_t i = 0; i < lower_bound.size(); i++) { 
    ranges.push_back(upper_bound[i]-lower_bound[i]); 
} 

for (int idx = 0; idx < numel; idx++) { 
    //if you don't need the actual indicies, you're done 

    //extract indexes 
    int idx2 = idx; 
    std::vector<int> indexes; 
    for (int i = 0; i < ranges.size(); i++) { 
     indexes.push_back(idx2%ranges[i]-lower_bound[i]); 
     idx2 = idx2/ranges[i]; 
    } 
    //do stuff 
    grid[idx] = 2 * indexes[0] + 3 *indexes[1] - 4 * indexes[2]; 
} 

Edit dönebilirsiniz aralık

x_0 + d_0*(x_1+d_1*(x_2+d_2....) 

olarak tarif edilebilir fikrine dayanmaktadır: daha genel olması:

template <typename D> 
void multi_for(const std::vector<int>& lower_bound, const std::vector<int> upper_bound, D d) { 
    std::vector<int> ranges; 
    for (size_t i = 0; i < lower_bound.size(); i++) { 
     ranges.push_back(upper_bound[i]-lower_bound[i]); 
    } 
    size_t numel = std::accumulate(ranges.begin(), ranges.end(), std::multiplies<int,int>{}); 

    for (int idx = 0; idx < numel; idx++) { 
     //if you don't need the actual indicies, you're done 

     //extract indexes 
     int idx2 = idx; 
     std::vector<int> indexes; 
     for (int i = 0; i < ranges.size(); i++) { 
      indexes.push_back(idx2%ranges[i]-lower_bound[i]); 
      idx2 = idx2/ranges[i]; 
     } 
     //do stuff 
     d(idx,indexes); 
    } 
} 
//main 
size_t* grid;//initialize to whateer 

std::vector<int> lower_bound{-4,-5,6}; 
std::vector<int> upper_bound{6,7,4}; 

auto do_stuff = [grid](size_t idx, const std::vector<int> indexes) { 
    grid[idx] = 2 * indexes[0] + 3 *indexes[1] - 4 * indexes[2]; 
}; 

multi_for(lower_bound,upper_bound,do_stuff); 
+0

Teşekkürler Gerçekten de koordinatlara ve onun karşılıklı koordinatlarına endeks veren indeks veren fonksiyonlara sahibim. Ama benim yaptığım işlerde indekslere ihtiyacım olduğu için ihtiyaçlarma uymuyor. – coincoin

+0

@coincoin Endeksleri ayıkladığından neden ihtiyaçlarınızı karşılamadığı konusunda kafam karıştı. Kodunuza "şeyler satırını" ekleyeceğim. – IdeaHat

+0

Ben senin yaklaşımını deneyeceğim Ben gerçekten uygun olabileceğini düşünüyorum. – coincoin

1

Yinelemeli bir yaklaşım şöyle görünebilir:

#include <iostream> 
#include <vector> 

int main() 
{ 
    std::vector<int> lower_bound({-4, -5, -6}); 
    std::vector<int> upper_bound({ 6, 7, 4}); 

    auto increase_counters = [&](std::vector<int> &c) { 
    for(std::size_t i = 0; i < c.size(); ++i) { 
     // This bit could be made to look prettier if the indices are counted the 
     // other way around. Not that it really matters. 
     int &ctr = c   .rbegin()[i]; 
     int top = upper_bound.rbegin()[i]; 
     int bottom = lower_bound.rbegin()[i]; 

     // count up the innermost counter 
     if(ctr + 1 < top) { 
     ++ctr; 
     return; 
     } 

     // if it flows over the upper bound, wrap around and continue with 
     // the next. 
     ctr = bottom; 
    } 

    // end condition. If we end up here, loop's over. 
    c = upper_bound; 
    }; 

    for(std::vector<int> counters = lower_bound; counters != upper_bound; increase_counters(counters)) { 
    for(int i : counters) { 
     std::cout << i << ", "; 
    } 
    std::cout << "\n"; 
    } 
} 

... bununla birlikte veya özyinelemeli bir yaklaşımın daha zarif olmasına rağmen kullanım durumuna bağlıdır.

bool increment(
    std::vector<int>& v, 
    const std::vector<int>& lower, 
    const std::vector<int>& upper) 
{ 
    assert(v.size() == lower.size()); 
    assert(v.size() == upper.size()); 

    for (auto i = v.size(); i-- != 0;) { 
     ++v[i]; 
     if (v[i] != upper[i]) { 
      return true; 
     } 
     v[i] = lower[i]; 
    } 
    return false; 
} 

Ve bu şekilde kullanın::

+0

Yardımlarınız için tekrar teşekkür ederiz. Yaklaşımınız da güzel görünüyor! – coincoin

1
#include <iostream> 
#include <vector> 

template <typename Func> 
void process(const std::vector<int>& lower, const std::vector<int>& upper, Func f) 
{ 
    std::vector<int> temp; 
    process(lower, upper, f, 0, temp); 
} 

template <typename Func> 
void process(const std::vector<int>& lower, const std::vector<int>& upper, Func f, 
    int index, std::vector<int>& current) 
{ 
    if (index == lower.size()) 
    { 
     f(current); 
     return; 
    } 

    for (int i = lower[index]; i < upper[index]; ++i) 
    { 
     current.push_back(i); 
     process(lower, upper, f, index + 1, current); 
     current.pop_back(); 
    } 
} 

int main() 
{ 
    // Dimension here is 3D 
    std::vector<int> lower_bound({-4, -5, 6}); 
    std::vector<int> upper_bound({6, 7, 4}); 
    // Replace the lambda below with whatever code you want to process 
    // the resulting permutations. 
    process(lower_bound, upper_bound, [](const std::vector<int>& values) 
    { 
     for (std::vector<int>::const_iterator it = values.begin(); it != values.end(); ++it) 
     { 
      std::cout << *it << " "; 
     } 
     std::cout << std::endl; 
    }); 
} 
+0

Yardımlarınız için teşekkür ederiz! – coincoin