什么';s是在byte[](C#)中搜索Int32的最快算法
本文关键字:搜索 Int32 算法 是在 byte 什么 | 更新日期: 2023-09-27 18:06:41
我想在一个大(50mb+(字节数组中搜索一个int。我应该使用什么算法?也许是一些不安全的方法?
编辑:它不是int数组,而是字节数组。数据没有以任何方式排序。
public IList<int> FindIntInBytes(byte[] bytes, int target)
{
IList<int> found = new List<int>();
unsafe
{
fixed (byte* pBytes = bytes)
{
byte* pCurrent = pBytes;
for (int i = 0; i <= bytes.Length - 4; i++, pCurrent++)
{
if (target == *(int*)pCurrent)
{
found.Add(i);
}
}
}
}
return found;
}
不会在big-endian体系结构上工作,但它们不用于大多数.Net应用程序。
根据阵列的大小和CPU的可用性,拆分为多个部分并在多个线程中运行,然后合并结果以获得更快的性能。
这是我的实现。在O(n(中工作;
int findInArray(byte[] array, int what)
{
byte[] toMatch = /* convert your dword to a 4 elements byte array */;
int matched = 0;
for(int i = 0; i < array.length; i++) {
if(array[i] == toMatch[matched]) {
matched += 1;
if(matched == 4) {
return i - 4;
}
}
else {
i -= matched;
matched = 0;
}
}
return -1;
}
从算法上讲,没有捷径可以搜索整个内容。在实现方面,如果性能很重要,那么最好的方法就是编写代码,尽可能避免内存读取、分支和函数调用。这将使编译器更容易生成高效的代码(尽管聪明的编译器无论如何都可能,而且当你编译到VM时,很难保证最终的机器代码,然后VM将其重新编译为机器代码来运行(。我的实现是这样的:
System.Collections.Generic.IEnumerable<int> FindIntInByteArray(int match, byte[] array) {
if (array.Length < 4) yield break;
byte b0 = 0;
byte b1 = array[0];
byte b2 = array[1];
byte b3 = array[2];
int len = array.Length;
for (int i=3;i<len;i++) {
b0 = b1;
b1 = b2;
b2 = b3;
b3 = array[i];
/* The following line should be changed depending on endian-ness or which
bytes are to be considered most significant. */
int comp = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
if (comp == match) yield return i-3;
}
}
您实际要做的是在字符串中查找子字符串。因此,您需要了解字符串搜索算法。
BlackBear的建议是一个天真的字符串搜索。例如,您也可以使用,Knuth–Morris–Pratt算法
如果对同一数据进行大量搜索,这听起来像是一项工作,您可能希望从数组中提取整数,并建立一个简单的哈希表或二进制树。数据库有索引也是出于同样的原因。根据您的指数,您可以获得N/2或更好的性能。
请参阅本文:数据库索引是如何工作的?
这篇文章:http://en.wikipedia.org/wiki/Binary_tree
如果你想走这条路,打开一个新的问题,关于哪一个更适合你正在处理的任务。
即使在.Net 2.0中,你也可以创建新的线程,使你能够并行搜索它。你没有提到你是否在寻找int的所有实例。我可以想出几种比直接搜索提高速度的方法,包括将数组预处理到字典中进行查找,等等。如果你总是使用同一个数组来查找数据,等等。
这里有一个方法。它只需要每隔4个字节查看一次,所以应该会稍微快一点。(如果您正在查找0x11223344,并且找到了0x55,则您知道目标整数根本不包含此字节。(应该是O(n/4(。
我没有运行这个,可能有语法错误或一个错误。
bool Compare4(byte[] searchIn, int offset, byte[] searchFor)
{
return searchIn[offset] == searchFor[0] &&
searchIn[offset+1] == searchFor[1] &&
searchIn[offset+2] == searchFor[2] &&
searchIn[offset+3] == searchFor[3];
}
/// Returns index if found, -1 if not found.
int FindIntInArray(int target, byte[] array)
{
byte[] targetArray = new byte[4];
targetArray[0] = target & 0xFF;
targetArray[1] = (target>>8) & 0xFF;
targetArray[2] = (target>>16) & 0xFF;
targetArray[3] = (target>>24) & 0xFF;
bool[] bytesUsed = new bool[256];
foreach(byte b in targetArray) bytesUsed[b] = true;
for(int i = 3; i < array.Length - 3; i += 4)
{
if(bytesUsed[array[i]])
{
if(Compare4(array, i-3, targetArray)) return i-3;
if(Compare4(array, i-2, targetArray)) return i-2;
if(Compare4(array, i-1, targetArray)) return i-1;
if(Compare4(array, i, targetArray)) return i;
}
}
return -1;
}
如果我能正确理解你的问题,你想在一个字节数组中搜索一个4字节的特定模式。下面的操作应该可以做到,在数组中的任何位置找到int值—没有关于对准的假设。
编辑以注意
- 它在O(n(时间内运行,并且
- 任何字节顺序问题都是您的问题
这是代码:
private static int FindIntValueInByteArray( int value , byte[] octets )
{
int matchPosition = -1 ; // assume no match
for ( int i = 0 ; i < octets.Length-3 ; ++i )
{
int t = BitConverter.ToInt32( octets , i ) ;
if ( t == value )
{
matchPosition = i ;
break ;
}
}
return matchPosition ;
}
public static class ByteListExtensions
{
public static IEnumerable<int> AllIndexesOf(this IList<byte> source, int match, bool isLittleEndian = true)
{
if (source.Count < 4)
{
return Enumerable.Empty<int>();
}
var b0 = (byte)(match & (isLittleEndian ? 0xff000000 : 0x000000ff));
var b1 = (byte)(match & (isLittleEndian ? 0x00ff0000 : 0x0000ff00));
var b2 = (byte)(match & (isLittleEndian ? 0x0000ff00 : 0x00ff0000));
var b3 = (byte)(match & (isLittleEndian ? 0x000000ff : 0xff000000));
var indexes = Enumerable.Range(0, source.Count / 4)
.AsParallel()
.Select(x => x * 4)
.Where(x => source[x] == b0)
.Where(x => source[x + 1] == b1)
.Where(x => source[x + 2] == b2)
.Where(x => source[x + 3] == b3);
return indexes;
}
}
示例用法:
var callingAssembly = Assembly.GetCallingAssembly();
var bytes = File.ReadAllBytes(callingAssembly.Location);
const int intToFind = 42;
var indexes = bytes.AllIndexesOf(intToFind);
foreach (var index in indexes)
{
Console.WriteLine(index);
}