更新数组中给定范围的单元格值的有效方法?

时间:2014-06-03 08:09:09

标签: c++ c arrays

A有一组灯 - > l1,l2,l3,l4,..... lx等。

在任何给定的时间点,只有一连串的灯光亮起。例如:

l2,l3,l4可以打开。或者l9,l10,l11可以打开。 但是两者之间不可能有差距。例如,你不能拥有l3,l7,l8。

在时钟的每个刻度线上,我都告诉了序列中的起始灯和终点灯,我需要确保只有该范围内的灯亮。 (这意味着我可能必须打开或关闭灯光,具体取决于前一个灯光打开和关闭的灯光。如果灯光已经打开或关闭,那么我不需要更新这些灯光。)。< / p>

计算哪些灯打开和关闭的最快方法是什么? 我已经提出了许多解决方案,但无法确定哪种解决方案最好:

  1. 线性方法: 在这个解决方案中,我迭代了l0 ... lx中的所有灯光。我检查每个灯是否在范围内。如果是的话,我确保它打开,如果没有,我确保它关闭。这很容易实现,但如果没有变化则更加浪费,更糟糕的是,如果范围是最终的(例如:l9,l10,l11等)

  2. 系列的if: 在这种方法中,我在时钟的前一个刻度中跟踪范围的开始/结束,并将其与当前刻度的范围进行比较。

    • 如果范围相同,则不执行任何操作。 (LS,US)
    • 如果下限增加,则将灯转到新的下限。 (LI)
    • 如果上限增加,则打开灯直到新的上限。 (UI)
    • 如果下限减少,请将灯从新的下限打开到旧的(ld)
    • 如果上限减小,则将灯从旧的上限关闭到新的上限。 (UD)。如您所见,在方法(2)中,有九种可能的变化组合。所以第二种方法有很多if,所以我不清楚编码的最佳方法是什么(有9块if else块? - 看起来很乱):
  3. li,ui li,us li,ud ls,ui ls,us ls,ud ld,ui ld,us ld,ud

  4. 指针方法 - 在这种方法中,我创建两个范围的数组..

  5. 例如:数组1:{3,4,5}     阵列2:{4,5,6,7}

    我使用p,q指向我正在观察的每个数组中的当前单元格。

    我从两个数组的最低索引开始读取 如果array1 [p]&lt; array2 [q],然后我转向索引array1 [p]处的光,并递增p 如果array1 [p] == array2 [q],那么我增加p和q,因为光已经打开了。 如果array1 [p]&gt; array2 [q],然后我打开array2 [q]处的灯,然后递增q。 我可以通过检查开始/结束索引是否相同来优化这种方法。

    我认为方法3是最好的 - 因为代码最容易阅读并且看起来很快。 我可以实现所有3,但不能决定哪种方法最好。希望了解更好的方法,或如何从这三个方法中进行选择并优化它们。可以在我的应用程序中使用C或C ++。

    编辑:两个连续刻度线的范围可能相差很远,大小也不同。例如:刻度线1可以是{3,4,5},刻度线2可以是{7,8,9,10}。所以在这个例子中,我必须在3,4,5关灯,然后在7,8,9,10开启。

    编辑:&#34;灯&#34;这是我用来解释问题的类比。灯光只是指软件中的对象。

    编辑:我只能访问一个线程,因此无法并行化其中的任何部分。

7 个答案:

答案 0 :(得分:1)

我会从

的方法开始
  • 最容易阅读(因为可读性很重要)和
  • 最容易实施(因为您需要快速的结果/反馈)

然后进行分析。如果这个算法足够,那么你就完成了。如果没有,继续下一步。

答案 1 :(得分:1)

这是一个人为的问题,还是你通过一个端口物理控制灯光?

事实上,只有一个连续的序列可以跟踪,这使得这个问题变得简单。我认为最好的是选项2.这样的事情应该处理重叠和非重叠范围的所有情况:

if( old_right < new_left || old_left > new_right ) {
    // Non-overlapping
    for( int i = old_left; i <= old_right; i++ ) set_light_state( i, OFF );
    for( int i = new_left; i <= new_right; i++ ) set_light_state( i, ON );
} else {
    // Overlapping
    for( int i = old_left; i < new_left; i++ ) set_light_state( i, OFF );
    for( int i = new_left; i < old_left; i++ ) set_light_state( i, ON );
    for( int i = old_right+1; i <= new_right; i++ ) set_light_state( i, ON );
    for( int i = new_right+1; i <= old_right; i++ ) set_light_state( i, OFF );
}

此解决方案应该只修改那些必要的灯光,而对分支预测效率低下的损失最小(如果这很重要)。我假设要解决的问题是最小化状态变化的数量。

答案 2 :(得分:0)

现在提出问题,我的第一个想法就是直接在循环中使用起始灯和终点灯。

for (i = startinglight ; i <= finishinglight ; i++){
    lightarray[i-1] * turn on lights * ;
}

...但由于这很容易,也许我在问题中遗漏了一些东西 (当然,如果数组中灯光的数量从1开始并且是连续的,它也只能起作用)

答案 3 :(得分:0)

我会确保对这些值进行比特打包,即不要进行操作。

bool lightStatus[NUMBER_OF_LIGHTS];

DO

uint64_t lightStatus[(NUMBER_OF_LIGHTS + 63) / 64];

假设您使用的是64位处理器。否则,请使用uint32_t。这将确保您在读/写时不会浪费任何位,因为lightStatus的所有位实际上都用于编码灯的状态。

只需执行整个阵列的memset(),它也可以非常有效地清除所有灯光。即使你冒着不必要的清除风险,这也可能足够快。

关于更新,我做的很明显,因为它基本上都是核心信息:存储当前范围,这样无论何时你得到一个新的,你都可以准确地清除旧的。如果事实证明它是一个瓶颈你可以得到花哨并消除重叠当然但我不担心,除非这是在一个非常慢的CPU上运行或有(很多?)数千个灯。

答案 4 :(得分:0)

一个简单的解决方案是for循环和if语句的组合:

其中X是灯光总数

int lightArray[X] = {0};
int startLight;
int endLight;

for (int i = 0, i < X, i++){
    if (i >= startLight && i <= endLight){
        if (lightArray[i] == 0){                 
            lightArray[i] = 1; // or you can change the lights in this loop
        }
    }
    else{
        lightArray[i] = 0; // or you can change the lights in this loop
    }
} 

for (int i = 0, i < x, i++){
    //some function that sets lights on and off based on lightArray[i]
}

答案 5 :(得分:0)

您可以将灯光置于二进制值数组{0,1}中,例如ar

然后,如果[first, last)是灯光所在的范围,则将数组拆分为3个范围​​[开始,第一个],[第一个,最后一个],[最后一个,结束],(每个范围可以是空的)。

begin = &ar[0];
end   = &ar[N];

最后,您为每个范围的每个elememt运行一个操作

for_each(begin, first, [](int i) { i &= 0x0; });
for_each(first, last , [](int i) { i |= 0x1; });
for_each(last,  end  , [](int i) { i &= 0x0; });

第一个和最后一个循环将状态设置为0,第二个循环将状态设置为1


类似的解决方案会将所有值保存在bitset而不是数组中,您可以使用成员函数setflip,但您又可以使用循环遍历每个位的值。

答案 6 :(得分:0)

嗯,因为这个问题在今天引起了我的兴趣,并且假设这只是一个思考练习,我已经提出了一些代码...

我申请的标准(并非100%确定这是否与原始海报的要求完全一致):

  1. 仅迭代需要状态更改的灯光
  2. 最小化需要存储的数据
  3. 永远不要打开任何已打开的灯。
  4. 永远不要关闭任何已关闭的灯。
  5. 假设turnOn()和turnOff()方法可能很昂贵
  6. 首先,观察在任何两个光更新之间,发生7种可能的状态变化之一,如这个基本图片所示:

    diagram of light state changes

    1. 灯亮,完全在当前灯光范围内。
    2. 灯光亮起时,与当前打开的整个序列完全重叠。
    3. 灯光亮起与当前开启的序列重叠。
    4. 灯光亮起与正在亮起的序列的末端重叠。
    5. 灯光亮起完全在当前灯光范围之前。
    6. 灯光亮起完全在当前灯光范围之后。
    7. 所有灯都关闭
    8. 这是我的实施......

      我们只跟踪当前灯光的开始和结束范围(在成员变量 start end 中)。

      请注意,所有范围都包括start但排除end,因此如果start = 2且end = 5,则表示灯2,3和4已打开。

      int start;
      int end;
      
      void Switcher::toggleLights(int new_start, int new_end) {
      
          if( new_start > start && new_end < end ) {
              //case 1: new lights are within range of lights that are already on           
              turnOffRange(start, new_start);       
              turnOffRange(new_end, end);                     
          }       
          else if( new_start < start && new_end > end) {
              //case 2: lights already on are entirely within new range       
              turnOnRange(new_start, start);
              turnOnRange(end, new_end);           
          }
          else if( new_start < start && new_end > start ) {
              //case 3: new light sequence overlaps start of current range
              turnOffRange(new_end, end);
              turnOnRange(new_start, start);            
          }
          else if( new_start < end && new_end > end ){
              //case 4: new light sequence overlaps end of current range
              turnOffRange(start, new_start);
              turnOnRange(end, new_end);            
          }   
          else {
              //case 5,6 or 7 (no overlap at all)
              turnOffRange(start,end);       
              turnOnRange(new_start,new_end); 
          }
      
          // keep track of the lights that are now on
          start = new_start;
          end = new_end;
      }
      
      void Switcher::turnOffRange(int from, int to) {
          for(int i=from; i < to; ++i) {
              turnOff(i); // (potentially expensive) method to turn light off
          }
      }
      
      void Switcher::turnOnRange(int from, int to) {
          for(int i=from; i < to; ++i) {
              turnOn(i); // (potentially expensive) method to turn lamp on
          }
      }