将数组中的给定值替换为 C# 中第二个数组中的值
本文关键字:数组 第二个 替换 | 更新日期: 2023-09-27 18:30:17
我有一个整数数组,其中有许多负值:
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。问题是,对于我的中等测试系统,我必须这样做超过四分之一次,而我正在考虑的实际系统将涉及此代码的大约 10e10 次迭代!
我会使用 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);
尝试使用指针:
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++;
}
}
运行与乔伊相同的测试,结果为:
选择: 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);
}
使用 @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
因此,知道我们在哪里有缺失值(-1 在哪里)是它快速的关键。
顺便说一下,如果我循环对相关方法的每个调用 10000 次并检查我得到的时间:
for loop took 190988
linq took 489052
with missing took 69198
Pointers took 159102
这里的效果更大