delphi 如何创建x组的y数字,组是不重叠彼此从一组范围内的数字,即,1-80

gt0wga4j  于 2023-05-06  发布在  其他
关注(0)|答案(1)|浏览(152)

我正试图找出一个好方法,就如何创建一些组,其中包含这么多的数字从一组范围内的数字。
更好的解释是:
x = number of groups to be created. y = the total amount of numbers to each group created. z = the range of numbers from 1-80 to pick from - Matrix of 8 rows, 10 Columns.组本身应采用块样式,即19,29,20,30(2x2)块,并且没有组也与任何其他组重叠。
对于这个问题的任何例子,请使用现在的x=2和y=4和z=80(0是无效的)
我该如何编写代码,还是使用数学公式来完成?
我正在尝试在我为自己创建的 Delphi 应用程序中做到这一点。
我真的很感激任何关于这个问题的见解。谢谢大家。
逻辑编程,但变得太混乱,必须有一个有效的公式来解决这类问题。

w6lpcovy

w6lpcovy1#

// x = seGroups.Value (Number of Groups)
// y = seNumsPerGroup.Value (Numbers per Group)
// z = MAX_NUMBERS

// Other components.
// sgNumbers = TStringGrid that is by 8 rows and 10 columns
// chkDoingCombinations = Checkbox that indicates to do combination building.
// chkGenGrouping = Checkbox that indicates we are to group numbers during random generation.

// Please define these types.
TMyCoords = record
  Col: Integer;
  Row: Integer;
end;

TMatrix = TMyCoords;

TMyObject = class(TObject)
  private
    bPaint: Boolean;
  public
    property IsPainted: Boolean read bPaint write bPaint default False;
  end;

// Our Constants
const
  MAX_ROWS: Integer = 8;
  MAX_COLS: Integer = 10;
  MAX_ATTEMPTS = 50;
  MAX_NUMBERS = 80;

var
  frmMain: TfrmMain;
  CellCoords: TMyCoords;
  iMAX_NUMBERS: Integer;

// Function that determines how a group will be built
// on our given grid matrix.
function TfrmMain.DetermineGroupMatrixType (): TMatrix;
var
  iResult: Integer;
  Matrix: TMatrix;
begin
  Matrix.Col := 0;
  Matrix.Row := 0;
  case seNumPerGroup.Value of
    // Groups of 2
    2: begin
         iResult := Random (2);
         // Are we doing Vertical?
         if iResult = 1 then
         begin
           Matrix.Col := 1;
           Matrix.Row := seNumPerGroup.Value;
           Result := Matrix;
           exit;
         end
         else
         begin
           // Otherwise, Go Horizontally.
           Matrix.Col := seNumPerGroup.Value;
           Matrix.Row := 1;
           Result := Matrix;
           exit;
         end;
       end; // End of Case 2

    // Groups of 3
    3: begin
         iResult := Random (2);
         // Are we doing Vertical?
         if iResult = 1 then
         begin
           Matrix.Col := 1;
           Matrix.Row := seNumPerGroup.Value;
           Result := Matrix;
           exit;
         end
         else
         begin
           // Otherwise, Go Horizontally.
           Matrix.Col := seNumPerGroup.Value;
           Matrix.Row := 1;
           Result := Matrix;
           exit;
         end;
       end;  // End of Case 3

    // Groups of 4
    4: begin
         iResult := Random (3);
         // Are we to create a block?
         if iResult = 2 then
         begin
           Matrix.Col := 2;
           Matrix.Row := 2;
           Result := Matrix;
           exit;
         end;

         // Are we going Vertically.
         if iResult = 1 then
         begin
           Matrix.Col := 1;
           Matrix.Row := seNumPerGroup.Value;
           Result := Matrix;
           exit;
         end
         else
         begin
           // Otherwise, Go Horizontally.
           Matrix.Col := seNumPerGroup.Value;
           Matrix.Row := 1;
           Result := Matrix;
           exit;
         end;
       end;  // End of Case 4

    // Groups of 5
    5: begin
         iResult := Random (2);
         // Are we doing Vertical?
         if iResult = 1 then
         begin
           Matrix.Col := 1;
           Matrix.Row := seNumPerGroup.Value;
           Result := Matrix;
           exit;
         end
         else
         begin
           // Otherwise, Go Horizontally.
           Matrix.Col := seNumPerGroup.Value;
           Matrix.Row := 1;
           Result := Matrix;
           exit;
         end;
       end; // End of Case 5

    // Groups of 6
    6: begin
         iResult := Random (3);
         // Are we to create a block?
         if iResult = 2 then
         begin
           // Yes, now which type of block.
           iResult := Random (2);
           if iResult = 1 then
           begin
             Matrix.Col := 3;
             Matrix.Row := 2;
           end
           else
           begin
             Matrix.Col := 2;
             Matrix.Row := 3;
           end;

           Result := Matrix;
           exit;
         end;

         // Are we going Vertically.
         if iResult = 1 then
         begin
           Matrix.Col := 1;
           Matrix.Row := seNumPerGroup.Value;
           Result := Matrix;
           exit;
         end
         else
         begin
           // Otherwise, Go Horizontally.
           Matrix.Col := seNumPerGroup.Value;
           Matrix.Row := 1;
           Result := Matrix;
           exit;
         end;
       end;  // End of Case 6

    // Groups of 7
    7: begin
         iResult := Random (2);
         // Are we doing Vertical?
         if iResult = 1 then
         begin
           Matrix.Col := 1;
           Matrix.Row := seNumPerGroup.Value;
           Result := Matrix;
           exit;
         end
         else
         begin
           // Otherwise, Go Horizontally.
           Matrix.Col := seNumPerGroup.Value;
           Matrix.Row := 1;
           Result := Matrix;
           exit;
         end;
       end;  // End of Case 7

    // Groups of 8
    8: begin
         iResult := Random (3);
         // Are we to create a block?
         if iResult = 2 then
         begin
           // Yes, now which type of block.
           iResult := Random (2);
           if iResult = 1 then
           begin
             Matrix.Col := 4;
             Matrix.Row := 2;
           end
           else
           begin
             Matrix.Col := 2;
             Matrix.Row := 4;
           end;

           Result := Matrix;
           exit;
         end;

         // Are we going Vertically.
         if iResult = 1 then
         begin
           Matrix.Col := 1;
           Matrix.Row := seNumPerGroup.Value;
           Result := Matrix;
           exit;
         end
         else
         begin
           // Otherwise, Go Horizontally.
           Matrix.Col := seNumPerGroup.Value;
           Matrix.Row := 1;
           Result := Matrix;
           exit;
         end;
       end;  // End of Case 8

    // Groups of 9
    9: begin
         // Yes, now which type of block.
         Matrix.Col := 3;
         Matrix.Row := 3;
         Result := Matrix;
         exit;
       end; // End of Case 9

    // Groups of 10
    10: begin
          iResult := Random (3);
          // Are we to create a block?
          if iResult = 2 then
          begin
            // Yes, now which type of block.
            iResult := Random (2);
            if iResult = 1 then
            begin
              Matrix.Col := 5;
              Matrix.Row := 2;
            end
            else
            begin
              Matrix.Col := 2;
              Matrix.Row := 5;
            end;

            Result := Matrix;
            exit;
          end;

          if iResult = 1 then
          begin
            // Yes, now which type of block.
            Matrix.Col := 10;
            Matrix.Row := 1;
            Result := Matrix;
            exit;
          end;

          // Otherwise, Go Horizontally.
          Matrix.Col := seNumPerGroup.Value;
          Matrix.Row := 1;
          Result := Matrix;
          exit;
        end; // End of Case 10
  end; // End of Case Block.
end;

// Function that calculates on our grid, exact row and col coordinates
function TfrmMain.CalculateGridCoords (CellNumber: Integer): TMyCoords;
begin
  Result.Row := (CellNumber div MAX_COLS);
  if (CellNumber > (MAX_COLS - 1)) and ((CellNumber mod MAX_COLS) = 0) then
     Dec (Result.Row);

  if (CellNumber mod MAX_COLS) = 0 then
     Result.Col := MAX_COLS - 1
  else
     Result.Col := (CellNumber mod MAX_COLS) - 1;
end;

// This function builds the sized group and tests for conflicts
// with existing numbers that have been selected on the grid
function TFrmMain.ValidateGrouping (iStartNumber: Integer; Matrix: TMatrix;
                                    var iSetNumbers: array of Integer): Boolean;
var
  iCol, iRow, iOrigBaseNum: Integer;
  myObj: TMyObject;
  myCoords: TMyCoords;
  iX, iY: Integer;

begin
  // Default Return result.
  Result := False;

  // Iterate through our Column and test if number is used.
  iOrigBaseNum := iStartNumber;
  for iCol := 0 to Matrix.Col - 1 do
  begin
    // First check to see if this is out of range or the number
    // has already been used.
    iX := iStartNumber + iCol;
    if (iX > MAX_NUMBERS) or (iX < 1) or (iSetNumbers[iX] > 0) then
       exit;

    // Iterate throu block row and check every number to
    // ensure none are in use.
    for iRow := 0 to Matrix.Row - 1 do
    begin
      iX := iStartNumber + (iRow * MAX_COLS);
      // Check this number to see if is out of range or the number
      // has already been used.
      if ((iX > MAX_NUMBERS) or (iX < 1)) or (iSetNumbers [iX] > 0) then
         exit;
    end; // End of For iRow
  end; //End of For iCol

  // If we have made it this far, then we are looking good to
  // create our block of numbers.
  iStartNumber := iOrigBaseNum;
  for iCol := 0 to Matrix.Col - 1 do
  begin
    // Calculate next position within block area
    iX := iStartNumber + iCol;

    // Set and Highlight on Grid.
    myCoords := CalculateGridCoords (iX);
    myObj := TMyObject (sgNumbers.Objects [myCoords.Col, myCoords.Row]);
    // Mark this cell for Highlighting
    myObj.IsPainted := True;

    // Iterate throu block row and check every number to
    // ensure none are in use.
    iY := iX;
    for iRow := 0 to Matrix.Row - 1 do
    begin
      // Calculate next position within block area
      iX := iY + (iRow * MAX_COLS);

      // Set and Highlight on Grid.
      iSetNumbers [iX] := iX;
      myCoords := CalculateGridCoords (iX);
      myObj := TMyObject (sgNumbers.Objects [myCoords.Col, myCoords.Row]);
      // Mark this cell for Highlighting
      myObj.IsPainted := True;
    end; // End of For iRow
  end; //End of For iCol

  Result := True;
end;

// This is the calling procedure to start with group
// or individual number generation
procedure TfrmMain.GenerateRandomNumbers(Sender: TObject);
var
  iNum: Integer;
  iSetNumbers: Array [1 .. MAX_NUMBERS] of Integer;
  iTotalNumbers: Integer;
  iX: Integer;
  myObj: TMyObject;
  myCoords: TMyCoords;

  // Procedure that starts the Random Number Generation process.
  procedure GenerateNumbers (iTotalNumbers: Integer);
  var
    bFound: Boolean;
    iX: Integer;
    myObj: TMyObject;
    iCount: Integer;

    // Function that generates our grouping of numbers.
    function GenerateGroup (var iBaseNum: Integer): Boolean;
    var
      iIndex: Integer;
      Matrix: TMatrix;
      iCount: Integer;

    begin
      // Build all Groups
      for iIndex := 1 to seGroups.Value do
      begin
        Matrix := DetermineGroupMatrixType();
        bFound := True;
        iCount := 1;

        while (bFound) and (iCount < MAX_ATTEMPTS) do
        begin
          if (iBaseNum = 0) or (iSetNumbers [iBaseNum] > 0) then
          begin
            // Invalid, Randomise a number and Initialise
            iBaseNum := Random (iMAX_NUMBERS);
            // Increase the number of attempts.
            Inc (iCount);
            continue;
          end;

          // Next we need to set and build our group of numbers and
          // ensure that each group does not conflict with other
          // numbers.
          bFound := ValidateGrouping (iBaseNum, Matrix, iSetNumbers);
          bFound := bFound xor True;

          // if failed, pick another number.
          if bFound then
          begin
            iBaseNum := Random (iMAX_NUMBERS);
            // Increase the number of attempts.
            Inc (iCount);
          end;
        end; // End of While Loop
      end;

      Result := True;
    end; // End of Function

  begin
    // Are we doing combinations?
    if chkDoingCombinations.Checked then
    begin
      // Generate our number list
      bFound := True;
      iCount := 0;
      while (bFound) and (iCount < MAX_ATTEMPTS) do
      begin
        // Randomise a number and Initialise
        iNum := Random (iMAX_NUMBERS);
        if (iNum = 0) or (iSetNumbers [iNum] > 0) then
        begin
          // Increase our number of attempts
          Inc (iCount);
          continue;
        end;

        // Are we grouping numbers?
        if chkGenGrouping.Checked then
        begin
          // If we group generation failed
          if not GenerateGroup (iNum) then
          begin
            iSetNumbers [iNum] := 0;
            // Increase our number of attempts
            Inc (iCount);
            bFound := True;
            continue;
          end
          else
            // Success :)
            bFound := False;
        end
        else
        begin
          // Record Number
          bFound := False;
          iSetNumbers [iNum] := iNum;

          // Set and Highlight on Grid.
          myCoords := CalculateGridCoords (iNum);
          myObj := TMyObject (sgNumbers.Objects [myCoords.Col, myCoords.Row]);
          myObj.bPaint := True;
        end;
      end;

      // Did we exceed all of our attempts?
      if (iCount >= MAX_ATTEMPTS) then
      begin
        // Report Impossibility factor.
        MessageDlg ('The number of attempts have been exceeded and results to / near impossibility!'#13#10#13#10+
                    'Please try again to use different random sets!', mtWarning, [mbOK], 0);
        exit;
      end;

      exit;
    end; // End of If

    // Otherwise random pick total set of numbers.
    // Generate our number list
    iTotalNumbers := seNumsPerGroup.Value;
    for iX := 1 to iTotalNumbers do
    begin
      bFound := True;
      while bFound do
      begin
        // Randomise a number and Initialise
        iNum := Random (iMAX_NUMBERS);
        if iNum = 0 then
           continue;

        // Checked our saved numbers to see if already picked.
        if (iSetNumbers[iNum] > 0) then
           continue;

        // Record Number
        bFound := False;
        iSetNumbers [iNum] := iNum;

        // Set and Highlight on Grid.
        myCoords := CalculateGridCoords (iNum);
        myObj := TMyObject (sgNumbers.Objects [myCoords.Col, myCoords.Row]);
        myObj.bPaint := True;
      end; // End of While Loop
    end;
  end;

begin
  ZeroMemory (@iSetNumbers, sizeof(iSetNumbers));
  // Set all Cells with appropriate numerical ID.
  for iX := 1 to (iMAX_NUMBERS - 1) do
  begin
    // Calculator Position
    myCoords := CalculateGridCoords (iX);

    // Set appropriate cell with Numerical ID
    myObj := TMyObject (sgNumbers.Objects [myCoords.Col, myCoords.Row]);
    myObj.bPaint := False;
  end;

  // Are we using combinations?
  if chkDoingCombinations.Checked then
  begin
    // Are we to generate groupings?
    if chkGenGrouping.Checked then
       iTotalNumbers := seGroups.Value
    else
       // No Standard Set Numbers.
       iTotalNumbers := seGroups.Value * seNumPerGroup.Value;

    // Generate our random number list.
    GenerateNumbers (iTotalNumbers);
  end
  else
  begin
    // Keeping it real, max 40 numbers maximum!
    iTotalNumbers := seNumsPerGroup.Value;
    if iTotalNumbers > 40 then
       iTotalNumbers := 40;

    // Generate our random number list.
    GenerateNumbers (iTotalNumbers);
  end;

  // Refresh our grid panel so highlighted numbers show up.
  sgNumbers.Repaint;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  // Initialise our Matrix grid and assign all possible objects.
  InitialiseGrid;
end;

procedure TfrmMain.FormDestroy(Sender: TObject);
var
  iX: Integer;
  myObj: TMyObject;
  myCoords: TMyCoords;
begin
  // Set all Cells with appropriate numerical ID.
  for iX := 1 to MAX_NUMBERS do
  begin
    try
      // Calculator Position
      myCoords := CalculateGridCoords (iX);

      // Set appropriate cell with Numerical ID
      myObj := TMyObject (sgNumbers.Objects [myCoords.Col, myCoords.Row]);
      // Release CELL MyObject
      FreeAndNil (myObj);
    except
      continue;
    end;
  end;
end;

// Initialise String Grid to contain the Grid Matrix
procedure TfrmMain.InitialiseGrid;
var
  iX: Integer;
  myObj: TMyObject;
  myCoords: TMyCoords;

begin
  try
    // Set all Cells with appropriate numerical ID and object.
    for iX := 1 to MAX_NUMBERS do
    begin
      // Calculator Position
      myCoords := CalculateGridCoords (iX);

      // Set appropriate cell with Numerical ID
      sgNumbers.Cells[myCoords.Col, myCoords.Row] := IntToStr (iX);
      myObj := TMyObject.Create;
      // Mark this cell for NON Highlighting
      myObj.IsPainted := False;
      // Set Cell Assigned Object
      sgNumbers.Objects [myCoords.Col, myCoords.Row] := TObject (myObj);
    end;

  except
    // Termination application if Exception occurs.
    Application.Terminate;
  end;
end;

procedure TfrmMain.sgNumbersDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  myObj: TMyObject;
begin
  bIsPainting := True;
  with TStringGrid (Sender) do
  begin
    myObj := TMyObject (sgNumbers.Objects [ACol, ARow]);
    if myObj = nil then
       exit;

    // Is this cell marked for Highlighting?
    if myObj.IsPainted then
    begin
      //paint the background clActiveBorder
      Canvas.Brush.Color := clActiveBorder;
      Canvas.FillRect (Rect);
      DrawText (Canvas.Handle, PChar(Cells[ACol, ARow]), Length(Cells[ACol, ARow]),
                Rect, DT_WORDBREAK or DT_EXPANDTABS or DT_CENTER or DT_VCENTER);

    end
    else
    begin
      //paint the background clGrayText
      Canvas.Brush.Color := clGrayText;
      Canvas.FillRect (Rect);
      DrawText (Canvas.Handle, PChar(Cells[ACol, ARow]), Length(Cells[ACol, ARow]),
                Rect, DT_WORDBREAK or DT_EXPANDTABS or DT_CENTER or DT_VCENTER);
    end;
  end;

  bIsPainting := False;
end;

相关问题