2016-05-23 151 views
1

我想生成指定長度的隨機數。delphi生成隨機PIN碼

function _RandomCode(const CodeLen: Word): Word; 
begin 
    Result := Random(CodeLen); 
    repeat 
    Result := Result + Random(CodeLen) + 1; 
    until (Length(IntToStr(Result)) = CodeLen) 
end; 

結果總是10000

+1

可能的重複的[在delphi中生成隨機數](http://stackoverflow.com/questions/34481987/generate-random-number-in-delphi) –

+1

如果codelen是,比方說,4.會返回一個值0007可以接受嗎?即7個前導零。或者你只在1000到9999之間尋找數字? –

+1

隨機(4)可以是0,1,2或3.爲什麼不簡單:'Result:= Random(IntPow(10,CodeLen));'? –

回答

6

雖然我投票決定關閉這個問題作爲一個重複,再一想這裏有問題,使得它不同於純粹的隨機函數應用程序。

首先,你的代碼不給你期待的結果,因爲你是添加小數字(0 .. codelen-1)的結果每個循環中,當值達到的值停止的是,當轉換爲字符串時,包含codelen個字符。對於codelen = 5,這將始終停止在10000 .. 10003.如果您要在調試器中加入代碼,您很快就會意識到爲什麼您獲得了您所做的結果。其次,在@MichaelVincent的啓發下,PIN碼通常允許前導零,f.ex。 '0123'。因此,我也認爲在這個問題上也是如此。因爲整型結果不能保存前導零,所以我建議你使用string型結果。

只在應用程序啓動時調用Randomize一次。

function _RandomCodeStr(const CodeLen: Word): string; 
var 
    n: integer; 
begin 
    SetLength(Result, CodeLen); 
    for n := 1 to CodeLen do 
    Result[n] := Char(ord('0')+ Random(10)); 
end; 

我改變了函數的名稱以反映它返回一個字符串。


添加對請求:

關於Randomize函數(或分配RandSeed)。它在文檔中有解釋:

隨機初始化內置隨機數發生器,隨機值爲 (從系統時鐘獲得)。隨機數 生成器應通過調用Randomize或 賦值給RandSeed來初始化。

不要將調用與隨機函數的調用結合起來調用隨機函數,並調用 隨機函數。通常,在調用Random之前,只調用一次Randomize。

如果你把_RandomCodeStr功能在一個單獨的單元,你可以把呼叫Randomize在單位initialization部分。

+0

你拿起了我在我的評論問題中得到的東西:D –

+0

你能解釋爲什麼我應該在應用程序啓動時調用'Randomize'而不是在函數本身內調用它。因爲我想把這個功能放在單獨的單元中,每當我需要它時調用它,並且在每個應用程序中擺脫調用'Randomize' – RepeatUntil

+0

@RepeatUntil我編輯了有關'Randomize'的回答,以及如何在您計劃的單元中調用一次把它塞進去。 –

1

除了使用randomize(但只有一次!)爲指向的湯姆Brunberg

1)隨機兩點需要注意是不是密碼安全PRNG,所以用它產生隨機(如在不可預知的情況下)PIN碼是相當不恰當的想法。

2)你的循環速度慢,甚至不太安全(通過連續多次調用Random,導致其統計量累積降低隨機性)。 它可能是不確定的。如果一個時刻的長度會比CodeLen多?它永遠不會縮小,循環永遠不會結束;直到至少整數邊界繞回。

使之顯得有些更好的,你可以那樣做:

function _PseudoRandomCode(const CodeLen: Word): Word; 
var s: string; i: integer; c: char; 
begin 
    SetLength(s, CodeLen); 
    for i := 1 to CodeLen do begin 
    c := '0'; 
    Inc(c, random(10)); // + 0..9 
    s[i] := c; 
    end; 
    Result := StrToInt(s); 
end; 

或類似的

function _PseudoRandomCode(const CodeLen: Word): Word; 
var i,j: integer; 
begin 
    i := 1; // 10^0 
    for j := 1 to CodeLen do 
     i := 10 * i; 
    // now i = 10^(CodeLen+1) that is '1' and CodeLen of zeroes. 

    Result := Random(i); 
end; 

雖然我會更好地使返回string值不word值的函數。

function _PseudoRandomCode(const CodeLen: Word): String; 
var s: string; i: integer; c: char; 
begin 
    SetLength(s, CodeLen); 
    for i := 1 to CodeLen do begin 
    c := '0'; 
    Inc(c, random(10)); // + 0..9 
    s[i] := c; 
    end; 
    Result := s; 
end; 

function _PseudoRandomCode(const CodeLen: Word): string; 
var i,j: integer; 
begin 
    i := 1; // 10^0 
    for j := 1 to CodeLen do 
     i := 10 * i; 
    // now i = 10^(CodeLen+1) that is '1' and CodeLen of zeroes. 

    Result := IntToStr(Random(i)); 
    if Length(Result) < CodeLen then 
    Result := StringOfChar('0', CodeLen - Length(Result)) + Result; 
end; 

你會做什麼,例如_RandomCode(4) = 25?您將不得不在該功能以外的任何位置使用零填充PIN碼!最好在功能內進行一次。

+1

在第二個代碼中,對於Codelen,j:= 0似乎是一次太多。使用'codelen = 1''i'將變爲100,IOW,返回值爲0 .. 99 –

+0

@TomBrunberg正確。固定。 –

3

您的代碼失敗的原因:您要添加較小的值(1..5),直到數字位數達到5爲止。總是得到10000的原因是您在啓動時從未調用過Randomize。這意味着你的「隨機」系列總是一樣的。

使用RandomRange代替:

返回指定範圍內的隨機整數。

RandomRange返回從AFrom和ATo(非包含)之間延伸的範圍內的隨機整數。 RandomRange可以處理負範圍(其中AFrom大於ATo)。

要初始化隨機數生成器,請在調用RandomRange之前添加一個調用Randomize或將值分配給RandSeed變量。

function _RandomCode(const CodeLen: Word): Cardinal; 
// CodeLen is 1,2,3 etc. 
begin 
    Result := RandomRange(0,Trunc(IntPower(10,CodeLen))); 
end; 

我假定前導零是在PIN碼可以接受的,否則所述第一參數設定爲適當的值(IntPower(10,CodeLen-1))。

要轉換一些固定長度和可能的前導零的字符串,使用:

Format('%.*d',[CodeLen,_RandomCode(CodeLen)]) 
+2

「Random」返回一個整數X,比如0 <= X

+2

有些問題:'Random(intvalue)'給出整數; RandomRange不包括ATo,所以'-1'是多餘的;作者一定要'Codelen = 1,2,3,4,5',而不是十個權力。 – MBo

+1

CodeLen其實不是CodeLen,更好的計算方法是 –