2015-11-09 27 views
5

Làm cách nào để tạo các số ngẫu nhiên trong một phạm vi cụ thể bằng crypto.randomBytes?Node.js - Cách tạo số ngẫu nhiên trong phạm vi cụ thể bằng cách sử dụng crypto.randomBytes

Tôi muốn để có thể tạo ra một số ngẫu nhiên như thế này:

console.log(random(55, 956)); // where 55 is minimum and 956 is maximum 

và tôi là hạn chế sử dụng crypto.randomBytes chỉ bên ngẫu nhiên chức năng để tạo ra số ngẫu nhiên cho phạm vi này.

Tôi biết cách chuyển đổi byte được tạo từ randomBytes thành thập phân hoặc thập phân nhưng tôi không thể tìm ra cách lấy số ngẫu nhiên trong một phạm vi cụ thể từ các byte ngẫu nhiên bằng toán học.

+0

Nếu đầu ra có phải là số nguyên hoặc dấu phẩy không? – CodesInChaos

+0

@CodesInChaos integer. – Nicolo

+0

Liên quan: https://github.com/paragonie/random_compat Đó là PHP, nhưng do việc xử lý các số nguyên và phao nổi của PHP khi chạy vào các vấn đề tương tự như bạn đã gặp phải nếu bạn muốn triển khai đúng cách này. – CodesInChaos

Trả lời

2

Nhờ câu trả lời từ @Mustafamg và trợ giúp lớn từ @CodesInChaos Tôi đã giải quyết được vấn đề này. Tôi đã thực hiện một số điều chỉnh và tăng phạm vi lên tối đa 256^6-1 hoặc 281,474,976,710,655. Phạm vi có thể được tăng thêm nhưng bạn cần phải sử dụng thư viện bổ sung cho số nguyên lớn, vì 256^7-1 nằm ngoài giới hạn Số.MAX_SAFE_INTEGER.

Nếu có ai gặp vấn đề tương tự, vui lòng sử dụng nó.

var crypto = require('crypto'); 
 

 
/* 
 
Generating random numbers in specific range using crypto.randomBytes from crypto library 
 
Maximum available range is 281474976710655 or 256^6-1 
 
Maximum number for range must be equal or less than Number.MAX_SAFE_INTEGER (usually 9007199254740991) 
 
Usage examples: 
 
cryptoRandomNumber(0, 350); 
 
cryptoRandomNumber(556, 1250425); 
 
cryptoRandomNumber(0, 281474976710655); 
 
cryptoRandomNumber((Number.MAX_SAFE_INTEGER-281474976710655), Number.MAX_SAFE_INTEGER); 
 

 
Tested and working on 64bit Windows and Unix operation systems. 
 
*/ 
 

 
function cryptoRandomNumber(minimum, maximum){ 
 
\t var distance = maximum-minimum; 
 
\t 
 
\t if(minimum>=maximum){ 
 
\t \t console.log('Minimum number should be less than maximum'); 
 
\t \t return false; 
 
\t } else if(distance>281474976710655){ 
 
\t \t console.log('You can not get all possible random numbers if range is greater than 256^6-1'); 
 
\t \t return false; 
 
\t } else if(maximum>Number.MAX_SAFE_INTEGER){ 
 
\t \t console.log('Maximum number should be safe integer limit'); 
 
\t \t return false; 
 
\t } else { 
 
\t \t var maxBytes = 6; 
 
\t \t var maxDec = 281474976710656; 
 
\t \t 
 
\t \t // To avoid huge mathematical operations and increase function performance for small ranges, you can uncomment following script 
 
\t \t /* 
 
\t \t if(distance<256){ 
 
\t \t \t maxBytes = 1; 
 
\t \t \t maxDec = 256; 
 
\t \t } else if(distance<65536){ 
 
\t \t \t maxBytes = 2; 
 
\t \t \t maxDec = 65536; 
 
\t \t } else if(distance<16777216){ 
 
\t \t \t maxBytes = 3; 
 
\t \t \t maxDec = 16777216; 
 
\t \t } else if(distance<4294967296){ 
 
\t \t \t maxBytes = 4; 
 
\t \t \t maxDec = 4294967296; 
 
\t \t } else if(distance<1099511627776){ 
 
\t \t \t maxBytes = 4; 
 
\t \t \t maxDec = 1099511627776; 
 
\t \t } 
 
\t \t */ 
 
\t \t 
 
\t \t var randbytes = parseInt(crypto.randomBytes(maxBytes).toString('hex'), 16); 
 
\t \t var result = Math.floor(randbytes/maxDec*(maximum-minimum+1)+minimum); 
 
\t \t 
 
\t \t if(result>maximum){ 
 
\t \t \t result = maximum; 
 
\t \t } 
 
\t \t return result; 
 
\t } 
 
}

Cho đến nay nó hoạt động tốt và bạn có thể sử dụng nó tạo số ngẫu nhiên như thực sự tốt, nhưng tôi hoàn toàn không đề xuất sử dụng chức năng này cho bất kỳ dịch vụ mật mã. Nếu bạn sẽ, hãy tự mình sử dụng nó.

Tất cả nhận xét, đề xuất và phê bình đều được hoan nghênh!

+0

Thử 'cryptoRandomNumber (-1, + 1)' và kiểm tra từng kết quả chung là. Tôi hy vọng nó sẽ là 1/4 | 1/2 | 1/4 thay vì đồng phục 1/3 bạn có thể mong đợi. – CodesInChaos

+0

@CodesInChaos Cảm ơn bạn đã đề xuất, bạn đã đúng. Đây là kết quả từ 1 triệu yêu cầu: -1x249836, 0x501146, 1x249018. Và điều này là cho tất cả các kết hợp tôi đã thử, không chỉ từ -1 đến 1 phạm vi: (Bất kỳ ý tưởng làm thế nào tôi có thể sửa chữa nó? – Nicolo

+0

Nếu bạn nhân với 'max-min + 1' và cắt ngắn kết quả thay vì làm tròn nó sẽ là chính xác về mặt toán học, nhưng đảm bảo rằng làm tròn điểm nổi không đôi khi (* rất * hiếm khi) trả về 'max + 1' có thể hơi khó chịu. Vì mã của bạn không sử dụng 53 bit đầy đủ của một đôi, nên không được có thể, nhưng cá nhân tôi muốn viết mã bằng cách sử dụng số học số nguyên chỉ – CodesInChaos

4

Để tạo số trong phạm vi [55 .. 956], trước tiên bạn tạo một số ngẫu nhiên trong phạm vi [0 .. 901] trong đó 901 = 956 - 55. Sau đó, thêm 55 vào số bạn vừa tạo.

Để tạo một số trong phạm vi [0 .. 901], hãy chọn hai byte ngẫu nhiên và bỏ qua 6 bit. Điều đó sẽ cho bạn một số ngẫu nhiên 10 bit trong phạm vi [0 .. 1023]. Nếu số đó là < = 901 thì bạn đã hoàn thành. Nếu nó lớn hơn 901, hãy loại bỏ nó và nhận thêm hai byte ngẫu nhiên. Làm không cố gắng sử dụng MOD, để đưa số vào đúng phạm vi, điều đó sẽ bóp méo đầu ra khiến nó không ngẫu nhiên.

ETA: Để giảm cơ hội phải loại bỏ số được tạo.

Vì chúng tôi đang lấy hai byte từ RNG, chúng tôi nhận được một số trong phạm vi [0 .. 65535]. Bây giờ 65535 MOD 902 là 591. Do đó, nếu số ngẫu nhiên hai byte của chúng tôi nhỏ hơn (65535 - 591), nghĩa là, nhỏ hơn 64944, chúng ta có thể sử dụng toán tử MOD một cách an toàn, vì mỗi số trong phạm vi [0 .. 901] bây giờ là như nhau có khả năng. Bất kỳ số hai byte nào> = 64944 sẽ vẫn phải bị vứt bỏ, vì việc sử dụng nó sẽ bóp méo đầu ra một cách ngẫu nhiên. Trước đây, cơ hội phải từ chối một số là (1024 - 901)/1024 = 12%. Bây giờ cơ hội bị từ chối là (65535 - 64944)/65535 = 1%. Chúng tôi ít có khả năng phải từ chối số được tạo ngẫu nhiên.

running <- true 
while running 
    num <- two byte random 
    if (num < 64944) 
    result <- num MOD 902 
    running <- false 
    endif 
endwhile 
return result + 55 
+0

chỉ cần thêm rằng để chuyển đổi byte thành số nguyên, bạn có thể kiểm tra câu trả lời được đưa ra trong http://stackoverflow.com/questions/15821447/converting-random-bytes-to-an-integer-range-how –

+0

Ý của bạn là hoạt động modulo bằng "MOD"? –

+1

Có, không sử dụng nó trừ khi bạn biết chính xác những gì bạn đang làm. Thật dễ dàng để có được kết quả không ngẫu nhiên sau MOD. – rossum

4

Để tạo số ngẫu nhiên trong một phạm vi nhất định, bạn có thể sử dụng phương trình sau

Math.random() * (high - low) + low 

Nhưng bạn muốn sử dụng crypto.randomBytes thay vì Math.random() hàm này trả về một bộ đệm với byte được tạo ngẫu nhiên. Đổi lại, bạn cần chuyển đổi kết quả của hàm này từ byte sang thập phân. điều này có thể được thực hiện bằng cách sử dụng gói định dạng biguint. Để cài đặt gói này chỉ đơn giản là sử dụng lệnh sau:

npm install biguint-format --save 

Bây giờ bạn cần phải chuyển đổi kết quả của crypto.randomBytes sang thập phân, bạn có thể làm điều đó như sau:

var x= crypto.randomBytes(1); 
return format(x, 'dec'); 

Bây giờ bạn có thể tạo của bạn chức năng ngẫu nhiên sẽ như sau:

var crypto = require('crypto'), 
    format = require('biguint-format'); 

function randomC (qty) { 
    var x= crypto.randomBytes(qty); 
    return format(x, 'dec'); 
} 
function random (low, high) { 
    return randomC(4)/Math.pow(2,4*8-1) * (high - low) + low; 
} 
console.log(random(50,1000)); 
+0

@CodesInChaos: điểm của bạn là hợp lệ, nhưng lưu ý rằng OP chỉ cho biết anh ta muốn sử dụng crypto.randomBytes, không phải là anh ta muốn kết quả được sử dụng trong mật mã. Ngoài ra, 'kịch bản kiểm duyệt' bạn đang đề cập đến là gì? –

+0

@Mustafamg cảm ơn bạn! Đây chính là điều tôi muốn. Bạn nên thêm mặt trước Math.round của randomC và đó là nó. trả về Math.round (randomC (1)/256 * (cao - thấp) + thấp); – Nicolo

+1

@ Mustafamg cũng nếu bạn tạo chỉ vài byte, không cần sử dụng npm có định dạng biguint. Thay vì định dạng ** (x, 'dec') ** chúng ta có thể sử dụng ** parseInt (x.toString ('hex'), 16) ** và tránh thêm mô-đun bổ sung. – Nicolo

Các vấn đề liên quan