我有一组ints,其中包含许多负值:
var arrayExisting = new int[]{1,2,-1,3,5,-1,0,0,-1};
另一个数组有一组相应的值我要插入到第一个数组中:
var replacements = new int[]{7,6,5};
有没有一种真正有效的方法呢?
目前我所拥有的是:
var newArray = arrayExisting.Select(val =>
{
if (val != -1) return val;
var ret = replacements[i];
i++;
return ret;
}).ToArray();
这很快。所讨论的数组长度只有大约15个整数,这可能会增加,但不可能超过100.问题是我必须为我的中等测试系统和现实系统做超过25万次我正在考虑将涉及此代码的10e10次迭代!
答案 0 :(得分:3)
我会使用for
循环并替换原始数组中的值。
int replacementIndex = 0;
for (var i = 0; i < arrayExisting.Length; i++) {
if (arrayExisting[i] < 0) {
arrayExisting[i] = replacements[replacementIndex++];
}
}
这样可以避免创建新数组的开销。如果您需要创建新阵列,可以创建new int[arrayExisting.Length]
运行快速基准测试似乎for循环速度提高了约4倍,即使在最糟糕的情况下,你必须每次更换并构建一个新数组来保存替换。
Select: 12672
For: 3386
如果您感兴趣,这是基准。
var loops = 1000000;
var arrayExisting = Enumerable.Repeat(-1, 1000).ToArray();
var replacements = Enumerable.Repeat(1, 1000).ToArray();
var selectTimer = Stopwatch.StartNew();
for (var j = 0; j < loops; j++)
{
var i = 0;
var newArray = arrayExisting.Select(val =>
{
if (val != -1) return val;
var ret = replacements[i];
i++;
return ret;
}).ToArray();
}
selectTimer.Stop();
var forTimer = Stopwatch.StartNew();
for (var j = 0; j < loops; j++)
{
var replaced = new int[arrayExisting.Length];
int replacementIndex = 0;
for (var i = 0; i < arrayExisting.Length; i++)
{
if (arrayExisting[i] < 0)
{
replaced[i] = replacements[replacementIndex++];
}
else
{
replaced[i] = arrayExisting[i];
}
}
}
forTimer.Stop();
Console.WriteLine("Select: " + selectTimer.ElapsedMilliseconds);
Console.WriteLine("For: " + forTimer.ElapsedMilliseconds);
答案 1 :(得分:0)
尝试使用指针:
int replacementsLength = arrayReplacements.Length;
fixed (int* existing = arrayExisting, replacements = arrayReplacements)
{
int* exist = existing;
int* replace = replacements;
int i = 0;
while (i < replacementsLength)
{
if (*exist == -1)
{
*exist = *replace;
i++;
replace++;
}
exist++; //edit: forgot to put exist++ outside the if block
}
}
编辑:此代码仅在您确定具有完全相同的替换数量和-1时才有效 要处理每个方案,请使用以下代码:
int replacementsLength = arrayReplacements.Length;
int existingLength = arrayExisting.Length;
fixed (int* existing = copy, replacements = arrayReplacements)
{
int* exist = existing;
int* replace = replacements;
int i = 0;
int x = 0;
while (i < replacementsLength && x < existingLength)
{
if (*exist == -1)
{
*exist = *replace;
i++;
replace++;
}
exist++;
x++;
}
}
运行与Joey相同的测试结果是:
选择:17378
对于:2172
指针:1780
编辑:我的错误,我忘了迭代我的代码1000000.仍然更快。
这是测试代码:
private unsafe static void test()
{
var loops = 1000000;
var arrayExisting = Enumerable.Repeat(-1, 1000).ToArray();
var arrayReplacements = Enumerable.Repeat(1, 1000).ToArray();
int[] newArray = null;
var selectTimer = Stopwatch.StartNew();
for (var j = 0; j < loops; j++)
{
var i = 0;
newArray = arrayExisting.Select(val =>
{
if (val != -1) return val;
var ret = arrayReplacements[i];
i++;
return ret;
}).ToArray();
}
selectTimer.Stop();
printResult("linQ", newArray);
arrayExisting = Enumerable.Repeat(-1, 1000).ToArray();
arrayReplacements = Enumerable.Repeat(1, 1000).ToArray();
int[] replaced = null;
var forTimer = Stopwatch.StartNew();
for (var j = 0; j < loops; j++)
{
replaced = new int[arrayExisting.Length];
int replacementIndex = 0;
for (var i = 0; i < arrayExisting.Length; i++)
{
if (arrayExisting[i] < 0)
{
replaced[i] = arrayReplacements[replacementIndex++];
}
else
{
replaced[i] = arrayExisting[i];
}
}
}
forTimer.Stop();
printResult("for", replaced);
arrayExisting = Enumerable.Repeat(-1, 1000).ToArray();
arrayReplacements = Enumerable.Repeat(1, 1000).ToArray();
int[] copy = null;
var pointerTimer = Stopwatch.StartNew();
//EDIT: fixed the test code
for (int j = 0; j < loops; j++)
{
copy = new int[arrayExisting.Length];
Array.Copy(arrayExisting, copy, arrayExisting.Length);
int replacementsLength = arrayReplacements.Length;
int existingLength = arrayExisting.Length;
fixed (int* existing = copy, replacements = arrayReplacements)
{
int* exist = existing;
int* replace = replacements;
int i = 0;
int x = 0;
while (i < replacementsLength && x < existingLength)
{
if (*exist == -1)
{
*exist = *replace;
i++;
replace++;
}
exist++;
x++;
}
}
}
pointerTimer.Stop();
printResult("pointer", copy);
File.AppendAllText(@"E:\dev\test.txt", "\r\n" +
"Select: " + selectTimer.ElapsedMilliseconds + "\r\n" +
"For: " + forTimer.ElapsedMilliseconds + "\r\n" +
"Pointer: " + pointerTimer.ElapsedMilliseconds);
}
答案 2 :(得分:0)
使用@TVOHM对原始问题的评论,我实现了以下代码
public static int[] ReplaceUsingLinq(IEnumerable<int> arrayFromExisting, IEnumerable<int> x)
{
var indices = x.ToArray();
var i = 0;
var newArray = arrayFromExisting.Select(val =>
{
if (val != -1) return val;
var ret = indices[i];
i++;
return ret;
}).ToArray();
return newArray;
}
public static int[] ReplceUsingForLoop(int[] arrayExisting, IEnumerable<int> x)
{
var arrayReplacements = x.ToArray();
var replaced = new int[arrayExisting.Length];
var replacementIndex = 0;
for (var i = 0; i < arrayExisting.Length; i++)
{
if (arrayExisting[i] < 0)
{
replaced[i] = arrayReplacements[replacementIndex++];
}
else
{
replaced[i] = arrayExisting[i];
}
}
return replaced;
}
public static unsafe int[] ReplaceUsingPointers(int[] arrayExisting, IEnumerable<int> reps)
{
var arrayReplacements = reps.ToArray();
int replacementsLength = arrayReplacements.Length;
var replaced = new int[arrayExisting.Length];
Array.Copy(arrayExisting, replaced, arrayExisting.Length);
int existingLength = replaced.Length;
fixed (int* existing = replaced, replacements = arrayReplacements)
{
int* exist = existing;
int* replace = replacements;
int i = 0;
int x = 0;
while (i < replacementsLength && x < existingLength)
{
if (*exist == -1)
{
*exist = *replace;
i++;
replace++;
}
exist++;
x++;
}
}
return replaced;
}
public static int[] ReplaceUsingLoopWithMissingArray(int[] arrayExisting, IEnumerable<int> x,
int[] missingIndices)
{
var arrayReplacements = x.ToArray();
var replaced = new int[arrayExisting.Length];
Array.Copy(arrayExisting, replaced, arrayExisting.Length);
var replacementIndex = 0;
foreach (var index in missingIndices)
{
replaced[index] = arrayReplacements[replacementIndex];
replacementIndex++;
}
return replaced;
}
使用以下代码对此进行基准测试:
public void BenchmarkArrayItemReplacements()
{
var rand = new Random();
var arrayExisting = Enumerable.Repeat(2, 1000).ToArray();
var arrayReplacements = Enumerable.Repeat(1, 100);
var toReplace = Enumerable.Range(0, 100).Select(x => rand.Next(100)).ToList();
toReplace.ForEach(x => arrayExisting[x] = -1);
var misisngIndices = toReplace.ToArray();
var sw = Stopwatch.StartNew();
var result = ArrayReplacement.ReplceUsingForLoop(arrayExisting, arrayReplacements);
Console.WriteLine($"for loop took {sw.ElapsedTicks}");
sw.Restart();
result = ArrayReplacement.ReplaceUsingLinq(arrayExisting, arrayReplacements);
Console.WriteLine($"linq took {sw.ElapsedTicks}");
sw.Restart();
result = ArrayReplacement.ReplaceUsingLoopWithMissingArray(arrayExisting, arrayReplacements, misisngIndices);
Console.WriteLine($"with missing took {sw.ElapsedTicks}");
sw.Restart();
result = ArrayReplacement.ReplaceUsingPointers(arrayExisting, arrayReplacements);
Console.WriteLine($"Pointers took {sw.ElapsedTicks}");
}
这给出了结果:
for loop took 848
linq took 2879
with missing took 584
Pointers took 722
因此,我们知道我们缺少值的位置(-1s所在的位置)是它快速的关键。
顺便说一句,如果我将每个调用循环到相关方法10000次并检查我得到的时间:
for loop took 190988
linq took 489052
with missing took 69198
Pointers took 159102
这里效果更大