[英]random 4 digit number with non repeating digits in C
我正在尝试创建一个get_random_4digit
函数,该get_random_4digit
生成一个 4 位数字,该数字具有 1-9 的非重复数字,同时仅使用int
s、 if
、 while
和函数,因此没有数组等。
这是我拥有的代码,但它并没有真正按预期工作,有人能指出我正确的方向吗?
int get_random_4digit() {
int d1 = rand() % 9 + 1;
int d2 = rand() % 9 + 1;
while (true) {
if (d1 != d2) {
int d3 = rand() % 9 + 1;
if (d3 != d1 || d3 != d2) {
int d4 = rand() % 9 + 1;
if (d4 != d1 || d4 != d2 || d4 != d3) {
random_4digit = (d1 * 1000) + (d2 * 100) + (d3 * 10) + d4;
break;
}
}
}
}
printf("Random 4digit = %d\n", random_4digit);
}
KISS 方法可能是这样的:
int getRandom4Digits() {
uint16_t acc = 0;
uint16_t used = 0;
for (int i = 0; i < 4; i++) {
int idx;
do {
idx = rand() % 9; // Not equidistributed but never mind...
} while (used & (1 << idx));
acc = acc * 10 + (idx + 1);
used |= (1 << idx);
}
return acc;
}
这乍一看非常愚蠢。 快速分析表明,这确实还不错,对rand()
的多次调用约为 4.9。
内循环步骤的预期数量[以及对rand()
相应调用,如果我们假设rand() % 9
为 iid] 将是:1 + 9/8 + 9/7 + 9/6 ~ 4.9107。
您可以使用线性反馈移位寄存器。 看看这个Computerphile 视频。
您应该继续生成数字,直到找到不同的数字:
int get_random_4digit() {
int random_4digit = 0;
/* We must have 4 digits number - at least 1234 */
while (random_4digit < 1000) {
int digit = rand() % 9 + 1;
/* check if generated digit is not in the result */
for (int number = random_4digit; number > 0; number /= 10)
if (number % 10 == digit) {
digit = 0; /* digit has been found, we'll try once more */
break;
}
if (digit > 0) /* unique digit generated, we add it to result */
random_4digit = random_4digit * 10 + digit;
}
return random_4digit;
}
请自己摆弄
您可以在 9 位数的数组上使用部分 Fisher-Yates shuffle,在 4 位数后停止:
// Return random integer from 0 to n-1 (for n in range 1 to RAND_MAX+1u).
int get_random_int(unsigned int n) {
unsigned int x = (RAND_MAX + 1u) / n;
unsigned int limit = x * n;
int s;
do {
s = rand();
} while (s >= limit);
return s / x;
}
// Return random 4-digit number from 1234 to 9876 with no repeating digits.
int get_random_4digit(void) {
char possible[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int result = 0;
int i;
// Uses partial Fisher-Yates shuffle.
for (i = 0; i < 4; i++) {
int rand_pos = i + get_random_int(9 - i);
char digit = possible[rand_pos];
possible[rand_pos] = possible[i];
possible[i] = digit; // not really needed
result = result * 10 + digit; // update result
}
return result;
}
第一位数字有 9 种可能,第二位数字有 8 种可能,第三位数字有 7 种可能,最后一位数字有 6 种可能。 这适用于“ 9*8*7*6 = 3024
排列”。
首先获取一个从 0 到 3023 的随机数。我们称之为P
。 要做到这一点而不会造成有偏差的分布,请使用类似do { P = rand() & 0xFFF; } while(P >= 3024);
do { P = rand() & 0xFFF; } while(P >= 3024);
.
第一个数字有 9 种可能,所以d1 = P % 9 + 1; P = P / 9;
d1 = P % 9 + 1; P = P / 9;
.
第二个数字有 8 种可能,所以d2 = P % 8 + 1; P = P / 8;
d2 = P % 8 + 1; P = P / 8;
.
第三个数字有 7 种可能,所以d3 = P % 7 + 1; P = P / 7;
d3 = P % 7 + 1; P = P / 7;
.
对于最后一位数字,您只需执行d4 = P + 1;
因为我们知道P
不能太高。
下一个; 将“可能性”转换为数字。 对于d1
你什么都不做。 对于d2
,如果它大于或等于d1
, if(d2 >= d1) d2++;
需要增加它,例如if(d2 >= d1) d2++;
. 对d3
和d4
执行相同操作(与之前的所有数字相比)。
最终的代码将类似于:
int get_random_4digit() {
int P, d1, d2, d3, d4;
do {
P = rand() & 0xFFF;
} while(P >= 3024);
d1 = P % 9 + 1; P = P / 9;
d2 = P % 8 + 1; P = P / 8;
d3 = P % 7 + 1; P = P / 7;
d4 = P;
if(d2 >= d1) d2++;
if(d3 >= d1) d3++;
if(d3 >= d2) d3++;
if(d4 >= d1) d4++;
if(d4 >= d2) d4++;
if(d4 >= d3) d4++;
return d1*1000 + d2*100 + d3*10 + d4;
}
一种方法是创建一个包含所有 9 位数字的数组,随机选择一个并将其从列表中删除。 像这样的东西:
uint_fast8_t digits[]={1,2,3,4,5,6,7,8,9}; //only 1-9 are allowed, 0 is not allowed
uint_fast8_t left=4; //How many digits are left to create
unsigned result=0; //Store the 4-digit number here
while(left--)
{
uint_fast8_t digit=getRand(9-4+left); //pick a random index
result=result*9+digits[digit];
//Move all digits above the selcted one 1 index down.
//This removes the picked digit from the array.
while(digit<8)
{
digits[digit]=digits[digit+1];
digit++;
}
}
你说你需要一个没有数组的解决方案。 幸运的是,我们可以在单个uint64_t
存储多达 16 个 4 位数字。 这是一个使用uint64_t
存储数字列表的示例,因此不需要数组。
#include <stdint.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
unsigned getRand(unsigned max)
{
return rand()%(max+1);
}
//Creates a uint64_t that is used as an array.
//Use no more than 16 values and don't use values >0x0F
//The last argument will have index 0
uint64_t fakeArrayCreate(uint_fast8_t count, ...)
{
uint64_t result=0;
va_list args;
va_start (args, count);
while(count--)
{
result=(result<<4) | va_arg(args,int);
}
return result;
}
uint_fast8_t fakeArrayGet(uint64_t array, uint_fast8_t index)
{
return array>>(4*index)&0x0F;
}
uint64_t fakeArraySet(uint64_t array, uint_fast8_t index, uint_fast8_t value)
{
array = array & ~((uint64_t)0x0F<<(4*index));
array = array | ((uint64_t)value<<(4*index));
return array;
}
unsigned getRandomDigits(void)
{
uint64_t digits = fakeArrayCreate(9,9,8,7,6,5,4,3,2,1);
uint_fast8_t left=4;
unsigned result=0;
while(left--)
{
uint_fast8_t digit=getRand(9-4+left);
result=result*9+fakeArrayGet(digits,digit);
//Move all digits above the selcted one 1 index down.
//This removes the picked digit from the array.
while(digit<8)
{
digits=fakeArraySet(digits,digit,fakeArrayGet(digits,digit+1));
digit++;
}
}
return result;
}
//Test our function
int main(int argc, char **argv)
{
srand(atoi(argv[1]));
printf("%u\n",getRandomDigits());
}
一种变体可能是使用一个值0x123456789
,并对半字节(构成十六进制值中一位数字的 4 位)进行 Fisher-Yates shuffle。 洗牌完成后,返回最低的 4 个半字节。
例子:
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
uint16_t get_random_4digit() {
uint64_t bits = 0x123456789; // nibbles
uint64_t mask = 0xFFFFFFFFF;
// shuffle the nibbles
for(unsigned idx = 9 - 1; idx > 0; --idx) {
unsigned ish = idx * 4; // index shift
// shift for random nibble to swap with `idx`
unsigned swp = (rand() % (idx + 1)) * 4;
// extract the selected nibbles
uint64_t a = (bits & (0xFULL << ish)) >> ish;
uint64_t b = (bits & (0xFULL << swp)) >> swp;
// swap them
bits &= (mask ^ (0xFULL << ish)) &
(mask ^ (0xFULL << swp));
bits |= (a << swp) | (b << ish);
}
return bits & 0xFFFF; // return the 4 lowest nibbles
}
位操作可能可以优化 - 但我按照我的想法写了它,所以保持原样可能更好可读性
然后,您可以将该值打印为十六进制值以获得所需的输出 - 或者提取 4 个半字节并将其转换为十进制输出。
int main() {
srand(time(NULL));
uint16_t res = get_random_4digit();
// print directly as hex:
printf("%X\n", res);
// ... or extract the nibbles and multiply to get decimal result - same output:
uint16_t a = (res >> 12) & 0xF;
uint16_t b = (res >> 8) & 0xF;
uint16_t c = (res >> 4) & 0xF;
uint16_t d = (res >> 0) & 0xF;
uint16_t dec = a * 1000 + b * 100 + c * 10 + d;
printf("%d\n", dec);
}
这个问题已经有多个答案,但似乎没有一个只使用int
s, if
, while
和 functions满足要求。 这是Pelle Evensen简单解决方案的修改版本:
#include <stdlib.h>
int get_random_4digit(void) {
int acc = 0, used = 0, i = 0;
while (i < 4) {
int idx = rand() % 9; // Not strictly uniform but never mind...
if (!(used & (1 << idx))) {
acc = acc * 10 + idx + 1;
used |= 1 << idx;
i++;
}
}
return acc;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.