循环多个维度

时间:2017-05-21 12:12:17

标签: algorithm loops delphi iteration

我有几个变量的函数f(例如3个变量f(x,y,z))。我想计算每个变量范围的函数结果并将它们存储在列表中。对于3个变量,这可能如下所示:

procedure LoopOver3Dimensions;
type
  TListItem=record
    x, y, z: Real;
    CalculationResult: Real;
  end;
var
  List: TList<TListItem>;
  NewListItem: TListItem;
  i, j, k: Integer;
  x, y, z: Real;
  xStart, yStart, zStart: Real;
  xEnd, yEnd, zEnd: Real;
  NumberOfValuesToCalculateForDimension0: Integer;
  NumberOfValuesToCalculateForDimension1: Integer;
  NumberOfValuesToCalculateForDimension2: Integer;
begin
  //set xStart, xEnd, NumberOfValuesToCalculateForDimension0 etc here

  for i := 0 to NumberOfValuesToCalculateForDimension0 do
  begin
    x:=xStart+i*Abs(xEnd-xStart)/(NumberOfValuesToCalculateForDimension0-1);
    for j := 0 to NumberOfValuesToCalculateForDimension1 do
    begin
      y:=yStart+j*Abs(yEnd-yStart)/(NumberOfValuesToCalculateForDimension1-1);
      for k := 0 to NumberOfValuesToCalculateForDimension2 do
      begin
        z:=zStart+k*Abs(zEnd-zStart)/(NumberOfValuesToCalculateForDimension2-1);
        NewListItem.x:=x;
        NewListItem.y:=y;
        NewListItem.z:=z;
        NewListItem.CalculationResult:=DoCalculation(x, y, z);
        List.Add(NewListItem);
      end;
    end;
  end;
end;

我当然可以以相同的方式为超过3个维度(例如20个维度)编程,但它会变得非常麻烦,因为所有内容都是硬编码的,所以我无法在运行时更改维度数。

这样做的最佳方式是什么?

2 个答案:

答案 0 :(得分:2)

正如评论中所讨论的,为了支持任意数量的参数,最好使用可变长度的数组而不是参数。这是因为该语言对可变长度参数列表没有很大的支持。

将所有参数打包为数组后,您将面临生成所有可能的组合。这不是一个完全简单的任务。我将在i{$APPTYPE CONSOLE} uses System.SysUtils; procedure IterMultiDim(const N: array of Integer; const Proc: TProc<TArray<Integer>>); var dim: Integer; Current: TArray<Integer>; begin SetLength(Current, Length(N)); while Current[high(N)]<N[high(N)] do begin Proc(Current); // increment state dim := 0; while dim<=high(N) do begin inc(Current[dim]); if Current[dim]=N[dim] then begin if dim<high(N) then begin Current[dim] := 0; end; end else begin break; end; inc(dim); end; end; end; procedure WriteIntArray(Arr: TArray<Integer>); var i: Integer; begin for i := 0 to high(Arr) do begin Write(Arr[i]); if i<high(Arr) then begin Write(', '); end; end; Writeln; end; procedure Main; begin IterMultiDim([2, 3, 4], WriteIntArray); end; begin try Main; except on E: Exception do begin Writeln(E.ClassName, ': ', E.Message); end; end; Readln; end. 范围内,而不是使用实际值,我将向您展示如何在每个维度为整数值的情况下执行此操作,其中money1是维度索引。一旦你可以迭代所有这些组合,你就可以轻松扩展到生成真正的价值。

基本概念是维持当前迭代值,该值递增。第一个维度是循环的最内层。当它达到其最大值时,它返回到零并且下一个外部维度递增。等等。以下是一些示例代码:

Jlabel

<强>输出:

0, 0, 0
1, 0, 0
0, 1, 0
1, 1, 0
0, 2, 0
1, 2, 0
0, 0, 1
1, 0, 1
0, 1, 1
1, 1, 1
0, 2, 1
1, 2, 1
0, 0, 2
1, 0, 2
0, 1, 2
1, 1, 2
0, 2, 2
1, 2, 2
0, 0, 3
1, 0, 3
0, 1, 3
1, 1, 3
0, 2, 3
1, 2, 3

这种技术可以用各种方式包裹起来。例如,它可以封装在for / in枚举器中,这将使更高的可读性成为可能。

答案 1 :(得分:0)

受David的方法启发,我提出了另一种解决方案:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

procedure IterMultiDim(const NumberOfCalculationsPerDimension: TArray<Integer>; const Proc: TProc<TArray<Integer>>);

  function ModInc(const MaxNumber: Integer; var AValue: Integer): Boolean;
  begin
    Inc(AValue);
    if AValue>MaxNumber then
    begin
      AValue:=0;
      Result:=true; //carry
    end
    else
    begin
      Result:=false; //no carry
    end;
  end;

var
  NumberOfDimensions: Integer;
  CurrentIndices: TArray<Integer>;
  i, j: Integer;
  TotalNumberOfCalculations: Integer;
  Carry: Boolean;
begin
  NumberOfDimensions:=Length(NumberOfCalculationsPerDimension);
  SetLength(CurrentIndices, NumberOfDimensions);
  TotalNumberOfCalculations:=1;
  for i := 0 to NumberOfDimensions-1 do
  begin
    TotalNumberOfCalculations:=TotalNumberOfCalculations*NumberOfCalculationsPerDimension[i];
  end;

  for i := 0 to TotalNumberOfCalculations-1 do
  begin
    Proc(CurrentIndices);
    Carry:=true;
    for j := 0 to NumberOfDimensions-1 do
    begin
      if Carry then
      begin
        Carry:=ModInc(NumberOfCalculationsPerDimension[j]-1, CurrentIndices[j]);
      end;
    end;
  end;
end;

procedure WriteIntArray(Arr: TArray<Integer>);
var
  i: Integer;
  S: string;
begin
  S:='';
  for i := 0 to High(Arr) do
  begin
    S:=S+Arr[i].ToString+'  ';
  end;
  Writeln(S);
end;

procedure Main;
begin
  IterMultiDim([3, 4, 2], WriteIntArray);
end;

begin
  try
    Main;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.