Делал подобную задачу - перемешивание колоды карт.
Собственно, проблема данной задачи в том, что при прямой генерации для исключения повторения сгенерированного значения необходимо заного его генерировать. Такой алгоритм приводит к практически бесконечной генерации в конце списка.
Примененное решение заключалось в том, что производится не генерация самих значений (их список известен и конечен), а генерация их места в итоговом списке. Если позиция уже занята, то происходит просто поиск следующей свободной позиции (сдвиг).
Вот пример генерации:
Код:
procedure GetNumbers(var A : Array of Integer);
var
I, Idx : Integer;
B : Array [0..8] Of Integer;
begin
If (Length(A) > 9) Or (Length(A) < 1)
Then Raise Exception.Create('Input array should have length 1 to 9. ');
// Init temp arrray
For I := 0 to 8 Do B[i] := 0;
// Generate list
For I := 1 to 9 Do
Begin
Idx := Random(9);
While B[Idx] <> 0 Do
If Idx = 8 Then Idx := 0 Else Inc(Idx);
B[Idx] := I;
End;
// Copy result to output array
For I := Low(A) To High(A) Do A[i] := B[i];
end;
Пример использования. Пусть будут римские цифры:
Код:
// Return rome 1..9
function IntToRome(Value : Integer) : String;
const
Romes : Array [1..9] Of String =
('I','II','III','IV','V','VI','VII','VIII','IX');
begin
If Not (Value In [1..9])
Then Raise Exception.Create('Value should be in range 1..9.');
Result := Romes[Value];
end;
procedure Form1.Button1Click(Sender : TObject);
var
N, I : Integer;
A : Array Of Integer;
S : String;
begin
Randomize;
N := Random(9)+1; // Random number of digits 1..9
SetLength(A,N); // prepare the array
GetNumbers(A); // Get values
// Prepare and show result
S := '';
For I := Low(A) To High(A) Do
Begin
S := S + IntToRome(A[i]);
If I < High(A) Then S := S + ', ';
End;
ShowMessage(S);
end;