假设我有一个布尔数组:
{false, true, false, false, true, true, ...}
获取(例如)假元素索引的最快方式(最优化)是什么?在这种情况下0 2 3
?
答案 0 :(得分:13)
for循环可能是最快的方式:
List<int> indices = new List<int>();
for (int i=0;i < theArray.Length; ++i)
{
if (theArray[i])
{
indices.Add(i);
}
}
请注意,通过预先分配List<int>
,您可能会以额外内存为代价获得一点点速度:
List<int> indices = new List<int>(theArray.Length);
这将避免额外的内存分配。
答案 1 :(得分:5)
最多32个元素:
int mask = 0;
for (int i = 0; i < arr.Length; i++)
{
if (!arr[i]) mask += 1 << i;
}
掩码将是一个32位掩码,如果该位索引处的元素为假,则每个位为1;如果该元素为真,则为0。它是数组的另一种表示,如果您希望这样说,则使用四个字节而不是每个布尔值一个字节。对于最多64个元素,您可以使用long
类型。但是,据我记忆,使用int
,您可以将enum
转换为适当的位掩码。
涉及的总字节数:掩码四个,数组中每个元素一个,循环中索引四个。完成的总分配:两个(如果我们不计算数组的分配)。
答案 2 :(得分:1)
在您获得经验证据之前,您永远不会知道最快的可能解决方案。您可以使用下一个代码作为LINQ和for
循环方法的计算速度比较的参考:
var r = new Random();
bool[] vals = new bool[100000000];
//initializing
for (int i = 0; i < vals.Length; i++)
{
vals[i] = r.Next(2)==0;
}
var watch = Stopwatch.StartNew();
//for loop benchmark
List<int> indices = new List<int>(vals.Length);
for(int i = 0; i < vals.Length; ++i)
{
if(!vals[i])
indices.Add(i);
}
Console.WriteLine ("for loop: {0} ms", watch.ElapsedMilliseconds);
watch.Restart();
//LINQ benchmark
List<int> falseIndices = vals.Where(flag => !flag)
.Select((flag, index) => index)
.ToList();
Console.WriteLine ("LINQ: {0} ms", watch.ElapsedMilliseconds);
打印出来的东西:
for loop: 600 ms
LINQ: 2072 ms
答案 3 :(得分:1)
这可能不是最快的方法,但它产生的IEnumerable只是真正的指数。这对我来说似乎有些混乱。我想知道它是否可以简化? for循环可能是最好的。但是为了它的价值:
var bools = new bool[] {true, false, true, true, false, false, true, false, true};
var falseIndices = bools.Select((b, i) => new { Index = i, Value = b })
.Where(o => !o.Value)
.Select(o => o.Index);
答案 4 :(得分:0)
最快的方法可能包括使用fixed
固定数组。但是结果代码不太可读。
你应该确保“真正找到false
元素的索引”是你的应用程序的瓶颈,并找到“最快的方法”真正改善它。可读性和可维护性也是非常重要的方面。
正如其他人所说,一个简单的for循环具有良好的性能并且非常易读。 Linq解决方案不会慢得多。
如果数组中包含很多true
而不是false
,那么您可以考虑为每个Array.IndexOf()
使用false
,直到您找不到更多事件为止。它可能比每元素方法更快。
答案 5 :(得分:0)
请尝试以下操作:
BlockingCollection<int> indexes = new BlockingCollection<int>(x.Length);
Parallel.For(0, x.Length, i =>
{
if (x[i])
{
indexes.Add(i);
}
});
答案 6 :(得分:0)
我也做了一些时间,它表明人们所说的是正确的: 使用指针并不比简单的循环快得多。
我怀疑只需填充结果列表就可以占用最多的时间。
以下是我从以下测试程序中获得的时间:
Unsafe took 00:00:06.6450271
Safe took 00:00:06.7691818
测试代码如下......
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Demo
{
class Program
{
void Run()
{
int size = 10000000;
bool[] array = new bool[size];
for (int i = 0; i < size - 1; ++i)
array[i] = true;
int count = 100;
time(() => UnsafeCountSetFlags(array), "Unsafe", count);
time(() => SafeCountSetFlags(array), "Safe", count);
}
void time(Action action, string title, int count)
{
var sw = Stopwatch.StartNew();
for (int i = 0; i < count; ++i)
action();
Console.WriteLine(title + " took " + sw.Elapsed);
}
List<int> UnsafeCountSetFlags(bool[] array)
{
unsafe
{
fixed (bool* parray = array)
{
List<int> result = new List<int>();
for (bool* p = parray, end = parray + array.Length; p != end; ++p)
if (*p)
result.Add((int)(p-parray));
return result;
}
}
}
List<int> SafeCountSetFlags(bool[] array)
{
List<int> result = new List<int>();
for (int i = 0; i < array.Length; ++i)
if (array[i])
result.Add(i);
return result;
}
static void Main()
{
new Program().Run();
}
}
}