0

In pascal, i want to randomly organize an array. Therefore the majority of the time the array should be organized differently.

Consider this array setup

const
  ARRAY_ELEMENTS = 3;

SetLength(iIndex, ARRAY_ELEMENTS);

for i := Low(iIndex) to High(iIndex) do
begin
   case i of
   0: iIndex[i] := 0;
   1: iIndex[i] := 1;
   2: iIndex[i] := 2;
   end;
end; 

How is it possible for the iIndex[] that contains the value 0 not to always be in the first element of the array and for the iIndex[] that contains value 2 not to be always the last value of the array but generate the order of the array randomly so that the order of the array is not always the same on initialization?

user2142260
  • 31
  • 1
  • 3
  • you could randomly swap/shuffle elements in the array. – kobik Mar 10 '13 at 16:11
  • 2
    So your question is [`how to shuffle array values`](http://delphi.about.com/cs/adptips2003/a/bltip1003_4.htm) ? – TLama Mar 10 '13 at 16:12
  • 1
    Your code makes no sense... – Andreas Rejbrand Mar 10 '13 at 16:13
  • You want the values to be associated with indices randomly while keeping the overall set of values same as the set of indices? That does seem like the problem @TLama has linked to. – Andriy M Mar 10 '13 at 16:18
  • "So your question is how to shuffle array values ?", Doesn't shuffle mean randomize? – user2142260 Mar 10 '13 at 17:36
  • 3
    @TLama, with all my respect to Zarko, that `Shuffle` method burned my eyes :) – kobik Mar 10 '13 at 17:37
  • @kobik, it's worse readable because it was meant to be universal for any kind of element. Sure it's not so good for an array of specific elements. – TLama Mar 10 '13 at 18:21
  • @david would have liked a fisher yates algorithm that worked on Arrays instead of lists, now I'll have to think... (I'm randomizing dictionary keys) – Peter Turner Apr 15 '14 at 18:50

2 Answers2

4

This code permutes an integer array, but I'm not sure if it is optimal (probably isn't).

type
  TDynIntegerArray = array of integer;

procedure PermuteArray(A: TDynIntegerArray);
var
  B: TDynIntegerArray;
  Z: TDynIntegerArray;
  π: TDynIntegerArray;
  i: Integer;
  j: Integer;
  k: Integer;
begin
  B := Copy(A);
  SetLength(Z, Length(A));
  SetLength(π, Length(A));
  for i := 0 to High(Z) do
    Z[i] := i;
  for i := 0 to High(π) do
  begin
    π[i] := RandomFrom(Z);
    for j := 0 to High(Z) do
    begin
      if Z[j] = π[i] then
      begin
        for k := j to High(Z) - 1 do
          Z[k] := Z[k+1];
        SetLength(Z, length(Z) - 1);
        break;
      end;
    end;
  end;
  for i := 0 to High(A) do
    A[i] := B[π[i]];
end;

A much faster, but less cool approach is simply to randomly swap the items, one pair at a time:

procedure FastPermuteArray(A: TDynIntegerArray);
  procedure Swap(n, m: integer);
  var
    tmp: integer;
  begin
    tmp := A[n];
    A[n] := A[m];
    A[m] := tmp;
  end;
var
  i: Integer;
begin
  for i := High(A) downto 1 do
    Swap(i, RandomRange(0, i));
end;
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • "π" ... knowing you, this meant to be a joke I don't get. – kobik Mar 10 '13 at 16:31
  • @kobik: Well, it does compile in Delphi 2009+. But of course, you can rename the variable to Pi or something. (In mathematics, a permutation is often denoted by *π*.) [The really bad joke, I would imagine, is the poor performance of this algorithm. I've never really studied practical algorithms, so there might be a far better 'school-book' algorithm for this problem.] – Andreas Rejbrand Mar 10 '13 at 16:32
  • 1
    +1, Your second method is actually "cool" and simple enough to do the job right. Just add `Randomize` at the beginning. – kobik Mar 10 '13 at 17:34
  • Do not add randomize in the procedure, as it usually should only be called once . – BeniBela Mar 10 '13 at 17:53
  • 1
    The shuffle is not uniform. Have a search for Fisher-Yates shuffle. I and others have answered the Q with Fisher-Yates shuffle here on SO before. – David Heffernan Mar 10 '13 at 18:40
  • @DavidHeffernan: But I guess the first, very naïve one is? – Andreas Rejbrand Mar 10 '13 at 18:42
  • Don't know. Hard to see past Fisher-yates in my view. – David Heffernan Mar 10 '13 at 18:49
  • @David: Yes, probably. As I said above, I've never really studied practical algorithms. But given the extreme naïveness of the first approach, I think it is almost perfect, except for the terrible performance. – Andreas Rejbrand Mar 10 '13 at 18:50
  • Read about FY, it's sweet. This is a dupe also, but I can't vote on it from my phone. I put the link in a comment to Q. – David Heffernan Mar 10 '13 at 18:54
  • @David: Voted to close. Seeing your implemenatation of FY, I have to agree it's stupid to do anything else if it is that easy to do it right! – Andreas Rejbrand Mar 10 '13 at 18:55
  • @David: Now it's right, I guess. – Andreas Rejbrand Mar 10 '13 at 19:54
0

Try something like this:

uses
  System.Generics.Collections;

const
  ARRAY_ELEMENTS = 3;
var
  iArray: array of Integer;
  iIndex: TList<Integer>;
  I, j: Integer;
begin
  Randomize;

  SetLength(iArray, ARRAY_ELEMENTS);

  iIndex := TList<Integer>.Create;
  try
    iIndex.Count := ARRAY_ELEMENTS;
    for i := 0 to Pred(ARRAY_ELEMENTS) do
      iIndex[i] := i;

    for i := Low(iArray) to High(iArray) do
    begin
      j := Random(iIndex.Count);
      iArray[iIndex[j]] := i;
      iIndex.Delete(j);
    end;
  finally
    iIndex.Free;
  end;
end;

If you don't have TList<T> available in your version of Delphi, you can use a plain TList instead:

uses
  Classes;

const
  ARRAY_ELEMENTS = 3;
var
  iArray: array of Integer;
  iIndex: TList;
  I, j: Integer;
begin
  Randomize;

  SetLength(iArray, ARRAY_ELEMENTS);

  iIndex := TList.Create;
  try
    iIndex.Count := ARRAY_ELEMENTS;
    for i := 0 to Pred(ARRAY_ELEMENTS) do
      iIndex[i] := Pointer(I);

    for i := Low(iArray) to High(iArray) do
    begin
      j := Random(iIndex.Count);
      iArray[Integer(iIndex[j])] := i;
      iIndex.Delete(j);
    end;
  finally
    iIndex.Free;
  end;
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    This is basically the same algorithm as my first one. So I guess it works but is far slower than Fisher-Yates. – Andreas Rejbrand Mar 10 '13 at 19:49
  • Heap allocation?!! Why? – David Heffernan Mar 10 '13 at 21:10
  • In your favour, this algo does produce a shuffle with uniform distribution. I don't understand why you did not say that. But there's really no reason for re-invent the wheel here. Shuffling is just so fundamental. Surely your first port of call is a web search to find out the body of knowledge that already exists on the topic? – David Heffernan Mar 10 '13 at 21:23