มีฟังก์ชั่นในการสร้างหมายเลข int แบบสุ่มใน C หรือไม่? หรือฉันจะต้องใช้ห้องสมุดบุคคลที่สาม?
srand
: ทำไมเรียกว่าเพียงครั้งเดียว
มีฟังก์ชั่นในการสร้างหมายเลข int แบบสุ่มใน C หรือไม่? หรือฉันจะต้องใช้ห้องสมุดบุคคลที่สาม?
srand
: ทำไมเรียกว่าเพียงครั้งเดียว
คำตอบ:
หมายเหตุ : อย่าใช้
rand()
เพื่อความปลอดภัย หากคุณต้องการหมายเลขที่ปลอดภัยเข้ารหัสให้ดูคำตอบนี้แทน
#include <time.h>
#include <stdlib.h>
srand(time(NULL)); // Initialization, should only be called once.
int r = rand(); // Returns a pseudo-random integer between 0 and RAND_MAX.
แก้ไข : บน Linux คุณอาจต้องการใช้สุ่มและ srandom
time()
เปลี่ยนแปลงเพียงครั้งเดียวต่อวินาที หากคุณทำการเพาะปลูกtime()
สำหรับการโทรแต่ละครั้งrand()
คุณจะได้รับค่าเดียวกันสำหรับการโทรทุกครั้งในหนึ่งวินาที แต่เหตุผลที่ใหญ่กว่าก็คือคุณสมบัติของrand()
และฟังก์ชั่นเช่นนี้เป็นที่รู้จักกันดีที่สุดสำหรับกรณีการใช้งานที่พวกเขาได้รับการเพาะเมล็ดอย่างแน่นอนหนึ่งครั้งต่อการใช้งานและไม่ได้อยู่ในการโทรทุกครั้ง ขึ้นอยู่กับ "การสุ่ม" ด้วยคุณสมบัติที่ยังไม่ทดลองหรือไม่ผ่านการพิสูจน์แล้วทำให้เกิดปัญหา
rand()
ปกติ) การเพาะด้วยวิธีrand()
ที่ดีที่สุดจะไม่มีผลเลยและที่แย่ที่สุดจะทำลายคุณสมบัติที่รู้จักของเครื่องกำเนิด นี่เป็นเรื่องที่ลึก เริ่มต้นด้วยการอ่านKnuth เล่มที่ 2บทที่ 3 เกี่ยวกับตัวเลขสุ่มว่าเป็นการแนะนำที่ดีที่สุดสำหรับคณิตศาสตร์และข้อผิดพลาด
srand((unsigned int)time(NULL));
rand()
ฟังก์ชั่นใน<stdlib.h>
ผลตอบแทนที่เป็นจำนวนเต็มสุ่มหลอกระหว่าง 0 RAND_MAX
และ คุณสามารถใช้srand(unsigned int seed)
เพื่อตั้งค่าเมล็ด
มันเป็นเรื่องธรรมดาที่จะใช้ตัว%
ดำเนินการร่วมกับrand()
เพื่อให้ได้ช่วงที่แตกต่างกัน (แต่จำไว้ว่าสิ่งนี้ทำให้ความสม่ำเสมอของมันค่อนข้างสม่ำเสมอ) ตัวอย่างเช่น:
/* random int between 0 and 19 */
int r = rand() % 20;
หากคุณจริงๆดูแลเกี่ยวกับความสม่ำเสมอของคุณสามารถทำอะไรเช่นนี้
/* Returns an integer in the range [0, n).
*
* Uses rand(), and so is affected-by/affects the same seed.
*/
int randint(int n) {
if ((n - 1) == RAND_MAX) {
return rand();
} else {
// Supporting larger values for n would requires an even more
// elaborate implementation that combines multiple calls to rand()
assert (n <= RAND_MAX)
// Chop off all of the values that would cause skew...
int end = RAND_MAX / n; // truncate skew
assert (end > 0);
end *= n;
// ... and ignore results from rand() that fall above that limit.
// (Worst case the loop condition should succeed 50% of the time,
// so we can expect to bail out of this loop pretty quickly.)
int r;
while ((r = rand()) >= end);
return r % n;
}
}
%
เป็นตัวดำเนินการโมดูลัส มันให้ส่วนที่เหลือของการหารจำนวนเต็มดังนั้นx % n
จะให้ตัวเลขระหว่าง0
และn - 1
(ตราบเท่าที่x
และn
ทั้งสองเป็นบวก) หากคุณยังพบว่ามีความสับสนลองเขียนโปรแกรมที่มีค่าi
ตั้งแต่ 0 ถึง 100 และพิมพ์ออกมาi % n
ตามn
จำนวนที่คุณเลือกน้อยกว่า 100
ตามที่ได้ระบุไว้ในวิธีสร้างหมายเลขสุ่มในภาษาการเขียนโปรแกรมที่หลากหลายคุณจะต้องทำอย่างใดอย่างหนึ่งต่อไปนี้:
randombytes
/dev/urandom
/dev/random
ไม่ได้ ไม่ใช่ OpenSSL (หรือ PRSG userspace อื่น ๆ )ตัวอย่างเช่น:
#include "sodium.h"
int foo()
{
char myString[32];
uint32_t myInt;
if (sodium_init() < 0) {
/* panic! the library couldn't be initialized, it is not safe to use */
return 1;
}
/* myString will be an array of 32 random bytes, not null-terminated */
randombytes_buf(myString, 32);
/* myInt will be a random number between 0 and 9 */
myInt = randombytes_uniform(10);
}
randombytes_uniform()
มีความปลอดภัยและไม่มีอคติเข้ารหัสลับ
sodium_init()
ที่จุดใดจุดหนึ่ง ไม่ต้องกังวลกับ RNG มันใช้เคอร์เนล
sodium_init()
แม้ว่าจะไม่จำเป็นต้องเป็นส่วนหนึ่งของตัวอย่างเพราะเป็นรายละเอียดที่สำคัญ
มาช่วยกันทำสิ่งนี้ ก่อนอื่นเราใช้ฟังก์ชั่น srand () เพื่อหว่านโปรแกรมสุ่ม โดยทั่วไปคอมพิวเตอร์สามารถสร้างตัวเลขสุ่มตามจำนวนที่ป้อนไปยัง srand () หากคุณให้ค่าเมล็ดเดียวกันจำนวนสุ่มจะถูกสร้างขึ้นทุกครั้ง
ดังนั้นเราต้องทำการสุ่มตัวสุ่มด้วยค่าที่เปลี่ยนแปลงอยู่เสมอ เราทำสิ่งนี้โดยป้อนค่าของเวลาปัจจุบันด้วยฟังก์ชัน time ()
ตอนนี้เมื่อเราเรียก rand () หมายเลขสุ่มใหม่จะถูกสร้างขึ้นทุกครั้ง
#include <stdio.h>
int random_number(int min_num, int max_num);
int main(void)
{
printf("Min : 1 Max : 40 %d\n", random_number(1,40));
printf("Min : 100 Max : 1000 %d\n",random_number(100,1000));
return 0;
}
int random_number(int min_num, int max_num)
{
int result = 0, low_num = 0, hi_num = 0;
if (min_num < max_num)
{
low_num = min_num;
hi_num = max_num + 1; // include max_num in output
} else {
low_num = max_num + 1; // include max_num in output
hi_num = min_num;
}
srand(time(NULL));
result = (rand() % (hi_num - low_num)) + low_num;
return result;
}
else{ low_num=max_num; hi_num=min_num+1;
2) hi_num - low_num > INT_MAX
ล้มเหลวเมื่อ 3) INT_MAX > hi_num - low_num > RAND_MAX
ค่าละเว้นในสถานการณ์ที่หายาก
hi_num = max_num + 1;
ไม่มีการป้องกันการล้น
หากคุณต้องการตัวเลขสุ่มที่มีคุณภาพที่ดีขึ้นกว่าสิ่งที่หลอกstdlib
ให้ตรวจสอบMersenne Twister มันเร็วกว่าด้วย การใช้งานตัวอย่างมีมากมายตัวอย่างเช่นที่นี่
ฟังก์ชั่น C rand()
มาตรฐานคือ มันดีพอที่จะแจกไพ่สำหรับเล่นไพ่คนเดียว แต่มันแย่มาก การใช้งานหลายrand()
รอบผ่านรายการตัวเลขสั้น ๆ และบิตต่ำมีรอบสั้นกว่า วิธีที่บางโปรแกรมเรียกrand()
นั้นแย่และการคำนวณเมล็ดพันธุ์ที่ดีที่จะผ่านไปsrand()
นั้นยาก
วิธีที่ดีที่สุดในการสร้างตัวเลขสุ่มใน C คือการใช้ห้องสมุดบุคคลที่สามเช่น OpenSSL ตัวอย่างเช่น,
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>
/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
union {
unsigned int i;
unsigned char c[sizeof(unsigned int)];
} u;
do {
if (!RAND_bytes(u.c, sizeof(u.c))) {
fprintf(stderr, "Can't get random bytes!\n");
exit(1);
}
} while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
return u.i % limit;
}
/* Random double in [0.0, 1.0) */
double random_double() {
union {
uint64_t i;
unsigned char c[sizeof(uint64_t)];
} u;
if (!RAND_bytes(u.c, sizeof(u.c))) {
fprintf(stderr, "Can't get random bytes!\n");
exit(1);
}
/* 53 bits / 2**53 */
return (u.i >> 11) * (1.0/9007199254740992.0);
}
int main() {
printf("Dice: %d\n", (int)(random_uint(6) + 1));
printf("Double: %f\n", random_double());
return 0;
}
ทำไมต้องใช้รหัสมาก? ภาษาอื่น ๆ เช่น Java และ Ruby มีฟังก์ชั่นสำหรับจำนวนเต็มหรือลอยแบบสุ่ม OpenSSL ให้การสุ่มแบบไบท์เท่านั้นดังนั้นฉันจึงลองเลียนแบบว่า Java หรือ Ruby จะแปลงมันเป็นจำนวนเต็มหรือลอยตัวได้อย่างไร
สำหรับจำนวนเต็มเราต้องการที่จะหลีกเลี่ยงอคติโมดูโล สมมติว่าเราได้รับจำนวนเต็ม 4 หลักแบบสุ่มrand() % 10000
แต่rand()
สามารถส่งกลับ 0 ถึง 32767 (เช่นเดียวกับใน Microsoft Windows) แต่ละหมายเลขตั้งแต่ 0 ถึง 2767 จะปรากฏบ่อยกว่าแต่ละหมายเลขจาก 2768 ถึง 9999 หากต้องการลบอคติเราสามารถลองอีกครั้งrand()
ในขณะที่ค่าต่ำกว่า 2768 เนื่องจากค่า 30000 จาก 2768 ถึง 32767 แมปไปที่ 10,000 ค่าจาก 0 ถึง 9999
สำหรับโฟลว์เราต้องการ 53 บิตสุ่มเนื่องจากdouble
ความแม่นยำถือ 53 บิต (สมมติว่าเป็น IEEE สองเท่า) ถ้าเราใช้มากกว่า 53 บิตเราจะได้อคติการปัดเศษ โปรแกรมเมอร์บางคนเขียนโค้ดเช่นrand() / (double)RAND_MAX
แต่rand()
อาจส่งคืนเพียง 31 บิตหรือ 15 บิตเท่านั้นใน Windows
RAND_bytes()
เมล็ดของ OpenSSL นั้นอาจจะอ่านโดยใช้/dev/urandom
Linux หากเราต้องการตัวเลขสุ่มจำนวนมากมันจะช้าเกินกว่าที่จะอ่านพวกเขาทั้งหมด/dev/urandom
เพราะพวกเขาจะต้องคัดลอกมาจากเคอร์เนล มันเร็วกว่าที่จะอนุญาตให้ OpenSSL สร้างตัวเลขสุ่มเพิ่มเติมจากเมล็ด
เพิ่มเติมเกี่ยวกับตัวเลขสุ่ม:
srand()
สำหรับ มันผสมบิตจากเวลาปัจจุบัน ID กระบวนการและพอยน์เตอร์บางตัวหากไม่สามารถอ่าน/dev/urandom
ได้float
/ double
ดังนั้นฉันได้ชี้แจงคำถามที่ยึดติดกับint
ตัวเลขเพื่อหลีกเลี่ยงการทำให้กว้างเกินไป มีคำถาม C อื่น ๆ ที่เกี่ยวข้องเฉพาะกับการมีfloat
/ double
ค่าสุ่มดังนั้นคุณอาจต้องการที่จะ repost ครึ่งหลังของคุณคำตอบของคำถามเช่นstackoverflow.com/questions/13408990/...
หากระบบของคุณรองรับarc4random
ตระกูลของฟังก์ชั่นฉันขอแนะนำให้ใช้ฟังก์ชันเหล่านั้นแทนrand
ฟังก์ชั่นมาตรฐาน
arc4random
ครอบครัวรวมถึง:
uint32_t arc4random(void)
void arc4random_buf(void *buf, size_t bytes)
uint32_t arc4random_uniform(uint32_t limit)
void arc4random_stir(void)
void arc4random_addrandom(unsigned char *dat, int datlen)
arc4random
ส่งคืนจำนวนเต็มที่ไม่ได้ลงชื่อแบบ 32 บิตแบบสุ่ม
arc4random_buf
ใส่เนื้อหาแบบสุ่มในพารามิเตอร์buf : void *
มัน จำนวนเนื้อหาถูกกำหนดโดยbytes : size_t
พารามิเตอร์
arc4random_uniform
ส่งคืนเลขจำนวนเต็มแบบไม่ระบุชื่อแบบ 32 บิตแบบสุ่มซึ่งเป็นไปตามกฎ: 0 <= arc4random_uniform(limit) < limit
โดยขีด จำกัด จะเป็นจำนวนเต็มแบบ 32 บิตแบบไม่ได้ลงนาม
arc4random_stir
อ่านข้อมูลจาก/dev/urandom
และส่งผ่านข้อมูลไปarc4random_addrandom
ยังเพื่อสุ่มเพิ่มเติมเป็นกลุ่มตัวเลขสุ่มภายใน
arc4random_addrandom
จะถูกใช้โดยarc4random_stir
การเติมมันเป็นกลุ่มหมายเลขสุ่มภายในตามข้อมูลที่ส่งผ่านไป
หากคุณไม่มีฟังก์ชั่นเหล่านี้ แต่คุณใช้ระบบปฏิบัติการยูนิกซ์คุณสามารถใช้รหัสนี้ได้:
/* This is C, not C++ */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h> /* exit */
#include <stdio.h> /* printf */
int urandom_fd = -2;
void urandom_init() {
urandom_fd = open("/dev/urandom", O_RDONLY);
if (urandom_fd == -1) {
int errsv = urandom_fd;
printf("Error opening [/dev/urandom]: %i\n", errsv);
exit(1);
}
}
unsigned long urandom() {
unsigned long buf_impl;
unsigned long *buf = &buf_impl;
if (urandom_fd == -2) {
urandom_init();
}
/* Read 4 bytes, or 32 bits into *buf, which points to buf_impl */
read(urandom_fd, buf, sizeof(long));
return buf_impl;
}
urandom_init
ฟังก์ชั่นเปิดอุปกรณ์และทำให้อธิบายไฟล์ใน/dev/urandom
urandom_fd
urandom
ฟังก์ชั่นเป็นพื้นเดียวกับการเรียกrand
ยกเว้นความปลอดภัยมากขึ้นและจะส่งกลับlong
(อาจมีการเปลี่ยนแปลงได้อย่างง่ายดาย)
อย่างไรก็ตาม/dev/urandom
อาจช้าไปหน่อยขอแนะนำให้คุณใช้เป็นเมล็ดสำหรับเครื่องกำเนิดหมายเลขสุ่มแบบอื่น
หากระบบของคุณไม่ได้มี/dev/urandom
แต่ไม่มี/dev/random
หรือแฟ้มที่คล้ายกันแล้วคุณก็สามารถเปลี่ยนเส้นทางผ่านไปในopen
urandom_init
การโทรและ API ที่ใช้ในurandom_init
และurandom
เป็น (ฉันเชื่อว่า) เป็นไปตาม POSIX และเป็นเช่นนี้ควรทำงานได้มากที่สุดถ้าไม่ใช่ระบบที่สอดคล้องกับ POSIX ทั้งหมด
หมายเหตุ: การอ่านจาก/dev/urandom
จะไม่ปิดกั้นหากมีเอนโทรปีไม่เพียงพอดังนั้นค่าที่สร้างขึ้นภายใต้สถานการณ์เช่นนั้นอาจไม่ปลอดภัยแบบเข้ารหัส หากคุณกังวลเกี่ยวกับสิ่งนั้นให้ใช้/dev/random
ซึ่งจะปิดกั้นเสมอหากมีเอนโทรปีไม่เพียงพอ
หากคุณอยู่ในระบบอื่น (เช่น Windows) ให้ใช้rand
หรือ API พกพาที่ไม่ขึ้นอยู่กับแพลตฟอร์มเฉพาะของ Windows ภายใน
ฟังก์ชั่นสำหรับ Wrapper urandom
, rand
หรือarc4random
โทร:
#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */
int myRandom(int bottom, int top){
return (RAND_IMPL() % (top - bottom)) + bottom;
}
STL ไม่อยู่สำหรับซีคุณต้องโทรหรือดีกว่ายังrand
เหล่านี้มีการประกาศในส่วนหัวห้องสมุดมาตรฐานrandom
คือ POSIX เป็นฟังก์ชันจำเพาะของ BSDstdlib.h
rand
random
ความแตกต่างระหว่างrand
กับrandom
คือrandom
ส่งคืนตัวเลขสุ่มแบบ 32 บิตที่สามารถใช้งานได้มากกว่าrand
โดยทั่วไปแล้วจะส่งกลับตัวเลข 16 บิต BSD manpages แสดงให้เห็นว่าบิตที่ต่ำกว่าrand
นั้นมีลักษณะเป็นวงรอบและสามารถคาดการณ์ได้ดังนั้นจึงrand
ไม่มีประโยชน์สำหรับตัวเลขขนาดเล็ก
extern int rand(void);
extern void srand(unsigned int);
ดูที่ISAAC (การเปลี่ยนทิศทางเปลี่ยนการสะสมเพิ่มและนับ) มันกระจายอย่างสม่ำเสมอและมีความยาววงจรเฉลี่ย 2 ^ 8295
นี่เป็นวิธีที่ดีในการรับตัวเลขสุ่มระหว่างตัวเลขสองตัวที่คุณเลือก
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define randnum(min, max) \
((rand() % (int)(((max) + 1) - (min))) + (min))
int main()
{
srand(time(NULL));
printf("%d\n", randnum(1, 70));
}
เอาต์พุตในครั้งแรก: 39
เอาท์พุทเป็นครั้งที่สอง: 61
เอาท์พุทเป็นครั้งที่สาม: 65
คุณสามารถเปลี่ยนค่าหลังจากrandnum
เป็นตัวเลขใดก็ได้ที่คุณเลือกและจะสร้างตัวเลขสุ่มสำหรับคุณระหว่างตัวเลขทั้งสอง
rand()
คุณต้องการที่จะใช้ หมายเหตุ ( สำคัญมาก ): ตรวจสอบให้แน่ใจว่าได้ตั้งค่า seed สำหรับฟังก์ชัน rand ถ้าคุณทำไม่ได้ตัวเลขสุ่มของคุณจะไม่สุ่มอย่างแท้จริง สิ่งนี้สำคัญมากและสำคัญมาก โชคดีที่คุณมักจะสามารถใช้การรวมกันของระบบติ๊กตัวจับเวลาและวันที่เพื่อรับเมล็ดพันธุ์ที่ดี
FWIW คำตอบคือใช่มีstdlib.h
ฟังก์ชั่นที่เรียกว่าrand
; ฟังก์ชั่นนี้ได้รับการปรับจูนเป็นหลักเพื่อความเร็วและการกระจายไม่ใช่สำหรับการคาดเดาไม่ได้ ฟังก์ชันสุ่มในตัวเกือบทั้งหมดสำหรับภาษาและกรอบต่าง ๆ ใช้ฟังก์ชันนี้ตามค่าเริ่มต้น นอกจากนี้ยังมีเครื่องกำเนิดเลขสุ่ม "เข้ารหัส" ที่คาดการณ์ได้น้อยกว่ามาก แต่ทำงานได้ช้ากว่ามาก ควรใช้สิ่งเหล่านี้ในแอปพลิเคชันที่เกี่ยวข้องกับความปลอดภัยทุกประเภท
หวังว่านี่จะสุ่มขึ้นมามากกว่าการใช้เพียงsrand(time(NULL))
เล็กน้อย
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
srand((unsigned int)**main + (unsigned int)&argc + (unsigned int)time(NULL));
srand(rand());
for (int i = 0; i < 10; i++)
printf("%d\n", rand());
}
STL คือ C ++ ไม่ใช่ C ดังนั้นฉันไม่รู้ว่าคุณต้องการอะไร หากคุณต้องการ C อย่างไรก็ตามมีrand()
และsrand()
ฟังก์ชั่น:
int rand(void);
void srand(unsigned seed);
เหล่านี้เป็นส่วนหนึ่งของ ANSI C นอกจากนี้ยังมีrandom()
ฟังก์ชั่น:
long random(void);
แต่เท่าที่ฉันสามารถบอกได้ว่าrandom()
ไม่ใช่มาตรฐาน ANSI C ห้องสมุดบุคคลที่สามอาจไม่ใช่ความคิดที่ไม่ดี แต่ทั้งหมดขึ้นอยู่กับว่าคุณต้องการสร้างจำนวนสุ่มเพียงใด
C โปรแกรมเพื่อสร้างตัวเลขสุ่มระหว่าง 9 และ 50
#include <time.h>
#include <stdlib.h>
int main()
{
srand(time(NULL));
int lowerLimit = 10, upperLimit = 50;
int r = lowerLimit + rand() % (upperLimit - lowerLimit);
printf("%d", r);
}
โดยทั่วไปเราสามารถสร้างตัวเลขสุ่มระหว่างlowerLimit และ upperLimit-1
ie lowerLimit ถูกรวมหรือว่าr ∈ [lowerLimit, UpperLimit)
rand()
เป็นวิธีที่สะดวกที่สุดในการสร้างตัวเลขสุ่ม
นอกจากนี้คุณยังสามารถจับหมายเลขสุ่มจากบริการออนไลน์ใด ๆ เช่น random.org
#include <stdio.h>
#include <stdlib.h>
void main()
{
int visited[100];
int randValue, a, b, vindex = 0;
randValue = (rand() % 100) + 1;
while (vindex < 100) {
for (b = 0; b < vindex; b++) {
if (visited[b] == randValue) {
randValue = (rand() % 100) + 1;
b = 0;
}
}
visited[vindex++] = randValue;
}
for (a = 0; a < 100; a++)
printf("%d ", visited[a]);
}
#include <stdio.h>
#include <dos.h>
int random(int range);
int main(void)
{
printf("%d", random(10));
return 0;
}
int random(int range)
{
struct time t;
int r;
gettime(&t);
r = t.ti_sec % range;
return r;
}
ในซีพียู x86_64 ที่ทันสมัยคุณสามารถใช้ตัวสร้างหมายเลขสุ่มฮาร์ดแวร์ได้ _rdrand64_step()
รหัสตัวอย่าง:
#include <immintrin.h>
uint64_t randVal;
if(!_rdrand64_step(&randVal)) {
// Report an error here: random number generation has failed!
}
// If no error occured, randVal contains a random 64-bit number
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//generate number in range [min,max)
int random(int min, int max){
int number = min + rand() % (max - min);
return number;
}
//Driver code
int main(){
srand(time(NULL));
for(int i = 1; i <= 10; i++){
printf("%d\t", random(10, 100));
}
return 0;
}
การได้ยินคำอธิบายที่ดีว่าทำไมการใช้rand()
เพื่อสร้างตัวเลขสุ่มที่กระจายอย่างสม่ำเสมอในช่วงที่กำหนดเป็นความคิดที่ไม่ดีฉันจึงตัดสินใจดูว่าผลลัพธ์ที่บิดเบือนนั้นเป็นอย่างไร กรณีทดสอบของฉันคือการโยนลูกเต๋าอย่างยุติธรรม นี่คือรหัส C:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char *argv[])
{
int i;
int dice[6];
for (i = 0; i < 6; i++)
dice[i] = 0;
srand(time(NULL));
const int TOTAL = 10000000;
for (i = 0; i < TOTAL; i++)
dice[(rand() % 6)] += 1;
double pers = 0.0, tpers = 0.0;
for (i = 0; i < 6; i++) {
pers = (dice[i] * 100.0) / TOTAL;
printf("\t%1d %5.2f%%\n", dice[i], pers);
tpers += pers;
}
printf("\ttotal: %6.2f%%\n", tpers);
}
และนี่คือผลลัพธ์:
$ gcc -o t3 t3.c
$ ./t3
1666598 16.67%
1668630 16.69%
1667682 16.68%
1666049 16.66%
1665948 16.66%
1665093 16.65%
total: 100.00%
$ ./t3
1667634 16.68%
1665914 16.66%
1665542 16.66%
1667828 16.68%
1663649 16.64%
1669433 16.69%
total: 100.00%
ฉันไม่ทราบว่าคุณต้องการหมายเลขสุ่มของคุณอย่างไร
แก้ไข: มันจะเป็นความคิดที่ดีในการเริ่มต้น PRNG time(NULL)
กับสิ่งที่ดีกว่า
ฉันมีปัญหาร้ายแรงกับตัวสร้างตัวเลขสุ่มหลอกในแอปพลิเคชันล่าสุดของฉัน: ฉันเรียกโปรแกรม C ของฉันซ้ำ ๆ ผ่านสคริปต์ pyhton และฉันใช้เป็นรหัสต่อไปนี้:
srand(time(NULL))
อย่างไรก็ตามเนื่องจาก:
man srand
);time
จะส่งคืนค่าเดิมทุกครั้งโปรแกรมของฉันสร้างลำดับหมายเลขเดียวกัน คุณสามารถทำ 3 สิ่งเพื่อแก้ไขปัญหานี้:
แสดงเวลาผสมกับข้อมูลอื่น ๆ ที่เปลี่ยนแปลงเมื่อทำงาน (ในแอปพลิเคชันของฉันชื่อเอาต์พุต):
srand(time(NULL) | getHashOfString(outputName))
ฉันใช้djb2เป็นฟังก์ชั่นแฮช
เพิ่มความละเอียดของเวลา บนแพลตฟอร์มของฉันclock_gettime
พร้อมใช้งานดังนั้นฉันจึงใช้มัน:
#include<time.h>
struct timespec nanos;
clock_gettime(CLOCK_MONOTONIC, &nanos)
srand(nanos.tv_nsec);
ใช้ทั้งสองวิธีร่วมกัน:
#include<time.h>
struct timespec nanos;
clock_gettime(CLOCK_MONOTONIC, &nanos)
srand(nanos.tv_nsec | getHashOfString(outputName));
ตัวเลือกที่ 3 ทำให้คุณมั่นใจได้ (เท่าที่ฉันรู้) การสุ่มเมล็ดที่ดีที่สุด แต่มันอาจสร้างความแตกต่างในแอปพลิเคชันที่รวดเร็วมากเท่านั้น ในความคิดของฉันตัวเลือกที่ 2 เป็นการเดิมพันที่ปลอดภัย
rand()
ไม่ควรใช้สำหรับข้อมูลการเข้ารหัสลับฉันเห็นด้วย อย่างน้อยสำหรับฉันแอปพลิเคชันของฉันไม่เกี่ยวข้องกับข้อมูลการเข้ารหัสดังนั้นสำหรับฉันมันก็โอเคกับวิธีการที่กำหนด
แม้จะมีคำแนะนำของทุกคนrand()
ที่นี่คุณไม่ต้องการใช้rand()
เว้นแต่คุณจะต้อง! ตัวเลขสุ่มที่rand()
สร้างมักจะไม่ดีมาก อ้างจากหน้าลินุกซ์:
เวอร์ชันของ
rand()
และsrand()
ใน Linux C Library ใช้ตัวสร้างตัวเลขสุ่มแบบเดียวกับrandom(3)
และsrandom(3)
ดังนั้นบิตลำดับที่ต่ำกว่าควรเป็นแบบสุ่มเป็นบิตลำดับสูงกว่า อย่างไรก็ตามในการใช้งาน rand () ที่เก่ากว่าและในการใช้งานในปัจจุบันในระบบที่แตกต่างกันบิตลำดับที่ต่ำกว่านั้นจะสุ่มน้อยกว่าบิตลำดับสูงกว่ามาก อย่าใช้ฟังก์ชั่นนี้ในแอพพลิเคชั่นที่ต้องการพกพาเมื่อจำเป็นต้องใช้การสุ่มตัวอย่างที่ดี ( ใช้random(3)
แทน )
เกี่ยวกับการพกพาrandom()
ยังถูกกำหนดโดยมาตรฐาน POSIX ในบางเวลา rand()
เก่ากว่ามันปรากฏในสเป็ค POSIX.1 แรก (IEEE Std 1003.1-1988) แล้วrandom()
ปรากฏครั้งแรกในแต่มาตรฐาน POSIX ปัจจุบันเป็น POSIX.1-2008 แล้ว (IEEE Std 1003.1-2008) ซึ่งได้รับการอัปเดตเมื่อหนึ่งปีก่อน (IEEE Std 1003.1-2008, 2016 Edition) ดังนั้นฉันจะพิจารณาrandom()
ให้เป็นแบบพกพามาก
POSIX.1-2001 ยังแนะนำlrand48()
และmrand48()
ฟังก์ชั่นดูที่นี่ :
ตระกูลของฟังก์ชั่นนี้จะสร้างตัวเลขสุ่มหลอกโดยใช้อัลกอริธึมเชิงเส้นตรงและคณิตศาสตร์เลขจำนวนเต็ม 48 บิต
และแหล่งสุ่มหลอกที่ดีก็คือarc4random()
ฟังก์ชั่นที่มีอยู่ในหลาย ๆ ระบบ ไม่ได้เป็นส่วนหนึ่งของมาตรฐานอย่างเป็นทางการที่ปรากฏใน BSD ประมาณปี 1997 แต่คุณสามารถค้นหาได้บนระบบเช่น Linux และ macOS / iOS
random()
ไม่มีอยู่ใน Windows
rand()
เมื่อจำเป็นตามมาตรฐาน C เท่านั้น สำหรับสิ่งอื่นคุณต้องการโซลูชันพิเศษสำหรับ Windows เท่านั้นเช่นเคย #ifdef _WIN32
เป็นวลีที่คุณจะเห็นบ่อยที่สุดในรหัสข้ามแพลตฟอร์มที่ต้องการสนับสนุน Windows รวมถึงมักจะมีวิธีแก้ไขปัญหาเดียวที่ทำงานได้กับทุกระบบและเป็นสิ่งที่จำเป็นสำหรับ Windows เท่านั้น
สำหรับแอปพลิเคชัน Linux C:
นี่คือรหัสทำใหม่ของฉันจากคำตอบข้างต้นซึ่งเป็นไปตามแนวทางปฏิบัติของรหัส C ของฉันและส่งกลับบัฟเฟอร์แบบสุ่มทุกขนาด (พร้อมรหัสส่งคืนที่เหมาะสม ฯลฯ ) ให้แน่ใจว่าได้โทรurandom_open()
หนึ่งครั้งที่จุดเริ่มต้นของโปรแกรมของคุณ
int gUrandomFd = -1;
int urandom_open(void)
{
if (gUrandomFd == -1) {
gUrandomFd = open("/dev/urandom", O_RDONLY);
}
if (gUrandomFd == -1) {
fprintf(stderr, "Error opening /dev/urandom: errno [%d], strerrer [%s]\n",
errno, strerror(errno));
return -1;
} else {
return 0;
}
}
void urandom_close(void)
{
close(gUrandomFd);
gUrandomFd = -1;
}
//
// This link essentially validates the merits of /dev/urandom:
// http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
//
int getRandomBuffer(uint8_t *buf, int size)
{
int ret = 0; // Return value
if (gUrandomFd == -1) {
fprintf(stderr, "Urandom (/dev/urandom) file not open\n");
return -1;
}
ret = read(gUrandomFd, buf, size);
if (ret != size) {
fprintf(stderr, "Only read [%d] bytes, expected [%d]\n",
ret, size);
return -1;
} else {
return 0;
}
}
[min, max)
วิธีการแก้ปัญหาที่เรียบง่ายของฉันควรจะทำงานสำหรับตัวเลขสุ่มในช่วง ใช้srand(time(NULL))
ก่อนเรียกใช้ฟังก์ชัน
int range_rand(int min_num, int max_num) {
if (min_num >= max_num) {
fprintf(stderr, "min_num is greater or equal than max_num!\n");
}
return min_num + (rand() % (max_num - min_num));
}
ลองสิ่งนี้ฉันรวบรวมมันจากแนวคิดบางอย่างที่อ้างอิงข้างต้น:
/*
Uses the srand() function to seed the random number generator based on time value,
then returns an integer in the range 1 to max. Call this with random(n) where n is an integer, and you get an integer as a return value.
*/
int random(int max) {
srand((unsigned) time(NULL));
return (rand() % max) + 1;
}
srand()
ทุกครั้งที่คุณต้องการโทรหาrand()
เป็นความคิดที่แย่มาก เนื่องจากtime()
โดยทั่วไปแล้วจะส่งคืนค่าในไม่กี่วินาทีที่เรียกใช้ฟังก์ชันนี้อย่างรวดเร็วจะส่งกลับค่า "สุ่ม" ที่เหมือนกัน
random()
หน้าที่ของ Unix