C#以int数组为索引过滤for循环的更快方法?

Posted

技术标签:

【中文标题】C#以int数组为索引过滤for循环的更快方法?【英文标题】:C# Faster way to filter for loop with array of int as index? 【发布时间】:2019-09-13 10:29:27 【问题描述】:

对不起,如果这是重复的,这里的第一个问题...

我想对大量称为 notes 的结构进行操作。 但我不想对音符的每个元素进行操作。我正在尝试使用 int 数组 (int[]) 的 filter 来跳过其中的很多内容,如下面的代码所示。

Note[] notes = new Note[]
 
   // Struct stuff ... 
;

int[] filter = new int[] 4,20,50,367... ;

for (int i = 0; i < notes.Length; i++)

     bool flag = false;
     for (int j = 0; j < filter.Length; j++)
     
          if (i == filter[j])
          
               flag = true;
               break;
          
      

      if (flag) continue;
      // Do something on notes[i]


问题是,当notes 数组和filter 数组都扩展时,代码会运行真的很慢(我认为)。 那么,有没有更好、更快的方法来做到这一点?请注意,过滤器的大小可以是任何基于其他条件的东西

【问题讨论】:

错字?你的意思是if (notes[i] == filter[j]) 而不是if (i == filter[j]) @DmitryBychenko 不是错字,我使用索引来过滤注释[i],但如果它是if (notes[i] == filter[j]),它不应该导致错误,因为 (struct == int)?? 【参考方案1】:

我们可以在HashSet&lt;int&gt; 的帮助下摆脱内循环,同时拥有更好的O(|filter| + |notes|)时间复杂度,而不是最初的O(|filter| * |notes|)

Note[] notes = new Note[]  
  ... //Struct stuff 
;

int[] filter = new int[]  
  4, 20, 50, 367... 
;

HashSet<int> toExclude = new HashSet<int>(filter);

for (int i = 0; i < notes.Length; i++) 
  if (toExclude.Contains(i)) // O(1) time complexity 
    continue;

  //Do something on notes[i] 

【讨论】:

你真的声称HashSet.Contains() 的复杂度为O(1)吗?它只会在更大的集合上表现更好,它仍然会趋向于 log n @Raul Sebastian:这取决于GetHashCode 的实现;如果 filter 中的项目是随机的,微软的就足够了。当然 adversary 可以这样的项目使得我们有很多哈希冲突和低效toExclude.Contains @Dmitry Bychenko 感谢您让我查找。看起来 HashSet 查找的复杂性确实保持不变,而在最坏的情况下它是线性的。【参考方案2】:

你可以像这样使用 Linq 过滤笔记:

Note[] notes = new Note[] ...//Struct stuff ;
int[] filter = new int[] 4,20,50,367... ;

var filteredNotes = notes.ToList().Where(note => !filter.Contains(note.Id)).ToList();

foreach(var note in filteredNotes)

//Do something on note

您需要测试性能,因为 Linq 在特定情况下往往很慢。

【讨论】:

Ermm,实际上,这与我想要做的完全相反,因为我的过滤器意味着“跳过这个” 对不起,我们应该迭代所有索引,但是过滤器(即0, 1, 2, 3, 5,...19, 21, ...)。请注意提供的代码中的if (flag) continue; @TheSorrowRaven 对不起,我误解了你。让我编辑它 最终实现.ToList() 冗余foreach 将与IEnumerable&lt;Node&gt; 一起使用【参考方案3】:

您可以循环过滤器数组并创建一个新的布尔数组,其中包含您想要跳过的所有元素为真。

bool[] filterArray= new bool[notes.Length];
foreach(var index in filter)

   if(index<filterArray.Length)
       filterArray[index]=true;

那么你只需要检查这个数组的索引。

for (int i = 0; i < notes.Length; i++)

     if(!filterArray[i])
     //Do something on notes[i]
     


这段代码的复杂度为O(m+n*X),其中m 是过滤器数组的长度,n 是节点数组的长度,X 是您对notes[i] 的操作的复杂度。假设 mO(n*X)。

你现在的复杂度是O(m*n*X)

【讨论】:

以上是关于C#以int数组为索引过滤for循环的更快方法?的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中将 int[] 转换为 byte[]

C#、For 循环和速度测试... 完全相同的循环第二次更快?

C# - 在for循环中使用相同的列表大小,索引超出了数组的范围[重复]

c#入门教程(五)foreach 循环

如何在 C# 字典中按值索引过滤项目?

foreach用法