Delphi - Random Combination (Math)

2019-09-21 09:18发布

问题:

I have a BIG problem here and do not even know how to start...

In short explanation, I need to know if a number is in a set of results from a random combination...

Let me explain better: I created a random "number" with 3 integer chars from 1 to 8, like this:

procedure TForm1.btn1Click(Sender: TObject);
var
  cTmp: Char;
  sTmp: String[3];
begin
  sTmp := '';
  While (Length(sTmp) < 3) Do
  Begin
    Randomize;
    cTmp := IntToStr(Random(7) + 1)[1];
    If (Pos(cTmp, sTmp) = 0) Then
      sTmp := sTmp + cTmp;
  end;
  edt1.Text := sTmp;
end;

Now I need to know is some other random number, let's say "324" (example), is in the set of results of that random combination.

Please, someone can help? A link to get the equations to solve this problem will be enough...


Ok, let me try to add some useful information:

Please, first check this link https://en.wikipedia.org/wiki/Combination

Once I get some number typed by user, in an editbox, I need to check if it is in the set of this random combination: S = (1..8) and k = 3

Tricky, hum?


Here is what I got. Maybe it be usefull for someone in the future. Thank you for all people that tried to help!

Function IsNumOnSet(const Min, Max, Num: Integer): Boolean;
var
  X, Y, Z: Integer;
Begin
  Result := False;
  For X := Min to Max Do
    For Y := Min to Max Do
      For Z := Min to Max Do
        If (X <> Y) and (X <> Z) and (Y <> Z) Then
          If (X * 100 + Y * 10 + Z = Num) Then
          Begin
            Result := True;
            Exit;
          end;
end;

回答1:

You want to test whether something is a combination. To do this you need to verify that the putative combination satisfies the following conditions:

  1. Each element is in the range 1..N and
  2. No element appears more than once.

So, implement it like this.

  • Declare an array of counts, say array [1..N] of Integer. If N varies at runtime you will need a dynamic array.
  • Initialise all members of the array to zero.
  • Loop through each element of the putative combination. Check that the element is in the range 1..N. And increment the count for that element.
  • If any element has a count greater than 1 then this is not a valid combination.

Now you can simplify by replacing the array of integers with an array of booleans but that should be self evident.



回答2:

You have your generator. Once your value is built, do something like

function isValidCode( Digits : Array of Char;  Value : String ) : Boolean;
var 
    nI : Integer;
begin
       for nI := 0 to High(Digits) do
       begin
             result := Pos(Digits[nI], Value ) > 0;
             if not result then break;
       end;
end;

Call like this...

 isValidCode(["3","2","4"], RandomValue);

Note : it works only because you have unique digits, the digit 3 is only once in you final number. For something more generic, you'll have to tweak this function. (testing "3","3","2" would return true but it would be false !)

UPDATED : I dislike the nested loop ^^. Here is a function that return the nTh digit of an integer. It will return -1 if the digits do not exists. :

 function TForm1.getDigits(value : integer; ndigits : Integer ) : Integer;
 var
    base : Integer;
 begin
       base := Round(IntPower( 10, ndigits-1 ));
       result := Trunc( value /  BASE ) mod 10;
 end;

nDigits is the digits number from right to left starting at 1. It will return the value of the digit.

GetDigits( 234, 1) returns 4
GetDigits( 234, 2) returns 3
GetDigits( 234, 3) returns 2.
GetDigits( 234, 4) returns 0.

Now this last function checks if a value is a good combination, specifying the maxdigits you're looking for :

function isValidCombination( value : integer; MinVal, MaxVal : Integer; MaxDigits : Integer ) :  Boolean;
var
   Buff : Array[0..9] of Integer;
   nI, digit: Integer;
 begin
   ZeroMemory( @Buff, 10*4);

   // Store the count of digits for
   for nI := 1 to MaxDigits do
   begin
      digit := getDigits(value, nI);
      Buff[digit] :=  Buff[digit] + 1;
   end;

   // Check if the value is more than the number of digits.
   if Value >= Round(IntPower( 10, MaxDigits )) then
   begin
     result := False;
     exit;
   end;

   // Check if the value has less than MaxDigits. 
   if Value < Round(IntPower( 10, MaxDigits-1 )) then
   begin
      result := False;
      exit;
   end;


  result := true;
  for nI := 0 to 9 do
  begin
     // Exit if more than One occurence of digit.
     result := Buff[nI] < 2 ;
     if not result then break;

     // Check if digit is present and valid.
     result := (Buff[nI] = 0) or InRange( nI, MinVal, MaxVal );
     if not result then break;
  end;

end;


回答3:

Question does not seem too vague to me, Maybe a bit poorly stated.

From what I understand you want to check if a string is in a set of randomly generated characters.

Here is how that would work fastest, keep a sorted array of all letters and how many times you have each letter.

Subtract each letter from the target string If any value in the sorted int array goes under 0 then that means the string can not be made from those characters.

I made it just work with case insensitive strings but it can easily be made to work with any string by making the alphabet array 255 characters long and not starting from A.

This will not allow you to use characters twice like the other example so 'boom' is not in 'b' 'o' 'm'

Hope this helps you.

function TForm1.isWordInArray(word: string; arr: array of Char):Boolean;
 var
   alphabetCount: array[0..25] of Integer;
   i, baseval, position : Integer;
   s: String;
   c: Char;
begin
  for i := 0  to 25 do alphabetCount[i] := 0;  // init alphabet
  s := UpperCase(word); // make string uppercase
  baseval := Ord('A'); // count A as the 0th letter
  for i := 0 to Length(arr)-1 do begin // disect array and build alhabet
    c := UpCase(arr[i]); // get current letter
    inc(alphabetCount[(Ord(c)-baseval)]); // add 1 to the letter count for that letter
  end;
  for i := 1 to Length(s) do begin // disect string
    c := s[i]; // get current letter
    position := (Ord(c)-baseval);
    if(alphabetCount[position]>0) then // if there is still latters of that kind left
      dec(alphabetCount[position]) // delete 1 to the letter count for that letter
    else begin // letternot there!, exit with a negative result
      Result := False;
      Exit;
    end;
  end;
  Result := True; // all tests where passed, the string is in the array
end;

implemented like so:

if isWordInArray('Delphi',['d','l','e','P','i','h']) then Caption := 'Yup' else Caption := 'Nope';  //yup
if isWordInArray('boom',['b','o','m']) then Caption := 'Yup' else Caption := 'Nope';  //nope, a char can only be used once

Delphi rocks!



回答4:

begin
  Randomize; //only need to execute this once.
  sTmp := '';
  While (Length(sTmp) < 3) Do
  Begin
    cTmp := IntToStr(Random(7) + 1)[1]; // RANDOM(7) produces # from 0..6
                                        // so result will be '1'..'7', not '8'
                                        // Alternative: clmp := chr(48 + random(8));
    If (Pos(cTmp, sTmp) = 0) Then
      sTmp := sTmp + cTmp;
    IF SLMP = '324' THEN
      DOSOMETHING; // don't know what you actually want to do
                   // Perhaps SET SLMP=''; to make sure '324'
                   // isn't generated?
  end;
  edt1.Text := sTmp;
end;