【问题标题】:Function to average elements of any array in DelphiDelphi中任何数组元素的平均函数
【发布时间】:2016-09-16 19:25:58
【问题描述】:

我们如何在 Delphi 6 中实现一个函数来平均一个动态多维数组的所有元素?如:

Function Average(arr:any_array):extended;

arr 是一个动态数组,可能有 1 个或多个维度。

相关问题:我们如何线性化多维数组?你有什么例子吗?

我问这个是因为我有许多不同的数组,如果可能的话,我必须用一个相同的函数对它们进行平均。

【问题讨论】:

  • 具有不同维度的数组需要一些魔法或包装类型。线性化是什么意思?
  • 您也不想使用 Extended,因为它往往对齐不佳并且性能很差。

标签: arrays delphi delphi-6


【解决方案1】:

如果您有一维数组,您可以简单地使用位于Math 单元中的Mean 函数。

但对于多维数组恐怕还没有可用的功能。主要原因是Delphi对多维数组的处理方式比较特殊。

而在许多其他编程语言中,多维数组实际上只是一维数组,具有用于访问数组元素的修改机制,其工作原理如下:

Actual Array Item Index = First parameter + (Second parameter * Size of first dimension)

在 Delphi 中,多维数组表示为由另一个数组引用的多个一维数组,如下所示:

Array of One Dimensional Array

编辑 2:正如 David 在他的评论中提到的,这些类型的数组也称为 Jagged arraysRagged arrays

为什么会如此不同?为什么如此重要?因为那些来自 Delphi 中多维数组的一维数组不需要具有相同的大小。所以你的多维数组中的数据实际上可以是这样的(每个 X 代表一个数组项):

XXXX
XXXXXXX
XXXX
XXXXX

所以恐怕你必须为此编写自己的函数。但这不应该那么难。

您所需要的只是一种遍历数组中每个项目的机制(我敢打赌,您已经在代码中的某个地方实现了这一点),这样您就可以总结数组中所有元素的值。然后将该总和除以数组中元素的数量,以获得所有项目的算术平均值。

编辑 3:我不确定您是否可以使用单个代码来遍历所有动态数组。我的意思是至少你需要知道你的数组有多少维。这是必需的,因为要遍历数组的多个维度,您实际上需要递归遍历表示单独维度的每个单独数组。

对 3D 动态数组中的元素求和的代码如下所示:

type
  TDyn3Darray = array of array of array of Single;

implementation

function SumOf3DArray(Dyn3DArray: TDyn3DArray): Single;
var X,Y,Z, a: Integer;
    ArrItem: Single;
begin
  Result := 0;
  SetLength(Dyn3Darray,5,8,40);
  for X := Low(Dyn3Darray) to High(Dyn3Darray) do
  begin
    for Y := Low(Dyn3Darray[X]) to High(Dyn3Darray[X]) do
    begin
      for Z := Low(Dyn3Darray[X,Y]) to High(Dyn3Darray[X,Y]) do
      begin
        Result := Result + Dyn3Darray[X,Y,Z];
      end;
    end;
  end;
end;

我相信您将能够自己针对不同数量的维度修改代码。


编辑1:至于linearizing 的多维数组,我相信您的意思是更改一维数组中的多维数组,您只需复制不同维度的元素并将它们添加到一维数组中。

但是仅仅为了获得所有元素的平均值而这样做是不切实际的,因为首先你需要双倍的内存量,其次为了将所有元素从多个维度复制到一个维度中,你可能需要首先遍历每个元素。然后,无论您使用什么方法来计算平均值,都必须再次遍历所有这些复制的元素,从而无缘无故地减慢处理速度。

如果您有兴趣用一维数组替换多维数组,同时仍然威胁其元素,就好像它们在多维数组中一样,请查看我对其他一些编程语言如何处理多维数组的解释。

请注意,这将要求您通过特殊方法访问此类数组的所有项,因此会导致您与这些数组交互的代码发生较大更改。除非您将这些数组包装在具有默认多索引属性的类中,该属性将允许您访问数组项。这种方法需要额外的代码来初始化和完成此类类,并且在调整此类数组的大小时需要稍微修改方法。

【讨论】:

  • FWIW Delphi 没有真正的多维动态数组,而是使用 jagged 数组。
  • @DavidHeffernan 你是对的。我必须承认,直到今天我还没有看到他们被这样提及。
  • 您说:“您所需要的只是一种遍历数组中每个项目的机制(我敢打赌,您已经在代码中的某个地方实现了这一点),因此您可以总结所有元素的值你的数组。”。对。既然有动态数组,怎么办?如何确定传递给我的函数的(动态)数组的 dims(dim/size),以便我可以迭代它们?请记住:我有许多不同的数组,并且想对所有数组使用相同的函数。
  • @danmatei 有很多例子说明如何迭代动态数组,使用 'low' 和 'high',如 low(myArray) 到 high(myArray) ..
  • @JohnEasley 使用 lowhigh 关键字对于动态一维数组非常简单。但是为了遍历多维数组,您需要对每个维度递归地使用lowhigh,这意味着为了迭代内部维度,您实际上需要传递特定外部维度的引用,例如for Y := Low(Dyn2DArray[X]) to High(Dyn2DArray[X]) do。这可能看起来并不那么简单。
【解决方案2】:

Delphi 没有提供简单的方法让您检查任意动态数组的维度。实际上,它没有为您提供传递任意动态数组的方法。但是,在实践中,您通常会遇到一维和二维数组,很少会遇到更高维的数组。所以你只需要编写几个函数:

type
  TDoubleArray1 = array of Double;
  TDoubleArray2 = array of TDoubleArray1;
  TDoubleArray3 = array of TDoubleArray2;

function Mean(const arr: TDoubleArray1): Double; overload;
function Mean(const arr: TDoubleArray2): Double; overload;
function Mean(const arr: TDoubleArray3): Double; overload;

这样实现:

function Mean(const arr: TDoubleArray1): Double;
var
  i: Integer;
begin
  Result := 0.0;
  for i := low(arr) to high(arr) do
    Result := Result + arr[i];
  Result := Result / Length(arr);
end;

function Mean(const arr: TDoubleArray2): Double;
var
  i, j, N: Integer;
begin
  Result := 0.0;
  N := 0;
  for i := low(arr) to high(arr) do
    for j := low(arr[i]) to high(arr[i]) do
    begin
      Result := Result + arr[i,j];
      inc(N);
    end;
  Result := Result / N;
end;

function Mean(const arr: TDoubleArray3): Double;
var
  i, j, k, N: Integer;
begin
  Result := 0.0;
  N := 0;
  for i := low(arr) to high(arr) do
    for j := low(arr[i]) to high(arr[i]) do
      for k := low(arr[i,j]) to high(arr[i,j]) do
      begin
        Result := Result + arr[i,j,k];
        inc(N);
      end;
  Result := Result / N;
end;

如果你真的需要比这更高的维度,我会感到震惊,但它很容易添加。

几点:

  • 出于性能原因,我使用Double 而不是Extended,因为后者的大小为10,这会导致大量未对齐的内存访问。如果你不能让自己避开Extended,你可以很容易地修改上面的代码。
  • 您可能会找到使用 RTTI 编写一个迭代任何动态数组的单个函数的低级方法,但是,RTTI 可能会影响性能。我个人认为走这条路没有任何意义。使用一些这样的功能即可完成。

【讨论】:

  • 我已经为 1...5 暗淡定义了 5 个重载函数,但我认为这是一种更优雅的方式。不管怎么说,还是要谢谢你。 PS这真的是使用双打或扩展之间的相关区别吗?
  • 我认为没有优雅的需要。是的,性能扩展通常很差
猜你喜欢
  • 2022-01-11
  • 2014-04-17
  • 2013-12-29
  • 2019-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多