无论方向如何,都从一个整数循环到另一个整数,开销最小

时间:2012-12-13 21:37:26

标签: c++ algorithm

假设我有两个无符号整数:

size_t A, B;

它们加载了一些随机数,A可能比B更大,相等或更小。我想从A循环到B.但是,比较和增量都取决于哪个更大。

for (size_t i = A; i <= B; ++i) //A <= B
for (size_t i = A; i >= B; --i) //A >= B

明显的暴力解决方案是将这些内容嵌入if语句中:

if (A <= B)
{
 for (size_t i = A; i <= B; ++i) ...
}
else
{
 for (size_t i = A; i >= B; --i) ...
}

请注意,我必须将循环到 B,所以我不能有两个中间整数并将A和B放入正确的插槽中然后进行相同的比较和增量。在“A更大”的情况下,我必须减少,而相反的必须增加。

我可能会有许多嵌套循环需要相同的设置,这意味着每个if / else都会有一个函数调用,我必须传递大量的变量,或者另一个if / else与另一个if / else别的等等。

在不牺牲速度的情况下避免这种情况是否有任何棘手的捷径?功能指针和东西在一个紧凑的,经常重复的循环中听起来非常痛苦。是否有一些疯狂的模板解决方案?

4 个答案:

答案 0 :(得分:7)

我的错误,最初误解了这个问题。

要进行从AB的包容性循环,您会遇到棘手的情况。你需要循环一个过去 B.所以你在循环之前计算出这个值。我在for循环中使用了逗号运算符,但为了清晰起见,您可以将它放在外面。

int direction = (A < B) ? 1 : -1;
for( size_t i = A, iEnd = B+direction; i != iEnd; i += direction ) {
    ...
}

如果您不介意修改AB,则可以执行此操作(使用A作为循环变量):

for( B+=direction, A != B; A += direction ) {

}

我有一个游戏......当涉及到函数指针时,不知道内联规则是什么,或者这是否更快,但它无论如何都是一个练习。 =)

inline const size_t up( size_t& val ) { return val++; }
inline const size_t down( size_t& val ) { return val--; }

typedef const size_t (*FnIncDec)( size_t& );

inline FnIncDec up_or_down( size_t A, size_t B )
{
    return (A <= B) ? up : down;
}

int main( void )
{
    size_t A = 4, B = 1;
    FnIncDec next = up_or_down( A, B );

    for( next(B); A != B; next(A) ) {
        std::cout << A << endl;
    }

    return 0;
}

回应:

  

这不适用于案例A = 0,B = UINT_MAX(反之亦然)

这是正确的。问题是由于溢出,iiEnd的初始值变得相同。要处理这个问题,您可以使用do->while循环。这将删除初始测试,这是多余的,因为您始终至少执行一次循环体...通过删除第一个测试,您迭代过去终止条件第一次。

size_t i = A;
size_t iEnd = B+direction;

do {
    // ...
    i += direction;
} while( i != iEnd );

答案 1 :(得分:5)

size_t const delta = size_t(A < B? 1 : -1);
size_t i = A;
for( ;; )
{
    // blah

    if( i == B ) { break; }
    i += delta;
}

答案 2 :(得分:2)

你打算怎么处理迭代值?

如果这将成为数组中的某个索引,则应使用相关的iteratorreverse_iterator类,并围绕这些类实现算法。您的代码将更强大,更易于维护或发展。此外,标准库中的许多工具都是使用这些接口构建的。

实际上,即使不这样做,也可以实现一个返回自己索引的迭代器类。

您还可以使用一点元编程魔术来定义迭代器的行为方式,按照AB的顺序。

在进一步说明之前,请考虑这只适用于AB常量值

template <int A,int B>
struct ordered {
    static const bool value = A > B ? false: true;
};

template <bool B>
int pre_incr(int &v){
    return ++v;
}

template <>
int pre_incr<false>(int &v){
    return --v;
}

template <int A, int B>
class const_int_iterator : public iterator<input_iterator_tag, const int>
{
    int p;
  public:
    typedef const_int_iterator<A,B> self_type;
    const_int_iterator() : p(A) {}
    const_int_iterator(int s) : p(s) {}
    const_int_iterator(const self_type& mit) : p(mit.p) {}
    self_type& operator++() {pre_incr< ordered<A,B>::value >(p);return *this;}
    self_type operator++(int) {self_type tmp(*this); operator++(); return tmp;}
    bool operator==(const self_type& rhs) {return p==rhs.p;}
    bool operator!=(const self_type& rhs) {return p!=rhs.p;}
    const int& operator*() {return p;}
};


template <int A, int B> 
class iterator_factory {    
  public:
    typedef const_int_iterator<A,B> iterator_type;
    static iterator_type begin(){
        return iterator_type();
    }
    static iterator_type end(){
        return iterator_type(B);
    }
};

在上面的代码中,我定义了一个准确的iterator类,它从A到B的值中进行。有一个简单的元编程测试来确定A和B是否按升序排列,并选择正确的运算符({{ 1}}或++)来查看值。

最后,我还定义了一个简单的工厂类来保存--begin迭代器方法。使用这个类,你只能为依赖类型值A和B提供一个单一的声明点(I这里的意思是你只需要为这个容器使用A和B一次,并且从那里生成的迭代器将取决于这些相同的A和B,从而稍微简化了代码)。

这里我提供了一个简单的测试程序,输出20到11之间的值。

end

但是,使用标准库可能有更好的方法。

提出了对#define A 20 #define B 10 typedef iterator_factory<A,B> factory; int main(){ auto it = factory::begin(); for (;it != factory::end();it++) cout << "iterator is : " << *it << endl; } O使用UINT_MAXA的问题。我认为应该可以通过使用这些特定值重载模板来处理这些情况(作为读者的练习)。

答案 3 :(得分:0)

size_t A, B;

if (A > B) swap(A,B);  // Assuming A <= B, if not, make B to be A

for (size_t i = A; A <= B; ++A) ...