在我正在处理的库中,我们有数据集(可能是其他数据集的子集),这些数据集以三维矩形跨步数组的形式分布在内存中。也就是说,数组A
可以下标为A(i,j,k)
,其中每个索引的范围从零到某个上限,内存中每个元素的位置由下式给出:
A(i,j,k) = A0 + i * A_stride_i + j * A_stride_j + k * A_stride_k
其中A0
是基指针,A_stride_i
等是尺寸步幅。
现在,因为这些数据集可能是其他数据集的子集,而不是每个数据集占用它们自己的独立malloc内存块,所以它们完全可能重叠(重叠意味着A(i,j,k) < B(m,n,p)
并非总是如此如果它们重叠,它们可能相互交错,或者它们可能相互碰撞(其中碰撞意味着A(i,j,k) == B(m,n,p)
对于某些指数的六重奏)。
这就是问题所在。对两个数据集(例如,副本)的某些操作仅在数组不相互冲突时有效,但如果它们以交错的非冲突方式重叠则有效。我想为两个数据集添加一个函数,无论两个数据集是否发生冲突。
是否存在以合理有效和直接的方式执行此操作的现有算法?
检查数据集是否重叠相当容易,因此关键问题是:给定这种形式的两个数据集重叠,确定它们是否交错或碰撞的有效算法是什么?
示例:
举一个简单的例子,假设我们的内存位置从0到F(十六进制):
0 1 2 3 4 5 6 7 8 9 A B C D E F
为简单起见,我在这里也只考虑2D数组。假设我们有一个大小为2,3(即0 <= i < 2
和0 <= j < 3
),其中stride_i = 1
和stride_j = 4
,基地址为2.这将占用(占用的位置由其i,j对表示):
0 1 2 3 4 5 6 7 8 9 A B C D E F
* * * * * *
同样,如果我们有另一个相同大小和步幅的数组,从基地址4开始,将如下所示:
0 1 2 3 4 5 6 7 8 9 A B C D E F
o o o o o o
在我用于描述问题的术语中,这些数组“重叠”,但它们不会发生碰撞。
限制和假设:
我们可以假设步幅是积极的,并且如果需要,它们正在递增。实际的库中没有任何东西是真的,但重新排列数组定义以达到这一点是相当简单的。
我们可以假设数组不会自交错。图书馆也没有强制执行,但这是一个病态案例,可以单独警告。那就是(假设步幅递增,i
范围从零到max_i
等等:
stride_j >= max_i * stride_i
stride_k >= max_j * stride_j
当然,对于不需要这些假设的方法,重点是将数组定义重新排列为规范顺序,这是一些理想的避免工作。
不能假设这两个数组具有相同的大小或步幅。
我认为在施工过程中跟踪事物没有价值 - 在进行测试时,施工中没有信息。此外,“构造”可能只是“考虑这个更大的数组的子集与这个基本指针,这些步幅,以及这些大小。”
最糟糕的案例
答案 0 :(得分:1)
我认为以下C#程序应该可行。它使用branch and bound method,适用于任意维度的数组。
using System;
using System.Collections.Generic;
namespace SO_strides
{
sealed class Dimension
{
public int Min { get; private set; }
public int Max { get; private set; }
public int Stride { get; private set; }
private Dimension() { }
public Dimension(int max, int stride)
{
Min = 0;
Max = max;
Stride = stride;
}
public Dimension[] Halve()
{
if (Max == Min)
throw new InvalidOperationException();
int split = Min + (Max - Min) / 2;
return new Dimension[]
{
new Dimension { Min = Min, Max = split, Stride = Stride },
new Dimension { Min = split + 1, Max = Max, Stride = Stride }
};
}
}
sealed class ArrayPart
{
public int BaseAddr { get; private set; }
public Dimension[] Dimensions { get; private set; }
public int FirstNonconstantIndex { get; private set; }
int? min;
public int Min
{
get
{
if (min == null)
{
int result = BaseAddr;
foreach (Dimension dimension in Dimensions)
result += dimension.Min * dimension.Stride;
min = result;
}
return min.Value;
}
}
int? max;
public int Max
{
get
{
if (max == null)
{
int result = BaseAddr;
foreach (Dimension dimension in Dimensions)
result += dimension.Max * dimension.Stride;
max = result;
}
return max.Value;
}
}
public int Size
{
get
{
return Max - Min + 1;
}
}
public ArrayPart(int baseAddr, Dimension[] dimensions)
: this(baseAddr, dimensions, 0)
{
Array.Sort(dimensions, (d1, d2) => d2.Stride - d1.Stride);
}
private ArrayPart(int baseAddr, Dimension[] dimensions, int fni)
{
BaseAddr = baseAddr;
Dimensions = dimensions;
FirstNonconstantIndex = fni;
}
public bool CanHalve()
{
while (FirstNonconstantIndex < Dimensions.Length
&& Dimensions[FirstNonconstantIndex].Min == Dimensions[FirstNonconstantIndex].Max)
FirstNonconstantIndex++;
return FirstNonconstantIndex < Dimensions.Length;
}
public ArrayPart[] Halve()
{
Dimension[][] result = new Dimension[2][];
Dimension[] halves = Dimensions[FirstNonconstantIndex].Halve();
for (int i = 0; i < 2; i++)
{
result[i] = (Dimension[])Dimensions.Clone();
result[i][FirstNonconstantIndex] = halves[i];
}
return new ArrayPart[]
{
new ArrayPart(BaseAddr, result[0], FirstNonconstantIndex),
new ArrayPart(BaseAddr, result[1], FirstNonconstantIndex)
};
}
}
sealed class CandidateSet
{
public ArrayPart First { get; private set; }
public ArrayPart Second { get; private set; }
public CandidateSet(ArrayPart first, ArrayPart second)
{
First = first;
Second = second;
}
public bool Empty
{
get
{
return First.Min > Second.Max || Second.Min > First.Max;
}
}
public CandidateSet[] Halve()
{
int firstSize = First.Size;
int secondSize = Second.Size;
CandidateSet[] result;
if (firstSize > secondSize && First.CanHalve())
{
ArrayPart[] halves = First.Halve();
result = new CandidateSet[]
{
new CandidateSet(halves[0], Second),
new CandidateSet(halves[1], Second)
};
}
else if (Second.CanHalve())
{
ArrayPart[] halves = Second.Halve();
result = new CandidateSet[]
{
new CandidateSet(First, halves[0]),
new CandidateSet(First, halves[1])
};
}
else
throw new InvalidOperationException();
return result;
}
public static bool HasSolution(ArrayPart first, ArrayPart second)
{
Stack<CandidateSet> stack = new Stack<CandidateSet>();
stack.Push(new CandidateSet(first, second));
bool found = false;
while (!found && stack.Count > 0)
{
CandidateSet candidate = stack.Pop();
if (candidate.First.Size == 1 && candidate.Second.Size == 1)
found = true;
else
{
foreach (CandidateSet half in candidate.Halve())
if (!half.Empty)
stack.Push(half);
}
}
return found;
}
}
static class Program
{
static void Main()
{
Console.WriteLine(
CandidateSet.HasSolution(
new ArrayPart(2, new Dimension[] { new Dimension(1, 1), new Dimension(2, 4) }),
new ArrayPart(4, new Dimension[] { new Dimension(1, 1), new Dimension(2, 4) })
)
);
}
}
}